mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
revert: OAuth 回调回退为 secscore:// URI 方案
SECTL token 接口对 loopback/private IP 的 redirect_uri 返回 403
("Local or private network IP addresses are not allowed"),
HTTP/localhost/HTTPS loopback 均不可行,回退到 URI 方案。
- callbackUrl 改为 secscore://oauth/callback
- OAuthLogin 改回 deep link (ss:oauth-deep-link) 流程,移除
本地 HTTP 回调服务器的启动/停止逻辑
- oauth_server.rs 保留(未被登录流程调用)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -55,7 +55,7 @@ pub async fn oauth_start_callback_server(
|
|||||||
if let Some(h) = handle_guard.as_ref() {
|
if let Some(h) = handle_guard.as_ref() {
|
||||||
if !h.is_finished() {
|
if !h.is_finished() {
|
||||||
let port = OAUTH_CALLBACK_PORT;
|
let port = OAUTH_CALLBACK_PORT;
|
||||||
let url = format!("http://127.0.0.1:{}/oauth/callback", port);
|
let url = format!("http://localhost:{}/oauth/callback", port);
|
||||||
log("server already running, return early");
|
log("server already running, return early");
|
||||||
return Ok(IpcResponse::success(OAuthServerStartResult { url, port }));
|
return Ok(IpcResponse::success(OAuthServerStartResult { url, port }));
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ pub async fn oauth_start_callback_server(
|
|||||||
|
|
||||||
let port = OAUTH_CALLBACK_PORT;
|
let port = OAUTH_CALLBACK_PORT;
|
||||||
let addr: SocketAddr = ([127, 0, 0, 1], port).into();
|
let addr: SocketAddr = ([127, 0, 0, 1], port).into();
|
||||||
let url = format!("http://127.0.0.1:{}/oauth/callback", port);
|
let url = format!("http://localhost:{}/oauth/callback", port);
|
||||||
let url_for_spawn = url.clone();
|
let url_for_spawn = url.clone();
|
||||||
|
|
||||||
// 尝试绑定;若端口被占用,强杀占用进程后重试一次
|
// 尝试绑定;若端口被占用,强杀占用进程后重试一次
|
||||||
|
|||||||
@@ -28,54 +28,28 @@ export function OAuthLogin({ visible, onClose, onSuccess }: OAuthLoginProps) {
|
|||||||
|
|
||||||
const platformId = import.meta.env.VITE_OAUTH_PLATFORM_ID
|
const platformId = import.meta.env.VITE_OAUTH_PLATFORM_ID
|
||||||
if (platformId) {
|
if (platformId) {
|
||||||
// 使用本地 HTTP 回调地址 (http://127.0.0.1:51267/oauth/callback)
|
// 使用 deep link 回调地址
|
||||||
sectlAuth.initialize(platformId, "http://127.0.0.1:51267/oauth/callback")
|
sectlAuth.initialize(platformId, "secscore://oauth/callback")
|
||||||
SECTL_CONFIG.platformId = platformId
|
SECTL_CONFIG.platformId = platformId
|
||||||
SECTL_CONFIG.callbackUrl = "http://127.0.0.1:51267/oauth/callback"
|
SECTL_CONFIG.callbackUrl = "secscore://oauth/callback"
|
||||||
}
|
}
|
||||||
}, [visible])
|
}, [visible])
|
||||||
|
|
||||||
// 监听本地 HTTP 回调服务器转发回来的 OAuth 回调
|
// 监听 Deep Link 回调
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visible || !loading) return
|
if (!visible || !loading) return
|
||||||
completedRef.current = false
|
completedRef.current = false
|
||||||
|
|
||||||
const api = (window as any).api
|
const handleDeepLink = async (event: Event) => {
|
||||||
if (!api || typeof api.onOAuthCallback !== "function") return
|
if (completedRef.current) return
|
||||||
|
const customEvent = event as CustomEvent<{ code: string; state: string }>
|
||||||
|
const { code, state } = customEvent.detail
|
||||||
|
|
||||||
let disposed = false
|
console.log("[OAuthLogin] Received deep link callback:", { code, state })
|
||||||
let unlisten: (() => void) | null = null
|
|
||||||
|
|
||||||
const handleCallback = async (payload: {
|
|
||||||
code?: string | null
|
|
||||||
state?: string | null
|
|
||||||
error?: string | null
|
|
||||||
error_description?: string | null
|
|
||||||
}) => {
|
|
||||||
if (disposed || completedRef.current) return
|
|
||||||
console.log("[OAuthLogin] Received http callback:", payload)
|
|
||||||
|
|
||||||
if (payload.error) {
|
|
||||||
completedRef.current = true
|
|
||||||
message.error("登录失败: " + (payload.error_description || payload.error))
|
|
||||||
await stopCallbackServer()
|
|
||||||
setLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payload.code) {
|
|
||||||
completedRef.current = true
|
|
||||||
message.error("登录失败: 未收到授权码")
|
|
||||||
await stopCallbackServer()
|
|
||||||
setLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await sectlAuth.exchangeCode(
|
// 使用 code 交换 token
|
||||||
payload.code,
|
const result = await sectlAuth.exchangeCode(code, state)
|
||||||
payload.state || ""
|
|
||||||
)
|
|
||||||
if (result) {
|
if (result) {
|
||||||
completedRef.current = true
|
completedRef.current = true
|
||||||
const userInfo = await sectlAuth.getUserInfo()
|
const userInfo = await sectlAuth.getUserInfo()
|
||||||
@@ -86,40 +60,16 @@ export function OAuthLogin({ visible, onClose, onSuccess }: OAuthLoginProps) {
|
|||||||
completedRef.current = true
|
completedRef.current = true
|
||||||
message.error(error.message || "登录失败")
|
message.error(error.message || "登录失败")
|
||||||
} finally {
|
} finally {
|
||||||
await stopCallbackServer()
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
api
|
window.addEventListener("ss:oauth-deep-link", handleDeepLink)
|
||||||
.onOAuthCallback(handleCallback)
|
|
||||||
.then((fn: () => void) => {
|
|
||||||
if (disposed) {
|
|
||||||
fn()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
unlisten = fn
|
|
||||||
})
|
|
||||||
.catch((err: any) => {
|
|
||||||
console.error("[OAuthLogin] Failed to listen oauth-callback:", err)
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
disposed = true
|
window.removeEventListener("ss:oauth-deep-link", handleDeepLink)
|
||||||
if (unlisten) unlisten()
|
|
||||||
}
|
}
|
||||||
}, [visible, loading, onClose, onSuccess])
|
}, [visible, loading, onClose, onSuccess])
|
||||||
|
|
||||||
const stopCallbackServer = async () => {
|
|
||||||
const api = (window as any).api
|
|
||||||
if (!api || typeof api.oauthStopCallbackServer !== "function") return
|
|
||||||
try {
|
|
||||||
await api.oauthStopCallbackServer()
|
|
||||||
} catch (err) {
|
|
||||||
console.warn("[OAuthLogin] stop callback server failed:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOAuthLogin = async () => {
|
const handleOAuthLogin = async () => {
|
||||||
if (!SECTL_CONFIG.platformId) {
|
if (!SECTL_CONFIG.platformId) {
|
||||||
message.error("OAuth 配置未设置")
|
message.error("OAuth 配置未设置")
|
||||||
@@ -129,40 +79,20 @@ export function OAuthLogin({ visible, onClose, onSuccess }: OAuthLoginProps) {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
const t0 = performance.now()
|
const t0 = performance.now()
|
||||||
const log = (step: string) =>
|
const log = (step: string) =>
|
||||||
console.log(
|
console.log(`[OAuthLogin] ${step} +${Math.round(performance.now() - t0)}ms`)
|
||||||
`[OAuthLogin] ${step} +${Math.round(performance.now() - t0)}ms (wall=${new Date().toISOString()})`
|
|
||||||
)
|
|
||||||
log("handleOAuthLogin start")
|
log("handleOAuthLogin start")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 启动本地 HTTP 回调服务器 (端口 51267,被占用则强杀已有进程)
|
|
||||||
const api = (window as any).api
|
|
||||||
if (api && typeof api.oauthStartCallbackServer === "function") {
|
|
||||||
log("before oauthStartCallbackServer")
|
|
||||||
const res = await api.oauthStartCallbackServer()
|
|
||||||
log(`after oauthStartCallbackServer success=${res?.success}`)
|
|
||||||
if (!res?.success) {
|
|
||||||
throw new Error(res?.message || "启动回调服务器失败")
|
|
||||||
}
|
|
||||||
// 以服务器实际返回的 URL 作为 redirect_uri
|
|
||||||
if (res.data?.url) {
|
|
||||||
SECTL_CONFIG.callbackUrl = res.data.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log("before getAuthorizationUrl")
|
|
||||||
const authUrl = await sectlAuth.getAuthorizationUrl()
|
const authUrl = await sectlAuth.getAuthorizationUrl()
|
||||||
log(`after getAuthorizationUrl url=${authUrl.slice(0, 60)}...`)
|
log("after getAuthorizationUrl")
|
||||||
// 使用 Tauri shell 打开系统浏览器
|
// 使用 Tauri shell 打开系统浏览器
|
||||||
log("before open(authUrl)")
|
|
||||||
await open(authUrl)
|
await open(authUrl)
|
||||||
log("after open(authUrl)")
|
log("after open(authUrl)")
|
||||||
|
|
||||||
// 设置超时
|
// 设置超时
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (loading && !completedRef.current) {
|
if (loading && !completedRef.current) {
|
||||||
log("login timeout, stopping callback server")
|
log("login timeout")
|
||||||
stopCallbackServer()
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
message.warning("登录超时,请重试")
|
message.warning("登录超时,请重试")
|
||||||
}
|
}
|
||||||
@@ -170,14 +100,12 @@ export function OAuthLogin({ visible, onClose, onSuccess }: OAuthLoginProps) {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
log(`handleOAuthLogin error: ${error?.message}`)
|
log(`handleOAuthLogin error: ${error?.message}`)
|
||||||
message.error(error.message || "登录失败")
|
message.error(error.message || "登录失败")
|
||||||
await stopCallbackServer()
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleModalClose = () => {
|
const handleModalClose = () => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
void stopCallbackServer()
|
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const SECTL_CONFIG = {
|
|||||||
baseUrl: "https://appwrite.sectl.cn",
|
baseUrl: "https://appwrite.sectl.cn",
|
||||||
authUrl: "https://sectl.cn",
|
authUrl: "https://sectl.cn",
|
||||||
platformId: "", // 需要在设置中配置
|
platformId: "", // 需要在设置中配置
|
||||||
callbackUrl: "http://127.0.0.1:51267/oauth/callback",
|
callbackUrl: "secscore://oauth/callback",
|
||||||
callbackPort: 51267,
|
callbackPort: 51267,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user