feat: add Keycloak authentication integration and guard
This commit is contained in:
parent
aff058a29e
commit
fe8b1a1399
7 changed files with 95 additions and 3 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -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"
|
||||||
|
|
3
public/silent-check-sso.html
Normal file
3
public/silent-check-sso.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<html><body>
|
||||||
|
<h1>balls</h1>
|
||||||
|
<script>parent.postMessage(location.href, location.origin)</script></body></html>
|
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
|
@ -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: '',
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
42
src/app/service/auth.service.ts
Normal file
42
src/app/service/auth.service.ts
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue