Skip to content
Merged
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
35 changes: 32 additions & 3 deletions src/services/auth/SupabaseAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ export class SupabaseAuth implements ServiceAuth {
has_session: !!session,
has_refresh_token: !!session?.refresh_token,
});
if (event === 'TOKEN_REFRESHED') {
if (session?.refresh_token)
Util.addRefreshTokenToStore(session?.refresh_token);
if (session?.refresh_token) {
Util.addRefreshTokenToStore(session.refresh_token);
}
Comment on lines +74 to 76

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid persisting initial sessions before Redux rehydrates

When this listener now runs for every auth event, the INITIAL_SESSION emitted as soon as SupabaseAuth.getInstance() is created can call setRefreshToken. That instance is constructed at module load in src/startup/serviceBootstrap.ts before bootstrapServicesAndRender waits for persistor.bootstrapped, so if the initial-session callback wins the startup race it modifies the auth subtree before redux-persist handles REHYDRATE; the default reconciler then skips the persisted auth state, dropping the stored app user/roles and sending returning users through a logged-out or wrong-mode path. Keep this restricted to real refresh/sign-in completions or defer the dispatch until rehydration is done.

Useful? React with 👍 / 👎.

},
);
Expand Down Expand Up @@ -476,14 +475,44 @@ export class SupabaseAuth implements ServiceAuth {
Util.addRefreshTokenToStore(
currentSession.data.session.refresh_token,
);
return;
}
if (currentSession?.error) {
logger.error(
'Unable to resolve web Supabase session:',
currentSession.error,
);
}

const stored = Util.getRefreshTokenFromStore();
if (!stored?.token) return;

const refreshToken = String(stored.token).replace(/"/g, '');
const response = await this._auth?.refreshSession({
refresh_token: refreshToken,
});
if (response?.error) {
throw new Error(
'Web session refresh failed: ' + response.error.message,
);
}
if (response?.data?.session) {
const { access_token, refresh_token } = response.data.session;
await this._auth?.setSession({ access_token, refresh_token });
Util.addRefreshTokenToStore(refresh_token);
}
Comment on lines +499 to +503

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Passing refresh_token directly to Util.addRefreshTokenToStore without a truthiness guard can lead to runtime errors or store corruption if the token is null or undefined. Although the Supabase Session type typically includes a refresh_token, certain authentication flows or partial sessions may return a null or undefined value. Adding a guard check before storing the token prevents persisting invalid state, aligning with the pattern used in other parts of this file.

        if (response?.data?.session) {
          const { access_token, refresh_token } = response.data.session;
          await this._auth?.setSession({ access_token, refresh_token });
          if (refresh_token) {
            Util.addRefreshTokenToStore(refresh_token);
          }
        }

} catch (error) {
if (this.isInvalidRefreshTokenError(error)) {
store.dispatch(setRefreshToken(null));
logAuthDebug(
'Cleared stale web refresh token after refresh failure.',
{
source: 'SupabaseAuth.doRefreshSession',
reason: 'web_invalid_refresh_token',
},
);
return;
}
logger.error('Unexpected error while resolving web session:', error);
}
return;
Expand Down
Loading