fix: add localStorage fallback for OAuth2 session state on mobile browsers (#900)

* fix: add localStorage fallback for OAuth2 session state on mobile browsers

Some mobile browsers (Safari ITP, WebViews) lose sessionStorage during
cross-origin OAuth2 redirects. Add localStorage fallback via computed
wrapper that dual-writes on set and reads sessionStorage-first on get.
Also cleanup state in finally block to ensure one-time consumption.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: i18n for 'code not found' in OAuth2 callback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dream Hunter
2026-03-16 00:04:00 +08:00
committed by GitHub
parent eeea512ab1
commit 6c58cd3c2e
2 changed files with 29 additions and 14 deletions

View File

@@ -112,8 +112,18 @@ export const useGlobalState = createGlobalState(
);
const telegramApp = ref(window.Telegram?.WebApp || {});
const isTelegram = ref(!!window.Telegram?.WebApp?.initData);
const userOauth2SessionState = useSessionStorage('userOauth2SessionState', '');
const userOauth2SessionClientID = useSessionStorage('userOauth2SessionClientID', '');
const _oauth2StateSession = useSessionStorage('userOauth2SessionState', '');
const _oauth2StateFallback = useStorage('userOauth2SessionState_fb', '');
const userOauth2SessionState = computed({
get: () => _oauth2StateSession.value || _oauth2StateFallback.value,
set: (v) => { _oauth2StateSession.value = v; _oauth2StateFallback.value = v; }
});
const _oauth2ClientIDSession = useSessionStorage('userOauth2SessionClientID', '');
const _oauth2ClientIDFallback = useStorage('userOauth2SessionClientID_fb', '');
const userOauth2SessionClientID = computed({
get: () => _oauth2ClientIDSession.value || _oauth2ClientIDFallback.value,
set: (v) => { _oauth2ClientIDSession.value = v; _oauth2ClientIDFallback.value = v; }
});
const browserFingerprint = ref('');
return {
isDark,

View File

@@ -19,28 +19,30 @@ const { t } = useI18n({
en: {
logging: 'Logging in...',
stateNotMatch: 'state not match',
codeNotFound: 'code not found',
},
zh: {
logging: '登录中...',
stateNotMatch: 'state 不匹配',
codeNotFound: '未找到授权码',
}
}
});
onMounted(async () => {
const state = route.query.state;
if (state != userOauth2SessionState.value) {
console.error('state not match');
message.error(t('stateNotMatch'));
return;
}
const code = route.query.code;
if (!code) {
console.error('code not found');
message.error('code not found');
return;
}
try {
const state = route.query.state;
if (state != userOauth2SessionState.value) {
console.error('state not match');
message.error(t('stateNotMatch'));
return;
}
const code = route.query.code;
if (!code) {
console.error('code not found');
message.error(t('codeNotFound'));
return;
}
const res = await api.fetch(`/user_api/oauth2/callback`, {
method: 'POST',
body: JSON.stringify({
@@ -53,6 +55,9 @@ onMounted(async () => {
} catch (error) {
console.error(error);
message.error(error.message || 'error');
} finally {
userOauth2SessionState.value = '';
userOauth2SessionClientID.value = '';
}
});
</script>