import {Injectable, signal} from '@angular/core'; import {LoginService} from '@api'; import {BehaviorSubject, Observable, throwError} from 'rxjs'; import {catchError, tap} from 'rxjs/operators'; import {MatSnackBar} from '@angular/material/snack-bar'; import {JwtHelperService} from '@auth0/angular-jwt'; import {JwtClaims} from '@custom-types/jwt_interface' import {environment} from '@environment'; @Injectable({ providedIn: 'root' }) export class AuthService { private isAuthenticatedSubject = new BehaviorSubject(environment.defaultAuthStatus); public isAuthenticated$ = this.isAuthenticatedSubject.asObservable(); private userClaimsSubject = new BehaviorSubject(null); public userClaims$ = this.userClaimsSubject.asObservable(); private jwtHelper = new JwtHelperService(); private _username = signal(environment.defaultAuthStatus ? 'akastijn' : null); public readonly username = this._username.asReadonly(); constructor( private loginService: LoginService, private snackBar: MatSnackBar ) { // Check if user is already logged in on service initialization this.checkAuthStatus(); } /** * Attempt to login with the provided code */ public login(code: string): Observable { return this.loginService.login(code).pipe( tap(jwt => { this.saveJwt(jwt); this.isAuthenticatedSubject.next(true); this.reloadUsername(); }), catchError(error => { this.snackBar.open('Login failed', '', {duration: 2000}); return throwError(() => error); }) ); } private reloadUsername() { this.loginService.getUsername().pipe( tap(username => { this._username.set(username.username); }), catchError(error => { return throwError(() => error); }) ) } /** * Log the user out by removing the JWT */ public logout(): void { localStorage.removeItem('jwt'); this.isAuthenticatedSubject.next(false); this.userClaimsSubject.next(null); this._username.set(null); } /** * Check if the user is authenticated */ public checkAuthStatus(): boolean { const jwt = this.getJwt(); if (!jwt) { console.log("No JWT found"); return false; } try { if (this.jwtHelper.isTokenExpired(jwt)) { this.logout(); console.log("Token expired, logging out"); return false; } const claims = this.extractJwtClaims(jwt); console.log("User claims: ", claims); this.userClaimsSubject.next(claims); this.isAuthenticatedSubject.next(true); this.reloadUsername(); return true; } catch (e) { this.logout(); } return false; } /** * Get the JWT from localStorage */ public getJwt(): string | null { return localStorage.getItem('jwt'); } /** * Save the JWT to localStorage */ private saveJwt(jwt: string): void { localStorage.setItem('jwt', jwt); const claims = this.extractJwtClaims(jwt); console.log("User claims: ", claims); this.userClaimsSubject.next(claims); } /** * Extract claims from JWT */ private extractJwtClaims(jwt: string): JwtClaims { return this.jwtHelper.decodeToken(jwt); } /** * Get user authorizations from claims */ public getUserAuthorizations(): string[] { const claims = this.userClaimsSubject.getValue(); return claims?.authorities || []; } public hasAccess(requiredAuthorizations: string[]): boolean { const userAuthorizations = this.getUserAuthorizations(); return requiredAuthorizations.some(auth => userAuthorizations.includes(auth)); } public getUuid(): string | null { if (environment.defaultAuthStatus) { return '55e46bc3-2a29-4c53-850f-dbd944dc5c5f'; } const jwtClaims = this.userClaimsSubject.getValue(); if (jwtClaims === null) { return null; } return jwtClaims.sub ?? null; } }