diff --git a/src/app/core/auth/auth.actions.ts b/src/app/core/auth/auth.actions.ts index 03b6bc11910..802d61d169c 100644 --- a/src/app/core/auth/auth.actions.ts +++ b/src/app/core/auth/auth.actions.ts @@ -69,8 +69,16 @@ export class AuthenticatedAction implements Action { public type: string = AuthActionTypes.AUTHENTICATED; payload: AuthTokenInfo; - constructor(token: AuthTokenInfo) { + /** + * Whether we should consider the given authentication info final. + * If the backend restarted we may have a token that hasn't expired yet, but it will be invalid anyway. + * In this case we'll have to check twice. + */ + checkAgain: boolean; + + constructor(token: AuthTokenInfo, checkAgain = false) { this.payload = token; + this.checkAgain = checkAgain; } } diff --git a/src/app/core/auth/auth.effects.spec.ts b/src/app/core/auth/auth.effects.spec.ts index 733efd6a014..996db991bcc 100644 --- a/src/app/core/auth/auth.effects.spec.ts +++ b/src/app/core/auth/auth.effects.spec.ts @@ -156,7 +156,7 @@ describe('AuthEffects', () => { describe('when token is valid', () => { it('should return a AUTHENTICATED_SUCCESS action in response to a AUTHENTICATED action', () => { - actions = hot('--a-', { a: { type: AuthActionTypes.AUTHENTICATED, payload: token } }); + actions = hot('--a-', { a: new AuthenticatedAction(token) }); const expected = cold('--b-', { b: new AuthenticatedSuccessAction(true, token, EPersonMock._links.self.href) }); @@ -164,17 +164,29 @@ describe('AuthEffects', () => { }); }); - describe('when token is not valid', () => { + describe('when token is expired', () => { it('should return a AUTHENTICATED_ERROR action in response to a AUTHENTICATED action', () => { spyOn((authEffects as any).authService, 'authenticatedUser').and.returnValue(observableThrow(new Error('Message Error test'))); - actions = hot('--a-', { a: { type: AuthActionTypes.AUTHENTICATED, payload: token } }); + actions = hot('--a-', { a: new AuthenticatedAction(token) }); const expected = cold('--b-', { b: new AuthenticatedErrorAction(new Error('Message Error test')) }); expect(authEffects.authenticated$).toBeObservable(expected); }); }); + + describe('when token is not valid but also not expired (~ cookie)', () => { + it('should return a AUTHENTICATED_ERROR action in response to a AUTHENTICATED action', () => { + spyOn((authEffects as any).authService, 'authenticatedUser').and.returnValue(observableThrow(new Error('Message Error test'))); + + actions = hot('--a-', { a: new AuthenticatedAction(token, true) }); + + const expected = cold('--b-', { b: new CheckAuthenticationTokenCookieAction() }); + + expect(authEffects.authenticated$).toBeObservable(expected); + }); + }); }); describe('authenticatedSuccess$', () => { @@ -210,7 +222,7 @@ describe('AuthEffects', () => { actions = hot('--a-', { a: { type: AuthActionTypes.CHECK_AUTHENTICATION_TOKEN } }); - const expected = cold('--b-', { b: new AuthenticatedAction(token) }); + const expected = cold('--b-', { b: new AuthenticatedAction(token, true) }); expect(authEffects.checkToken$).toBeObservable(expected); }); diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index 05f8c597033..89b4df80ece 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -121,7 +121,13 @@ export class AuthEffects { switchMap((action: AuthenticatedAction) => { return this.authService.authenticatedUser(action.payload).pipe( map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)), - catchError((error: unknown) => errorToAuthAction$(AuthenticatedErrorAction, error)), + catchError((error: unknown) => { + if (action.checkAgain) { + return of(new CheckAuthenticationTokenCookieAction()); + } else { + return errorToAuthAction$(AuthenticatedErrorAction, error); + } + }), ); }), )); @@ -176,7 +182,7 @@ export class AuthEffects { public checkToken$: Observable = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN), switchMap(() => { return this.authService.hasValidAuthenticationToken().pipe( - map((token: AuthTokenInfo) => new AuthenticatedAction(token)), + map((token: AuthTokenInfo) => new AuthenticatedAction(token, true)), catchError((error: unknown) => of(new CheckAuthenticationTokenCookieAction())), ); }),