Merge pull request 'Authorization' (#11) from feature/auth into trunk
All checks were successful
Quality Check / Linting (push) Successful in 21s

Reviewed-on: #11
Reviewed-by: SZUT-Rajbir <rajbir2@schule.bremen.de>
This commit is contained in:
Dominik Säume 2025-01-08 11:02:20 +00:00 committed by Euph Forge
commit a0f1d31523
Signed by: Euph Forge
GPG key ID: 85A06461FB6BDBB7
11 changed files with 120 additions and 25 deletions

View file

@ -16,3 +16,8 @@
1. Docker Container Hochfahren
1. Angular App starten
## Zugangsdaten
**Benutzername:** `user`
**Passwort:** `test`

View file

@ -1,4 +0,0 @@
POST http://authproxy.szut.dev
Content-Type: application/x-www-form-urlencoded
grant_type=password&client_id=employee-management-service&username=user&password=test

23
package-lock.json generated
View file

@ -18,6 +18,7 @@
"@angular/platform-browser": "^19.0.0",
"@angular/platform-browser-dynamic": "^19.0.0",
"@angular/router": "^19.0.0",
"angular-auth-oidc-client": "^19.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
@ -5955,6 +5956,22 @@
"ajv": "^8.8.2"
}
},
"node_modules/angular-auth-oidc-client": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/angular-auth-oidc-client/-/angular-auth-oidc-client-19.0.0.tgz",
"integrity": "sha512-CloBjmHjG6CxbFFjYB1Ei+e172JUY1V3cK/v9pdbVuUz3OhiMC6CxBr331oB3Em2eWMmLi23jecl10lfiy9WUQ==",
"license": "MIT",
"dependencies": {
"rfc4648": "^1.5.0",
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": ">=15.0.0",
"@angular/core": ">=15.0.0",
"@angular/router": ">=15.0.0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/ansi-colors": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
@ -13545,6 +13562,12 @@
"node": ">=0.10.0"
}
},
"node_modules/rfc4648": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.4.tgz",
"integrity": "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==",
"license": "MIT"
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",

View file

@ -22,6 +22,7 @@
"@angular/platform-browser": "^19.0.0",
"@angular/platform-browser-dynamic": "^19.0.0",
"@angular/router": "^19.0.0",
"angular-auth-oidc-client": "^19.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"

View file

@ -4,14 +4,28 @@ import { provideAnimationsAsync } from '@angular/platform-browser/animations/asy
import {provideRouter} from '@angular/router';
import {routes} from '@app/app.routes';
import {OpenAPI} from '@core/ems/core/OpenAPI';
import {LogLevel, provideAuth} from 'angular-auth-oidc-client';
OpenAPI.BASE = 'http://localhost:8080';
OpenAPI.TOKEN = 'eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzUFQ0dldiNno5MnlQWk1EWnBqT1U0RjFVN0lwNi1ELUlqQWVGczJPbGU0In0.eyJleHAiOjE3MzQ1MjA4MzgsImlhdCI6MTczNDUxNzIzOCwianRpIjoiODVkM2NlNDQtM2QwMS00MDFkLTlhY2YtZjFhZTMwNzQzMDRmIiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay5zenV0LmRldi9hdXRoL3JlYWxtcy9zenV0IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjU1NDZjZDIxLTk4NTQtNDMyZi1hNDY3LTRkZTNlZWRmNTg4OSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImVtcGxveWVlLW1hbmFnZW1lbnQtc2VydmljZSIsInNlc3Npb25fc3RhdGUiOiJiOWI3OGQ0ZS1jM2E4LTRiOWQtODdmMy04N2FiNzhjYzQyYTMiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9sb2NhbGhvc3Q6NDIwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsicHJvZHVjdF9vd25lciIsIm9mZmxpbmVfYWNjZXNzIiwiZGVmYXVsdC1yb2xlcy1zenV0IiwidW1hX2F1dGhvcml6YXRpb24iLCJ1c2VyIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6InVzZXIifQ.I3CGELu7IXrh5kYapp-v54c-R8hNqZOtvpmR98NhoTCrOVUoA-V16ayhF42qQ3Pj2YymxENpQWHR1-BTFQOJmAhQKBzvKWgOHuiLCPr8NkMGWFQ520BcJdipbGnsM8tmXuLE9FWezHA7LEGVkwY2gTbQMLEPq8v8hrtIf76F2Dq34BIG3Nq_6QlG10rG_Heqta6kWWe2p4DJmebdpvAeW2qvTLD4x89oIhtYXSiRIiVS2uAur5XuJcjpbv8UXWx11zVX7KelZEXky92q_xPKerFMOGt6up5MkCijJBhVWewSWNldaix7_jKTXCp70Gtu4sgCIuoojKp9Nts942RZjw';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({eventCoalescing: true}),
provideAnimationsAsync(),
provideRouter(routes),
provideHttpClient(),
provideAnimationsAsync(), provideAnimationsAsync()
provideAuth({
config: {
authority: 'https://keycloak.szut.dev/auth/realms/szut',
redirectUrl: window.location.origin,
postLogoutRedirectUri: window.location.origin,
clientId: 'employee-management-service',
scope: 'openid profile email offline_access',
responseType: 'code',
silentRenew: true,
useRefreshToken: true,
logLevel: LogLevel.Error,
},
})
],
};

