From 8d99521ffcced0645e6dc52522d58d8c06701772 Mon Sep 17 00:00:00 2001 From: Yosvel Quintero Date: Tue, 28 Nov 2023 14:10:14 +0700 Subject: [PATCH] chore: added token revocation --- packages/core/config/src/lib/constants/api.ts | 1 + .../src/lib/+state/account/account.effects.ts | 2 +- .../src/lib/+state/authn/authn.actions.ts | 14 +++++- .../src/lib/+state/authn/authn.effects.ts | 43 ++++++++++++++++--- .../src/lib/+state/authn/authn.facade.ts | 2 +- .../src/lib/+state/authn/authn.reducer.ts | 18 +++++++- .../management/country/country.reducer.ts | 2 +- .../lib/services/identity/authn.service.ts | 13 ++++++ packages/core/types/src/lib/interfaces/api.ts | 1 + .../core/types/src/lib/interfaces/auth.ts | 4 ++ 10 files changed, 88 insertions(+), 12 deletions(-) diff --git a/packages/core/config/src/lib/constants/api.ts b/packages/core/config/src/lib/constants/api.ts index 34757baf..83e1a130 100644 --- a/packages/core/config/src/lib/constants/api.ts +++ b/packages/core/config/src/lib/constants/api.ts @@ -3,5 +3,6 @@ import { IApiConstant } from '@console-core/types'; export const API: Readonly = { endpoints: { token: '/token', + tokenRevocation: '/token/revocation', }, }; diff --git a/packages/core/state/src/lib/+state/account/account.effects.ts b/packages/core/state/src/lib/+state/account/account.effects.ts index 09e8c948..bb277a5f 100644 --- a/packages/core/state/src/lib/+state/account/account.effects.ts +++ b/packages/core/state/src/lib/+state/account/account.effects.ts @@ -304,7 +304,7 @@ export class AccountEffects { resetAccountState$ = createEffect(() => { return this.actions$.pipe( - ofType(authnActions.signOut), + ofType(authnActions.signOutSuccess, authnActions.signOutFail), map(() => accountActions.resetAccountState()) ); }); diff --git a/packages/core/state/src/lib/+state/authn/authn.actions.ts b/packages/core/state/src/lib/+state/authn/authn.actions.ts index 2065c8ca..b83af44f 100644 --- a/packages/core/state/src/lib/+state/authn/authn.actions.ts +++ b/packages/core/state/src/lib/+state/authn/authn.actions.ts @@ -75,9 +75,19 @@ export const confirmPasswordFail = createAction( props<{ error: string }>() ); -export const signOut = createAction( - '[AUTHN] Sign out', +export const signOutRequest = createAction( + '[AUTHN] Sign out request', props<{ payload: { showNotification: boolean } }>() ); +export const signOutSuccess = createAction( + '[AUTHN] Sign out success', + props<{ payload: { showNotification: boolean } }>() +); + +export const signOutFail = createAction( + '[AUTHN] Sign out fail', + props<{ error: string; showNotification: boolean }>() +); + export const resetAuthnState = createAction('[AUTHN] Reset authn state'); diff --git a/packages/core/state/src/lib/+state/authn/authn.effects.ts b/packages/core/state/src/lib/+state/authn/authn.effects.ts index 5241baff..a24b4de3 100644 --- a/packages/core/state/src/lib/+state/authn/authn.effects.ts +++ b/packages/core/state/src/lib/+state/authn/authn.effects.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; import { catchError, map, of, switchMap, take, tap } from 'rxjs'; import { ROUTER } from '@console-core/config'; @@ -11,6 +11,7 @@ import { AccountFacade } from '../account'; import { AppFacade } from '../app'; import * as authnActions from './authn.actions'; +import { AuthnFacade } from './authn.facade'; @Injectable() export class AuthnEffects { @@ -251,12 +252,41 @@ export class AuthnEffects { { dispatch: false } ); - signOut$ = createEffect( + signOutRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(authnActions.signOutRequest), + concatLatestFrom(() => this.authnFacade.token$), + switchMap(([{ payload }, token]) => { + if (!token) { + return of( + authnActions.signOutFail({ + error: 'your token is invalid', + showNotification: payload.showNotification, + }) + ); + } + + return this.authnService.signOut({ token }).pipe( + map(() => authnActions.signOutSuccess({ payload })), + catchError((error: Error) => + of( + authnActions.signOutFail({ + error: error.message, + showNotification: payload.showNotification, + }) + ) + ) + ); + }) + ); + }); + + signOut = createEffect( () => { return this.actions$.pipe( - ofType(authnActions.signOut), - tap(({ payload }) => { - if (payload.showNotification) { + ofType(authnActions.signOutSuccess, authnActions.signOutFail), + tap((action) => { + if ('payload' in action && action.payload.showNotification) { this.appFacade.addNotification({ content: 'signed out', type: ENotificationTypes.SUCCESS, @@ -299,7 +329,7 @@ export class AuthnEffects { resetAuthnState$ = createEffect(() => { return this.actions$.pipe( - ofType(authnActions.signOut), + ofType(authnActions.signOutSuccess, authnActions.signOutFail), map(() => authnActions.resetAuthnState()) ); }); @@ -311,6 +341,7 @@ export class AuthnEffects { private readonly authnService: AuthnService, private readonly userService: UserService, private readonly appFacade: AppFacade, + private readonly authnFacade: AuthnFacade, private readonly accountFacade: AccountFacade ) {} } diff --git a/packages/core/state/src/lib/+state/authn/authn.facade.ts b/packages/core/state/src/lib/+state/authn/authn.facade.ts index b0906ff0..3558f861 100644 --- a/packages/core/state/src/lib/+state/authn/authn.facade.ts +++ b/packages/core/state/src/lib/+state/authn/authn.facade.ts @@ -36,7 +36,7 @@ export class AuthnFacade { this.store.dispatch(authnActions.signInRequest({ payload })); signOut = (showNotification = true) => this.store.dispatch( - authnActions.signOut({ + authnActions.signOutRequest({ payload: { showNotification }, }) ); diff --git a/packages/core/state/src/lib/+state/authn/authn.reducer.ts b/packages/core/state/src/lib/+state/authn/authn.reducer.ts index 0e416f15..b2c694c6 100644 --- a/packages/core/state/src/lib/+state/authn/authn.reducer.ts +++ b/packages/core/state/src/lib/+state/authn/authn.reducer.ts @@ -139,13 +139,29 @@ const reducer = createReducer( }) ), on( - authnActions.signOut, + authnActions.signOutRequest, + (state): IAuthnState => ({ + ...state, + actionStatus: EActionStatus.REQUESTING, + }) + ), + on( + authnActions.signOutSuccess, (state): IAuthnState => ({ ...state, isAuthenticated: false, actionStatus: EActionStatus.SUCCEEDED, }) ), + on( + authnActions.signOutFail, + (state, { error }): IAuthnState => ({ + ...state, + isAuthenticated: false, + actionStatus: EActionStatus.FAILED, + error, + }) + ), on( authnActions.resetAuthnState, (_): IAuthnState => ({ diff --git a/packages/core/state/src/lib/+state/management/country/country.reducer.ts b/packages/core/state/src/lib/+state/management/country/country.reducer.ts index 6db1f412..706b9d18 100644 --- a/packages/core/state/src/lib/+state/management/country/country.reducer.ts +++ b/packages/core/state/src/lib/+state/management/country/country.reducer.ts @@ -19,7 +19,7 @@ const reducer = createReducer( countryActions.countryReadRequest, (state): ICountryState => ({ ...state, - actionStatus: EActionStatus.CREATED, + actionStatus: EActionStatus.REQUESTING, }) ), on( diff --git a/packages/core/state/src/lib/services/identity/authn.service.ts b/packages/core/state/src/lib/services/identity/authn.service.ts index 75a858c7..c2ea68ad 100644 --- a/packages/core/state/src/lib/services/identity/authn.service.ts +++ b/packages/core/state/src/lib/services/identity/authn.service.ts @@ -12,6 +12,7 @@ import { import { IAuthnTokenSignInPayload, IAuthnTokenSignInResponse, + IAuthnTokenSignOutPayload, } from '@console-core/types'; import { ApiService } from '../api.service'; @@ -53,4 +54,16 @@ export class AuthnService { } ); } + + signOut(payload: IAuthnTokenSignOutPayload): Observable { + const body = new URLSearchParams(); + body.set('token', payload.token); + return this.httpClient.post( + this.apiService.getEndpoint('tokenRevocation'), + body.toString(), + { + headers: this.headers, + } + ); + } } diff --git a/packages/core/types/src/lib/interfaces/api.ts b/packages/core/types/src/lib/interfaces/api.ts index 6a90ae02..648d44ff 100644 --- a/packages/core/types/src/lib/interfaces/api.ts +++ b/packages/core/types/src/lib/interfaces/api.ts @@ -1,5 +1,6 @@ export interface IApiConstant { readonly endpoints: { readonly token: string; + readonly tokenRevocation: string; }; } diff --git a/packages/core/types/src/lib/interfaces/auth.ts b/packages/core/types/src/lib/interfaces/auth.ts index b5cbf22c..dbfe8aea 100644 --- a/packages/core/types/src/lib/interfaces/auth.ts +++ b/packages/core/types/src/lib/interfaces/auth.ts @@ -11,6 +11,10 @@ export interface IAuthnTokenSignInPayload { password?: string; } +export interface IAuthnTokenSignOutPayload { + token: string; +} + export interface IAuthnTokenSignInResponse { access_token?: string; id_token?: string;