Skip to content

Commit 40f62c4

Browse files
authored
Merge pull request #836 from xiaomakuaiz/fix/mobile-baizhi-native-auth
fix: complete baizhi mobile auth with native session
2 parents 5152abb + be5f3d1 commit 40f62c4

1 file changed

Lines changed: 57 additions & 9 deletions

File tree

mobile/app/login.tsx

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const BROWSER_UA = Platform.select({
2525

2626
type LoginView = 'password' | 'phone' | 'oauthWeb';
2727
type WebOAuthMode = 'github' | 'baizhiBridge';
28+
type BaizhiBridgeState = { targetBaseUrl: string; phoneToSave?: string; authorizeViaFetch?: boolean };
2829

2930
function hostOf(url: string): string {
3031
return url.match(/^https?:\/\/([^/?#]+)/i)?.[1]?.toLowerCase() || '';
@@ -83,6 +84,21 @@ function isGithubCallback(url: string): boolean {
8384
return url.startsWith(GITHUB_CALLBACK_URL) || url.startsWith('monkeycode:///oauth/github');
8485
}
8586

87+
async function readResponseError(res: Response, fallback: string): Promise<string> {
88+
try {
89+
const json = (await res.clone().json()) as { message?: string };
90+
if (json?.message) return json.message.replace(/\s*\[trace_id:[^\]]+\]\s*$/i, '').trim();
91+
} catch {
92+
try {
93+
const text = await res.text();
94+
if (text.trim()) return text.trim();
95+
} catch {
96+
/* keep fallback */
97+
}
98+
}
99+
return fallback;
100+
}
101+
86102
export default function LoginScreen() {
87103
const t = useTheme();
88104
const {
@@ -127,7 +143,7 @@ export default function LoginScreen() {
127143
const baizhiBridgeLeftRef = useRef(false);
128144
const baizhiBridgeDoneRef = useRef(false);
129145
const baizhiAutoAuthorizeUrlRef = useRef('');
130-
const baizhiBridgeRef = useRef<{ targetBaseUrl: string; phoneToSave?: string } | null>(null);
146+
const baizhiBridgeRef = useRef<BaizhiBridgeState | null>(null);
131147

132148
// 手机号 / 抖音 / GitHub 登录入口只在官方云展示;私有化 / 自定义地址保持账号密码入口。
133149
const cloud = norm(serverUrl || baseUrl) === DEFAULT_BASE_URL;
@@ -209,9 +225,9 @@ export default function LoginScreen() {
209225
return target;
210226
}, [baseUrl, basicAuth, basicAuthInput, serverUrl, updateBaseUrl, updateBasicAuth]);
211227

212-
const startBaizhiBridge = useCallback((title: string, targetBaseUrl: string, phoneToSave?: string) => {
228+
const startBaizhiBridge = useCallback((title: string, targetBaseUrl: string, phoneToSave?: string, authorizeViaFetch = false) => {
213229
const cleanTarget = norm(targetBaseUrl || baseUrl || DEFAULT_BASE_URL);
214-
baizhiBridgeRef.current = { targetBaseUrl: cleanTarget, phoneToSave };
230+
baizhiBridgeRef.current = { targetBaseUrl: cleanTarget, phoneToSave, authorizeViaFetch };
215231
baizhiBridgeLeftRef.current = false;
216232
baizhiBridgeDoneRef.current = false;
217233
baizhiAutoAuthorizeUrlRef.current = '';
@@ -327,7 +343,7 @@ export default function LoginScreen() {
327343
const result = await authorizeDouyin();
328344
setPhase('正在完成抖音登录…');
329345
await startDouyinAppBaizhiLogin(result.code);
330-
startBaizhiBridge('抖音登录', targetBaseUrl);
346+
startBaizhiBridge('抖音登录', targetBaseUrl, undefined, true);
331347
} catch (e) {
332348
if ((e as { code?: string })?.code === 'E_DOUYIN_CANCELLED') return;
333349
setError(formatError(e, '抖音登录失败,请重试'));
@@ -406,7 +422,7 @@ export default function LoginScreen() {
406422
try {
407423
const targetBaseUrl = await applyServerSettings();
408424
await startBaizhiPhoneLogin(cleanPhone, cleanCode);
409-
startBaizhiBridge('手机号登录', targetBaseUrl, cleanPhone);
425+
startBaizhiBridge('手机号登录', targetBaseUrl, cleanPhone, true);
410426
} catch (e) {
411427
setError(formatError(e, '登录失败,请重试'));
412428
} finally {
@@ -427,17 +443,49 @@ export default function LoginScreen() {
427443
setView(nextView);
428444
};
429445

446+
const authorizeBaizhiWithNativeSession = useCallback(async (apiUrl: string) => {
447+
const bridge = baizhiBridgeRef.current;
448+
if (!bridge || baizhiBridgeDoneRef.current) return;
449+
setError('');
450+
setBusy(true);
451+
setPhase('正在确认授权…');
452+
try {
453+
const res = await fetch(apiUrl, { credentials: 'include', redirect: 'follow' });
454+
if (!res.ok) {
455+
throw new ApiError(await readResponseError(res, `百智云授权失败(${res.status})`), undefined, res.status);
456+
}
457+
await finishBaizhiBridgeLogin();
458+
} catch (e) {
459+
baizhiBridgeDoneRef.current = false;
460+
baizhiBridgeLeftRef.current = false;
461+
baizhiAutoAuthorizeUrlRef.current = '';
462+
baizhiBridgeRef.current = null;
463+
setWebOAuthUrl('');
464+
setWebOAuthTitle('');
465+
setWebOAuthMode('github');
466+
setView(bridge.phoneToSave ? 'phone' : 'password');
467+
setError(formatError(e, '登录失败,请重试'));
468+
} finally {
469+
setBusy(false);
470+
setPhase('');
471+
}
472+
}, [finishBaizhiBridgeLogin]);
473+
430474
const openBaizhiAuthorizeApi = useCallback((url: string) => {
431475
if (webOAuthMode !== 'baizhiBridge') return false;
432476
const apiUrl = toBaizhiAuthorizeApiUrl(url);
433477
if (!apiUrl) return false;
434478
if (baizhiAutoAuthorizeUrlRef.current === apiUrl) return true;
435479
baizhiAutoAuthorizeUrlRef.current = apiUrl;
436-
setPhase('正在确认授权…');
437-
setWebOAuthUrl(apiUrl);
438-
setWebOAuthKey((k) => k + 1);
480+
if (baizhiBridgeRef.current?.authorizeViaFetch) {
481+
void authorizeBaizhiWithNativeSession(apiUrl);
482+
} else {
483+
setPhase('正在确认授权…');
484+
setWebOAuthUrl(apiUrl);
485+
setWebOAuthKey((k) => k + 1);
486+
}
439487
return true;
440-
}, [webOAuthMode]);
488+
}, [authorizeBaizhiWithNativeSession, webOAuthMode]);
441489

442490
const onWebOAuthNav = useCallback(
443491
(navState: WebViewNavigation) => {

0 commit comments

Comments
 (0)