6
src/app/core/auth/UserData.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
interface UserData {
username: string,
verified: boolean
}
export default UserData;

View file

@ -0,0 +1,32 @@
import {Injectable} from '@angular/core';
import UserData from '@core/auth/UserData';
import {OidcSecurityService} from 'angular-auth-oidc-client';
import {Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
public $user: Observable<UserData | undefined>;
constructor(private readonly oidcSecurityService: OidcSecurityService) {
this.$user = new Observable((publish) => {
this.oidcSecurityService.checkAuth().subscribe(({isAuthenticated, userData}) => {
publish.next(isAuthenticated ? {
username: userData.preferred_username,
verified: userData.email_verified
} : undefined);
});
});
}
login() {
this.oidcSecurityService.authorize();
}
logout() {
this.oidcSecurityService.logoff();
}
}

View file

@ -1,12 +1,25 @@
<mat-toolbar class="header">
<nav>
<a routerLink="'/'" mat-button><mat-icon>badge</mat-icon>EMS</a>
<a routerLink="'/'" mat-button>
<mat-icon>badge</mat-icon>
EMS</a>
@for (route of routes; track route) {
<a mat-button href="{{ route.path }}" class="{{ route.class }}">{{ route.title }}</a>
}
</nav>
<button mat-button class="header__login">
@if (auth.$user|async; as user) {
<a mat-button href="https://keycloak.szut.dev/auth/realms/szut/account">
{{ user.username|titlecase }}
</a>
<button mat-icon-button (click)="auth.logout()">
<mat-icon>logout</mat-icon>
</button>
} @else {
<button mat-button class="header__login" (click)="auth.login()">
<mat-icon>login</mat-icon>
Login
</button>
}
</mat-toolbar>

View file

@ -1,9 +1,10 @@
import {Location} from '@angular/common';
import {AsyncPipe, Location, TitleCasePipe} from '@angular/common';
import {Component, OnInit} from '@angular/core';
import {MatAnchor, MatButton} from '@angular/material/button';
import {MatAnchor, MatButton, MatIconButton} from '@angular/material/button';
import {MatIcon} from '@angular/material/icon';
import {MatToolbar} from '@angular/material/toolbar';
import {Router, RouterLink} from '@angular/router';
import {AuthService} from '@core/auth/auth.service';
@Component({
selector: 'app-header',
@ -13,6 +14,9 @@ import {Router, RouterLink} from '@angular/router';
RouterLink,
MatIcon,
MatButton,
MatIconButton,
AsyncPipe,
TitleCasePipe,
],
templateUrl: './header.component.html',
styleUrl: './header.component.scss'
@ -22,7 +26,8 @@ export class HeaderComponent implements OnInit {
constructor(
private router: Router,
private location: Location
private location: Location,
protected auth: AuthService
) {
}

View file