Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/application/services/js-services/cached-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as random from 'lib0/random';
import * as Y from 'yjs';

import { openCollabDB } from '@/application/db';
import { Log } from '@/utils/log';
import {
createRow,
deleteRow,
Expand Down Expand Up @@ -283,12 +284,15 @@ export async function getPublishInfoCached(viewId: string) {
}

export async function loginAuth(url: string) {
Log.info('[Auth] loginAuth: processing OAuth callback');
try {
await signInWithUrl(url);
Log.info('[Auth] loginAuth: success, calling afterAuth');
emit(EventType.SESSION_VALID);
afterAuth();
return;
} catch (e) {
Log.error('[Auth] loginAuth: failed', e);
emit(EventType.SESSION_INVALID);
return Promise.reject(e);
}
Expand Down
21 changes: 14 additions & 7 deletions src/application/services/js-services/http/auth-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ export interface ServerInfo {
}

export async function signInWithUrl(url: string) {
Log.info('[Auth] signInWithUrl: processing OAuth callback');

// First check for GoTrue errors in the URL
const gotrueError = parseGoTrueErrorFromUrl(url);

if (gotrueError) {
console.warn('[signInWithUrl] GoTrue error detected in callback URL', {
Log.error('[Auth] signInWithUrl: GoTrue error in callback URL', {
code: gotrueError.code,
message: gotrueError.message,
});
// GoTrue returned an error, reject with parsed error
return Promise.reject({
code: gotrueError.code,
message: gotrueError.message,
Expand All @@ -30,7 +31,7 @@ export async function signInWithUrl(url: string) {
const hash = urlObj.hash;

if (!hash) {
console.warn('[signInWithUrl] No hash found in callback URL');
Log.error('[Auth] signInWithUrl: no hash fragment in callback URL');
return Promise.reject('No hash found');
}

Expand All @@ -39,7 +40,7 @@ export async function signInWithUrl(url: string) {
const refresh_token = params.get('refresh_token');

if (!accessToken || !refresh_token) {
console.warn('[signInWithUrl] Missing tokens in callback hash', {
Log.error('[Auth] signInWithUrl: missing tokens in callback hash', {
hasAccessToken: !!accessToken,
hasRefreshToken: !!refresh_token,
});
Expand All @@ -49,6 +50,8 @@ export async function signInWithUrl(url: string) {
});
}

Log.info('[Auth] signInWithUrl: tokens extracted from callback URL');

// CRITICAL: Clear old token BEFORE processing new OAuth tokens
// This prevents axios interceptor from trying to auto-refresh the old expired token
// during verifyToken() API call, which would cause a race condition where:
Expand All @@ -60,29 +63,33 @@ export async function signInWithUrl(url: string) {
const hadOldToken = !!localStorage.getItem('token');

if (hadOldToken) {
Log.debug('[signInWithUrl] Clearing old token before processing OAuth callback to prevent race condition');
Log.info('[Auth] signInWithUrl: clearing old token to prevent race condition');
localStorage.removeItem('token');
}

Log.info('[Auth] signInWithUrl: verifying token with AppFlowy Cloud');
try {
await verifyToken(accessToken);
} catch (e) {
console.warn('[signInWithUrl] Verify token failed', { message: (e as Error)?.message });
Log.error('[Auth] signInWithUrl: verifyToken failed', { message: (e as Error)?.message });
return Promise.reject({
code: -1,
message: 'Verify token failed',
});
}

Log.info('[Auth] signInWithUrl: refreshing token');
try {
await refreshToken(refresh_token);
} catch (e) {
console.warn('[signInWithUrl] Refresh token failed', { message: (e as Error)?.message });
Log.error('[Auth] signInWithUrl: refreshToken failed', { message: (e as Error)?.message });
return Promise.reject({
code: -1,
message: 'Refresh token failed',
});
}

Log.info('[Auth] signInWithUrl: OAuth callback processed successfully');
}

export async function verifyToken(accessToken: string) {
Expand Down
Loading
Loading