Ajout de la dépendance:
npm install --save keycloak-angular@latest
npm install --save [email protected]
Editez app.module, pour ajouter le provider et remplacez bootstrap par entryComponents, ajout de la méthode keycloakService.init
environment.settings.keycloakJsonUrl
contient l'emplacement relatif du fichier keycloak.json généré à partir de keycloak ex : ./assets/config/keycloak.json
import { KeycloakService, KeycloakAngularModule } from 'keycloak-angular';
const keycloakService = new KeycloakService();
@NgModule({
imports: [KeycloakAngularModule],
providers: [
{
provide: KeycloakService,
useValue: keycloakService
}
],
entryComponents: [AppComponent] /*<- ne pas oublier la modification ici */
})
export class AppModule {
ngDoBootstrap(app: ApplicationRef) {
Logger.i('[AppModule] ngDoBootstrap');
keycloakService
.init({
config: environment.settings.keycloakJsonUrl,
loadUserProfileAtStartUp: false, /* Important car REALM/account est desactivé */
enableBearerInterceptor: true,
bearerPrefix: 'Bearer', /* Important voir la RFC 6750*/
bearerExcludedUrls: ['/assets']
})
.then(() => {
Logger.i('[AppModule] Keycloak init success');
app.bootstrap(AppComponent);
})
.catch(error => Logger.e('[AppModule] Keycloak init failed', error));
}
}
Ajout du AuhtGuard (routage)
ng g auth auth/auth
ng g guard auth/auth
Modifiez le fichier auth/auth-guard.ts généré par celui joint à ce snippet
Editez votre module de routage pour y protéger vos routes :
const routes: Routes = [
{
path: "",
redirectTo: "/home",
pathMatch: 'full'
},
{
path: "home",
component: HomeComponent
},
{
path: "secure",
component: SecureComponent,
canActivate: [AuthGuard]
},
{
path: "admin",
component: AdminComponent,
data: {
roles: ["ADMIN_APP"] // Ici le jeton Keycloak devra contenir le rôle ADMIN_APP
},
canActivate: [AuthGuard],
}
];
Il faut s'abonner pour être notifié du refus par un rôle manquant
this.authGuard.onRoleError().subscribe(() => {
this.showMessage();
})
Récupération du profile de l'utilisateur
this.keycloakService.loadUserProfile().then((profile) => {
console.log(profile);
})
S'abonner aux événements de Keycloak
this.keycloakService.keycloakEvents$.subscribe(e => {
console.log(e);
})
fichier auth-guard.ts
import { Injectable, Output } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
import { Subject, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthGuard extends KeycloakAuthGuard implements CanActivate {
private _roleError: Subject<any> = new Subject();
private roleError$ = this._roleError.asObservable();
constructor(protected router: Router, protected keycloakAngular: KeycloakService) {
super(router, keycloakAngular);
}
public onRoleError(): Observable<any> {
return this._roleError.asObservable();
}
isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return new Promise((resolve, reject) => {
if (!this.authenticated) {
this.keycloakAngular.login()
.catch(e => console.error(e));
return reject(false); // throw Error: Uncaught (in promise): An error happened during access validation. Details:false in console
}
const requiredRoles: string[] = route.data.roles;
if (!requiredRoles || requiredRoles.length === 0) {
return resolve(true);
} else {
if (!this.roles || this.roles.length === 0) {
this._roleError.next(null);
resolve(false);
}
if (requiredRoles.every(role => this.roles.indexOf(role) > -1)) {
resolve(true);
} else {
this._roleError.next(null);
resolve(false);
}
}
});
}
}
Utilisation en dehors des routes (sur un composant en particulier)
public connectedUser = false;
constructor(private keycloakService: KeycloakService) {
}
ngOnInit(): void {
this.keycloakService.keycloakEvents$.subscribe(e => {
Logger.i(e.type);
switch (e.type) {
case KeycloakEventType.OnAuthSuccess:
case KeycloakEventType.OnReady:
this.connectedUser = true;
break;
case KeycloakEventType.OnAuthError:
case KeycloakEventType.OnAuthLogout:
case KeycloakEventType.OnAuthRefreshError:
case KeycloakEventType.OnAuthRefreshSuccess:
case KeycloakEventType.OnTokenExpired:
default:
this.connectedUser = false;
break;
}
});
}
login() {
this.keycloakService.login().then((e) => {
}).catch((e) => {
Logger.e(e);
});
}
logout() {
this.connectedUser = false;
this.keycloakService.logout();
}