diff --git a/bun.lockb b/bun.lockb index a4ad67b..6fd82b0 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 232ef91..f1859dd 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@angular/platform-browser-dynamic": "^18.2.0", "@angular/router": "^18.2.0", "bootstrap": "^5.3.3", + "keycloak-angular": "^16.1.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.10" diff --git a/public/silent-check-sso.html b/public/silent-check-sso.html new file mode 100644 index 0000000..75b40d3 --- /dev/null +++ b/public/silent-check-sso.html @@ -0,0 +1,3 @@ + +

balls

+ diff --git a/src/app/app.config.ts b/src/app/app.config.ts index a1e7d6f..b047b4b 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,8 +1,47 @@ -import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { APP_INITIALIZER, ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; 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 { + return () => initializeKeycloak(keycloak)(); +} 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 + } + ] }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index ae2c715..7f0657d 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -3,6 +3,8 @@ import { LoginViewComponent } from './components/login-view/login-view.component import { MitarbeiterverwaltungViewComponent } from './components/mitarbeiterverwaltung-view/mitarbeiterverwaltung-view.component'; import { EmployeeDetailComponent } from './components/employee-detail/employee-detail.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 = [ { @@ -12,6 +14,7 @@ export const routes: Routes = [ { path: "mitarbeiter", component: MitarbeiterverwaltungViewComponent, + canActivate: [AuthGuard], }, { path: "mitarbeiterdetails", @@ -20,5 +23,9 @@ export const routes: Routes = [ { path: "qualifikationbearbeiten", component: QualifikatonBearbeitenViewComponent, + }, + { + path: "**", + redirectTo: '', } ]; diff --git a/src/app/components/login-view/login-view.component.ts b/src/app/components/login-view/login-view.component.ts index ffc3ab9..9e9655e 100644 --- a/src/app/components/login-view/login-view.component.ts +++ b/src/app/components/login-view/login-view.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { KeycloakService } from 'keycloak-angular'; @Component({ selector: 'app-login-view', @@ -8,5 +9,4 @@ import { Component } from '@angular/core'; styleUrl: './login-view.component.css' }) export class LoginViewComponent { - } diff --git a/src/app/service/auth.service.ts b/src/app/service/auth.service.ts new file mode 100644 index 0000000..0179ce9 --- /dev/null +++ b/src/app/service/auth.service.ts @@ -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)); + } +}