I have an angular application with an OAuth2 authentication that blocks my pages thanks to the 'angular-oauth2-oidc' library, I need to recover a role and permissions with this token via an api. But so I autilise AuthGuard for block route by role; but the problem is that for this AuthGuard I need the token or the role check comes first.
I therefore looked for how to manage multiple AuthGuard, and I found the solution of a creates a master that manage the call order, but my problem is due to the fact that I don’t know how to manually call OAuth2, or then I would need to override the AuthGuard, so since I can’t do either I’m a blocker.
const configAuthZero: AuthConfig = environment.idp;
export function storageFactory(): OAuthStorage {
return localStorage
}
@NgModule({
imports: [OAuthModule.forRoot({
resourceServer: {
allowedUrls: [environment.adressUrl],
sendAccessToken: true
}
})],
providers: [
InitialAuthService,
{ provide: AuthConfig, useValue: configAuthZero },
{ provide: OAuthStorage, useFactory: storageFactory },
{
provide: APP_INITIALIZER,
useFactory: (initialAuthService: InitialAuthService) => () =>
initialAuthService.initAuth(),
deps: [InitialAuthService],
multi: true,
},
],
})
export class AuthModule { }
@Injectable({
providedIn: "root",
})
export class InitialAuthService {
private jwtHelper: JwtHelperService = new JwtHelperService();
private isAuthenticatedSubject = new BehaviorSubject<boolean>(false);
private isAuthenticated = this.isAuthenticatedSubject.asObservable();
private _decodedAccessToken: any;
private _decodedIDToken: any;
get decodedAccessToken() {
return this._decodedAccessToken;
}
get decodedIDToken() {
return this._decodedIDToken;
}
get profile() {
return this.oauthService.loadUserProfile();
}
constructor(
private oauthService: OAuthService,
private authConfig: AuthConfig,
public router: Router,
private authService: AuthService,
) {
}
async initAuth(): Promise<any> {
return new Promise<void>((resolveFn, rejectFn) => {
this.oauthService.configure(this.authConfig);
this.oauthService.setStorage(localStorage);
this.oauthService.tokenValidationHandler = new JwksValidationHandler();
this.oauthService.events
.pipe(filter((e: any) => e.type === "token_received"))
.subscribe(({ type }) => {
this.handleNewToken();
});
this.oauthService.events
.pipe(filter((e: any) => e.type === "token_expires"))
.subscribe(({ type }) => {
console.debug("token_expires");
this.logoutSession();
});
this.oauthService.events
.pipe(filter((e: any) => e.type === "token_error"))
.subscribe(({ type }) => {
console.debug("token_error");
this.logoutSession();
});
this.oauthService.events
.subscribe(_ => {
this.isAuthenticatedSubject.next(this.oauthService.hasValidAccessToken());
});
this.oauthService.loadDiscoveryDocumentAndLogin().then(
(isLoggedIn) => {
if (isLoggedIn) {
resolveFn();
} else {
this.oauthService.initImplicitFlow();
rejectFn();
}
},
(error: { status: number; }) => {
console.log({ error });
if (error.status === 400) {
location.reload();
}
}
);
});
}
private handleNewToken() {
this._decodedAccessToken = this.jwtHelper.decodeToken(
this.oauthService.getAccessToken()
);
this._decodedIDToken = this.jwtHelper.decodeToken(
this.oauthService.getIdToken()
);
this.authService.login();
}
logoutSession() {
this.oauthService.logOut();
this.authService.logout();
}
isLogin() : Observable<boolean> {
return this.isAuthenticated;
}
}
@Injectable()
export class AuthService {
constructor(private userRoleService: UserRoleService) {
}
private user!: UserRole|null;
isAuthorized() {
return !!this.user;
}
hasRole(role: Role) {
return this.user!=null && this.isAuthorized() && this.user.id === role;
}
login() {
this.userRoleService.getByUserRoleToken().subscribe({
next: (res) => {
this.user = res;
},
error: (e) => {
console.error(e);
},
complete: () => {
}
});
}
logout() {
this.user = null;
}
}
@Injectable()
export class AuthGuard implements CanActivate, CanLoad {
constructor(
private router: Router,
private authService: AuthService
) { }
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if (!this.authService.isAuthorized()) {
this.router.navigate(['login']);
return false;
}
const roles = route.data['roles'] as Role[];
if (roles && !roles.some(r => this.authService.hasRole(r))) {
this.router.navigate(['error', 'not-found']);
return false;
}
return true;
}
canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
if (!this.authService.isAuthorized()) {
return false;
}
const roles = route.data && route.data["roles"] as Role[];
if (roles && !roles.some(r => this.authService.hasRole(r))) {
return false;
}
return true;
}
}
const routes: Routes = [
{
path: 'admin',
canLoad: [AuthGuard],
canActivate: [AuthGuard],
data: {
roles: [
"ADMIN",
]
},
loadChildren: () => import('./admin/admin-routing.module').then(m => m.AdminRoutingModule),
},
{ path: 'user', loadChildren: () => import('./auth/user/user.module').then(m => m.UserModule),},
// Always last
{ path: '**', component: PageNotFoundComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes),],
exports: [RouterModule],
declarations: [
],
providers: [
AuthGuard,
AuthService
],
})
export class AppRoutingModule { }