diff --git a/projects/auth0-angular/src/lib/auth.service.spec.ts b/projects/auth0-angular/src/lib/auth.service.spec.ts index 2732b86a..bc8a34b6 100644 --- a/projects/auth0-angular/src/lib/auth.service.spec.ts +++ b/projects/auth0-angular/src/lib/auth.service.spec.ts @@ -1039,6 +1039,9 @@ describe('AuthService', () => { }); it('should call the underlying SDK', async () => { + ( + auth0Client.isAuthenticated as unknown as MockInstance + ).mockResolvedValue(true); const localService = createService(); await firstValueFrom(localService.handleRedirectCallback()); expect(auth0Client.handleRedirectCallback).toHaveBeenCalled(); @@ -1046,6 +1049,9 @@ describe('AuthService', () => { it('should call the underlying SDK and pass options', async () => { const url = 'http://localhost'; + ( + auth0Client.isAuthenticated as unknown as MockInstance + ).mockResolvedValue(true); const localService = createService(); await firstValueFrom(localService.handleRedirectCallback(url)); expect(auth0Client.handleRedirectCallback).toHaveBeenCalledWith(url); @@ -1076,6 +1082,9 @@ describe('AuthService', () => { it('should record the appState in the appState$ observable if it is present', async () => { const appState = { myValue: 'State to Preserve' }; + ( + auth0Client.isAuthenticated as unknown as MockInstance + ).mockResolvedValue(true); ( auth0Client.handleRedirectCallback as unknown as MockInstance ).mockResolvedValue({ appState }); @@ -1087,6 +1096,9 @@ describe('AuthService', () => { it('should preserve appState as-is for regular login', async () => { const appState = { myValue: 'State to Preserve' }; + ( + auth0Client.isAuthenticated as unknown as MockInstance + ).mockResolvedValue(true); ( auth0Client.handleRedirectCallback as unknown as MockInstance ).mockResolvedValue({ appState, response_type: ResponseType.Code }); @@ -1105,6 +1117,9 @@ describe('AuthService', () => { created_at: '2024-01-01T00:00:00.000Z', expires_at: '2024-01-02T00:00:00.000Z', }; + ( + auth0Client.isAuthenticated as unknown as MockInstance + ).mockResolvedValue(true); ( auth0Client.handleRedirectCallback as unknown as MockInstance ).mockResolvedValue({ @@ -1130,6 +1145,9 @@ describe('AuthService', () => { created_at: '2024-02-01T00:00:00.000Z', expires_at: '2024-02-02T00:00:00.000Z', }; + ( + auth0Client.isAuthenticated as unknown as MockInstance + ).mockResolvedValue(true); ( auth0Client.handleRedirectCallback as unknown as MockInstance ).mockResolvedValue({ diff --git a/projects/auth0-angular/src/lib/auth.service.ts b/projects/auth0-angular/src/lib/auth.service.ts index f930962d..bd381904 100644 --- a/projects/auth0-angular/src/lib/auth.service.ts +++ b/projects/auth0-angular/src/lib/auth.service.ts @@ -47,6 +47,8 @@ import { concatMap, tap, map, + filter, + take, takeUntil, catchError, switchMap, @@ -407,10 +409,7 @@ export class AuthService this.auth0Client.handleRedirectCallback(url) ).pipe( withLatestFrom(this.authState.isLoading$), - tap(([result, isLoading]) => { - if (!isLoading) { - this.authState.refresh(); - } + switchMap(([result, isLoading]) => { const { appState, response_type, ...rest } = result; const target = appState?.target ?? '/'; @@ -424,9 +423,22 @@ export class AuthService this.appStateSubject$.next(appState); } + if (!isLoading) { + this.authState.refresh(); + // Capacitor flow: app was already running so isLoading is false. + // refresh() is async — wait for isAuthenticated$ to become true + // before navigating so AuthGuard does not read the stale cached false. + return this.authState.isAuthenticated$.pipe( + filter((authenticated) => authenticated), + take(1), + tap(() => this.navigator.navigateByUrl(target)), + map(() => result) + ); + } + this.navigator.navigateByUrl(target); - }), - map(([result]) => result) + return of(result); + }) ); } @@ -577,10 +589,7 @@ export class AuthService updateAuthenticationMethod: ( id: string, data: UpdateAuthenticationMethodRequest - ) => - from( - this.auth0Client.myAccount.updateAuthenticationMethod(id, data) - ), + ) => from(this.auth0Client.myAccount.updateAuthenticationMethod(id, data)), enrollmentChallenge: (options: EnrollmentChallengeOptions) => from(this.auth0Client.myAccount.enrollmentChallenge(options)), enrollmentVerify: (options: EnrollmentVerifyOptions) =>