From 8fde9eec377685c3148ff39236f3ac74318cfc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr> Date: Thu, 19 Nov 2020 16:53:44 +0100 Subject: [PATCH] Update auth module --- package.json | 1 + src/app/auth/auth.action.ts | 19 +++++++++++++++++- src/app/auth/auth.effects.ts | 23 ++++++++++++++++++---- src/app/auth/auth.reducer.ts | 13 +++++++++++- src/app/auth/auth.selector.ts | 11 ++++++++--- src/app/auth/auth.service.ts | 7 ++++++- src/app/core/components/nav.component.html | 8 ++++---- src/app/core/components/nav.component.ts | 1 + src/environments/environment.prod.ts | 1 + src/environments/environment.ts | 1 + yarn.lock | 5 +++++ 11 files changed, 76 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index c09cf705..063d8be8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "angular-auth-oidc-client": "^11.2.2", "bootstrap": "^4.5.3", "d3": "^5.15.1", + "jwt-decode": "^3.1.2", "ng-inline-svg": "^11.0.1", "ngx-bootstrap": "^6.1.0", "ngx-json-viewer": "^2.4.0", diff --git a/src/app/auth/auth.action.ts b/src/app/auth/auth.action.ts index 84c0990f..66b97e09 100644 --- a/src/app/auth/auth.action.ts +++ b/src/app/auth/auth.action.ts @@ -15,6 +15,9 @@ export const LOGIN = '[Auth] Login'; export const LOGIN_COMPLETE = '[Auth] Login Complete'; export const LOGOUT = '[Auth] Logout'; export const OPEN_EDIT_PROFILE = '[Auth] Edit Profile'; +export const GET_USER_ROLES = '[Auth] Get User Roles'; +export const PARSE_JWT = '[Auth] Parse JWT'; +export const LOAD_USER_ROLES = '[Auth] Load User Roles'; export class CheckAuthAction implements Action { readonly type = CHECK_AUTH; @@ -63,10 +66,24 @@ export class OpenEditProfileAction implements Action { constructor(public payload: {} = null) { } } +export class ParseJwtAction implements Action { + readonly type = PARSE_JWT; + + constructor(public payload: string) { } +} + +export class LoadUserRolesAction implements Action { + readonly type = LOAD_USER_ROLES; + + constructor(public payload: string[]) { } +} + export type Actions = CheckAuthAction | CheckAuthCompleteAction | LoginAction | LoginCompleteAction | LogoutAction - | OpenEditProfileAction; + | OpenEditProfileAction + | ParseJwtAction + | LoadUserRolesAction; diff --git a/src/app/auth/auth.effects.ts b/src/app/auth/auth.effects.ts index f3418fd1..f8472960 100644 --- a/src/app/auth/auth.effects.ts +++ b/src/app/auth/auth.effects.ts @@ -13,6 +13,7 @@ import { Router } from '@angular/router'; import { of } from 'rxjs'; import { switchMap, map, tap } from 'rxjs/operators'; import { Effect, Actions, ofType } from '@ngrx/effects'; +import jwt_decode from 'jwt-decode'; import * as authActions from './auth.action'; import { AuthService } from './auth.service'; @@ -39,7 +40,7 @@ export class AuthEffects { tap((action: authActions.LoginAction) => { const redirectUrl = action.payload; if (redirectUrl !== null) { - sessionStorage.setItem(environment.ssoClientId + '_redirectUrl', this.router.url); + sessionStorage.setItem(environment.ssoClientId + '_redirectUrl', redirectUrl); } return this.authService.doLogin() }) @@ -65,9 +66,10 @@ export class AuthEffects { if (isAuthenticated) { return this.authService.userData.pipe( - map((profile) => - new authActions.LoginCompleteAction(profile) - ) + switchMap((profile) => [ + new authActions.LoginCompleteAction(profile), + new authActions.ParseJwtAction(this.authService.token) + ]) ); } else { return of({ type: '[No Action] ' + authActions.CHECK_AUTH_COMPLETE }); @@ -87,6 +89,19 @@ export class AuthEffects { }) ); + @Effect() + parseJWTAction$ = this.actions$.pipe( + ofType(authActions.PARSE_JWT), + switchMap((action: authActions.ParseJwtAction) => { + const jwt = jwt_decode(action.payload) as any; + if (environment.ssoName === 'auth0') { + return of({ type: '[No Action] ' + authActions.PARSE_JWT }); + } else { + return of(new authActions.LoadUserRolesAction(jwt.realm_access.roles)); + } + }) + ); + @Effect({ dispatch: false }) logoutAction$ = this.actions$.pipe( ofType(authActions.LOGOUT), diff --git a/src/app/auth/auth.reducer.ts b/src/app/auth/auth.reducer.ts index b50c5516..6f46c3e1 100644 --- a/src/app/auth/auth.reducer.ts +++ b/src/app/auth/auth.reducer.ts @@ -17,11 +17,13 @@ import * as actions from './auth.action'; export interface State { isAuthenticated: boolean; userProfile: any; + userRoles: string[]; } export const initialState: State = { isAuthenticated: false, - userProfile: null + userProfile: null, + userRoles: [] }; /** @@ -43,6 +45,14 @@ export function reducer(state: State = initialState, action: actions.Actions): S userProfile }; + case actions.LOAD_USER_ROLES: + const userRoles = action.payload; + + return { + ...state, + userRoles + }; + case actions.LOGOUT: return { ...state, @@ -57,3 +67,4 @@ export function reducer(state: State = initialState, action: actions.Actions): S export const isAuthenticated = (state: State) => state.isAuthenticated; export const getUserProfile = (state: State) => state.userProfile; +export const getUserRoles = (state: State) => state.userRoles; diff --git a/src/app/auth/auth.selector.ts b/src/app/auth/auth.selector.ts index c312a6e4..db6ccdde 100644 --- a/src/app/auth/auth.selector.ts +++ b/src/app/auth/auth.selector.ts @@ -11,14 +11,19 @@ import { createSelector, createFeatureSelector } from '@ngrx/store'; import * as auth from './auth.reducer'; -export const getAuthtate = createFeatureSelector<auth.State>('auth'); +export const getAuthState = createFeatureSelector<auth.State>('auth'); export const isAuthenticated = createSelector( - getAuthtate, + getAuthState, auth.isAuthenticated ); export const getUserProfile = createSelector( - getAuthtate, + getAuthState, auth.getUserProfile ); + +export const getUserRoles = createSelector( + getAuthState, + auth.getUserRoles +); diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts index 19481135..5b82d73e 100644 --- a/src/app/auth/auth.service.ts +++ b/src/app/auth/auth.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { of } from 'rxjs'; import { OidcSecurityService } from 'angular-auth-oidc-client'; +import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root' }) export class AuthService { @@ -12,7 +13,11 @@ export class AuthService { } get token() { - return this.oidcSecurityService.getIdToken(); + if (environment.ssoName === 'auth0') { + return this.oidcSecurityService.getIdToken(); + } else { + return this.oidcSecurityService.getToken(); + } } get userData() { diff --git a/src/app/core/components/nav.component.html b/src/app/core/components/nav.component.html index 8161ec80..5d0a999d 100644 --- a/src/app/core/components/nav.component.html +++ b/src/app/core/components/nav.component.html @@ -49,12 +49,12 @@ <span class="dropdown-item font-italic">{{ userProfile.email }}</span> </li> <li class="divider dropdown-divider"></li> - <li role="menuitem"> + <li *ngIf="ssoName != 'auth0'" role="menuitem"> <button class="dropdown-item pointer" (click)="openEditProfile.emit()"> <span class="fas fa-id-card"></span> Edit profile </button> </li> - <li class="divider dropdown-divider"></li> + <li *ngIf="ssoName != 'auth0'" class="divider dropdown-divider"></li> <li role="menuitem"> <button class="dropdown-item text-danger pointer" (click)="logout.emit()"> <span class="fas fa-sign-out-alt fa-fw"></span> Sign Out @@ -96,12 +96,12 @@ </a> </li> <li *ngIf="getConfig('authentication', 'allowed') && isAuthenticated" class="divider dropdown-divider"></li> - <li *ngIf="getConfig('authentication', 'allowed') && isAuthenticated" role="menuitem"> + <li *ngIf="getConfig('authentication', 'allowed') && isAuthenticated && ssoName != 'auth0'" role="menuitem"> <button class="dropdown-item pointer" (click)="openEditProfile.emit()"> <span class="fas fa-id-card"></span> Edit profile </button> </li> - <li *ngIf="getConfig('authentication', 'allowed')" class="divider dropdown-divider"></li> + <li *ngIf="getConfig('authentication', 'allowed') && ssoName != 'auth0'" class="divider dropdown-divider"></li> <li role="menuitem"> <button *ngIf="getConfig('authentication', 'allowed') && !isAuthenticated" class="dropdown-item text-success" diff --git a/src/app/core/components/nav.component.ts b/src/app/core/components/nav.component.ts index fce80ae4..e65122ab 100644 --- a/src/app/core/components/nav.component.ts +++ b/src/app/core/components/nav.component.ts @@ -31,6 +31,7 @@ export class NavComponent { @Output() openEditProfile: EventEmitter<any> = new EventEmitter(); baseHref: string = environment.baseHref; + ssoName: string = environment.ssoName; /** * Checks if given key for the given configuration propriety is allowed. diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 777ce7a7..bcf02505 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -14,6 +14,7 @@ export const environment = { spectraUrl: 'https://cesam.lam.fr/anis-tools/spectra_to_csv/?file=/dataproject/SPECTRA/', instanceName: 'default', baseHref: '/', + ssoName: 'keycloak', ssoAuthUrl: 'https://anis-dev.lam.fr/auth/realms/anis', ssoClientId: 'anis-client' }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 91489b09..a92b10b3 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -18,6 +18,7 @@ export const environment = { spectraUrl: 'https://cesam.lam.fr/anis-tools/spectra_to_csv/?file=/dataproject/SPECTRA/', instanceName: 'default', baseHref: '/', + ssoName: 'keycloak', ssoAuthUrl: 'http://localhost:8180/auth/realms/anis', ssoClientId: 'anis-client' }; diff --git a/yarn.lock b/yarn.lock index 4dddb419..9d7951e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5962,6 +5962,11 @@ jszip@^3.1.3: readable-stream "~2.3.6" set-immediate-shim "~1.0.1" +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + karma-chrome-launcher@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738" -- GitLab