feat: add Keycloak authentication integration and guard

This commit is contained in:
Jan Gleytenhoover 2024-12-18 14:06:19 +01:00
parent aff058a29e
commit fe8b1a1399
Signed by: jank
GPG key ID: B267751B8AE29EFE
7 changed files with 95 additions and 3 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -19,6 +19,7 @@
"@angular/platform-browser-dynamic": "^18.2.0", "@angular/platform-browser-dynamic": "^18.2.0",
"@angular/router": "^18.2.0", "@angular/router": "^18.2.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"keycloak-angular": "^16.1.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"

View file

@ -0,0 +1,3 @@
<html><body>
<h1>balls</h1>
<script>parent.postMessage(location.href, location.origin)</script></body></html>

View file

@ -1,8 +1,47 @@
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { APP_INITIALIZER, ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router'; import { provideRouter } from '@angular/router';
import { routes } from './app.routes'; import { routes } from './app.routes';
import {KeycloakAngularModule, KeycloakBearerInterceptor, KeycloakService} from "keycloak-angular";
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
export const initializeKeycloak = (keycloak: KeycloakService) => async () =>
keycloak.init({
config: {
url: 'https://keycloak.szut.dev/auth',
realm: 'szut',
clientId: 'employee-management-service-frontend',
},
loadUserProfileAtStartUp: true,
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/silent-check-sso.html',
checkLoginIframe: false,
redirectUri: 'http://localhost:4200',
},
});
function initializeApp(keycloak: KeycloakService): () => Promise<boolean> {
return () => initializeKeycloak(keycloak)();
}
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)] providers: [
provideRouter(routes),
KeycloakAngularModule,
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
multi: true,
deps: [KeycloakService]
},
KeycloakService,
provideHttpClient(withInterceptorsFromDi()),
{
provide: HTTP_INTERCEPTORS,
useClass: KeycloakBearerInterceptor,
multi: true
}
]
}; };

View file

@ -3,6 +3,8 @@ import { LoginViewComponent } from './components/login-view/login-view.component
import { MitarbeiterverwaltungViewComponent } from './components/mitarbeiterverwaltung-view/mitarbeiterverwaltung-view.component'; import { MitarbeiterverwaltungViewComponent } from './components/mitarbeiterverwaltung-view/mitarbeiterverwaltung-view.component';
import { EmployeeDetailComponent } from './components/employee-detail/employee-detail.component'; import { EmployeeDetailComponent } from './components/employee-detail/employee-detail.component';
import { QualifikatonBearbeitenViewComponent } from './components/qualifikaton-bearbeiten-view/qualifikaton-bearbeiten-view.component'; import { QualifikatonBearbeitenViewComponent } from './components/qualifikaton-bearbeiten-view/qualifikaton-bearbeiten-view.component';
import { KeycloakAuthGuard } from 'keycloak-angular';
import { AuthGuard } from './service/auth.service';
export const routes: Routes = [ export const routes: Routes = [
{ {
@ -12,6 +14,7 @@ export const routes: Routes = [
{ {
path: "mitarbeiter", path: "mitarbeiter",
component: MitarbeiterverwaltungViewComponent, component: MitarbeiterverwaltungViewComponent,
canActivate: [AuthGuard],
}, },
{ {
path: "mitarbeiterdetails", path: "mitarbeiterdetails",
@ -20,5 +23,9 @@ export const routes: Routes = [
{ {
path: "qualifikationbearbeiten", path: "qualifikationbearbeiten",
component: QualifikatonBearbeitenViewComponent, component: QualifikatonBearbeitenViewComponent,
},
{
path: "**",
redirectTo: '',
} }
]; ];

View file

@ -1,4 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { KeycloakService } from 'keycloak-angular';
@Component({ @Component({
selector: 'app-login-view', selector: 'app-login-view',
@ -8,5 +9,4 @@ import { Component } from '@angular/core';
styleUrl: './login-view.component.css' styleUrl: './login-view.component.css'
}) })
export class LoginViewComponent { export class LoginViewComponent {
} }

View file

@ -0,0 +1,42 @@
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot
} from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
@Injectable({
providedIn: 'root'
})
export class AuthGuard extends KeycloakAuthGuard {
constructor(
protected override readonly router: Router,
protected readonly keycloak: KeycloakService
) {
super(router, keycloak);
}
public async isAccessAllowed(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) {
// Force the user to log in if currently unauthenticated.
if (!this.authenticated) {
await this.keycloak.login({
redirectUri: window.location.origin + state.url
});
}
// Get the roles required from the route.
const requiredRoles = route.data['roles'];
// Allow the user to proceed if no additional roles are required to access the route.
if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) {
return true;
}
// Allow the user to proceed if all the required roles are present.
return requiredRoles.every((role) => this.roles.includes(role));
}
}