Skip to content

Commit b0a879a

Browse files
author
ci-bot
committed
token validation issues
1 parent 32adbd9 commit b0a879a

File tree

3 files changed

+50
-33
lines changed

3 files changed

+50
-33
lines changed

apps/remix-ide/src/app/plugins/auth-plugin.tsx

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,8 @@ export class AuthPlugin extends Plugin {
512512
try {
513513
const refreshToken = localStorage.getItem('remix_refresh_token')
514514
if (!refreshToken) {
515-
console.warn('[AuthPlugin] No refresh token available')
515+
console.warn('[AuthPlugin] No refresh token available, logging out')
516+
await this.logout()
516517
return null
517518
}
518519

@@ -544,10 +545,8 @@ export class AuthPlugin extends Plugin {
544545

545546
console.warn('[AuthPlugin] Token refresh failed:', response.error)
546547

547-
// If refresh failed, clear tokens and emit logout
548-
if (response.status === 401) {
549-
await this.logout()
550-
}
548+
// Any failed refresh means tokens are no longer usable — log out
549+
await this.logout()
551550

552551
return null
553552
} catch (error) {
@@ -632,11 +631,14 @@ export class AuthPlugin extends Plugin {
632631
}
633632
}
634633

635-
onActivation(): void {
634+
async onActivation(): Promise<void> {
636635
console.log('[AuthPlugin] Activated - using popup + localStorage mode')
637636

638637
// Validate existing token with the API on load
639-
this.validateAndRestoreSession()
638+
// Awaited so that plugin activation only completes after validation.
639+
// This ensures AuthContext (which polls for activation) never sees
640+
// stale/unvalidated tokens in localStorage.
641+
await this.validateAndRestoreSession()
640642
}
641643

642644
/**
@@ -661,9 +663,25 @@ export class AuthPlugin extends Plugin {
661663
if (!refreshed) {
662664
console.log('[AuthPlugin] Refresh failed, clearing session')
663665
this.clearStoredAuth()
666+
this.emit('authStateChanged', {
667+
isAuthenticated: false,
668+
user: null,
669+
token: null
670+
})
664671
return
665672
}
666-
// refreshAccessToken already emits authStateChanged if successful
673+
// Refresh succeeded — emit authenticated state with refreshed data
674+
const refreshedToken = localStorage.getItem('remix_access_token')
675+
const userStr = localStorage.getItem('remix_user')
676+
const user = userStr ? JSON.parse(userStr) : null
677+
if (user && refreshedToken) {
678+
this.emit('authStateChanged', {
679+
isAuthenticated: true,
680+
user,
681+
token: refreshedToken
682+
})
683+
this.refreshCredits().catch(console.error)
684+
}
667685
return
668686
}
669687

@@ -715,22 +733,15 @@ export class AuthPlugin extends Plugin {
715733
}
716734
} catch (error) {
717735
console.error('[AuthPlugin] Session validation error:', error)
718-
// Network error - don't clear session, user might be offline
719-
// Try to use cached session but mark as unverified
720-
const userStr = localStorage.getItem('remix_user')
721-
if (userStr) {
722-
try {
723-
const user = JSON.parse(userStr)
724-
this.emit('authStateChanged', {
725-
isAuthenticated: true,
726-
user,
727-
token,
728-
verified: false // Indicate session is not verified
729-
})
730-
} catch (e) {
731-
// Invalid stored data
732-
}
733-
}
736+
// Network error — cannot verify token, clear session to be safe.
737+
// An unverifiable token should not grant access.
738+
console.log('[AuthPlugin] Cannot reach auth server, clearing session')
739+
this.clearStoredAuth()
740+
this.emit('authStateChanged', {
741+
isAuthenticated: false,
742+
user: null,
743+
token: null
744+
})
734745
}
735746
}
736747

libs/remix-api/src/lib/plugins/api-services.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ export class SSOApiService {
7272
}
7373

7474
/**
75-
* Refresh access token using refresh token
75+
* Refresh access token using refresh token.
76+
* Uses skipTokenRefresh to prevent recursive auto-refresh on 401.
7677
*/
7778
async refreshToken(refreshToken: string): Promise<ApiResponse<RefreshTokenResponse>> {
78-
return this.apiClient.post<RefreshTokenResponse>('/refresh', { refresh_token: refreshToken })
79+
return this.apiClient.post<RefreshTokenResponse>('/refresh', { refresh_token: refreshToken }, { skipTokenRefresh: true })
7980
}
8081

8182
/**

libs/remix-ui/app/src/lib/remix-app/context/auth-context.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,20 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children, plugin })
116116
useEffect(() => {
117117
if (!isReady || !plugin) return
118118

119+
// Session restoration is handled by the AuthPlugin's validateAndRestoreSession()
120+
// which runs during plugin activation (before isReady becomes true).
121+
// By this point, localStorage has already been validated/cleaned by the server.
122+
// We just read the validated state — no separate API call needed here.
119123
const initAuth = async () => {
120124
try {
121-
const isAuth = await plugin.isAuthenticated()
122-
if (isAuth) {
123-
const user = await plugin.getUser()
124-
const token = await plugin.getToken()
125-
if (user && token) {
126-
dispatch({ type: 'AUTH_SUCCESS', payload: { user, token } })
127-
}
125+
const token = await plugin.getToken()
126+
if (!token) {
127+
// No token after validation means user is not authenticated
128+
return
129+
}
130+
const user = await plugin.getUser()
131+
if (user) {
132+
dispatch({ type: 'AUTH_SUCCESS', payload: { user, token } })
128133

129134
// Fetch credits
130135
const credits = await plugin.getCredits()

0 commit comments

Comments
 (0)