import {Injectable} from '@angular/core'; import {LoginService} from '@api'; import {CookieService} from 'ngx-cookie-service'; import {BehaviorSubject, Observable, throwError} from 'rxjs'; import {catchError, tap} from 'rxjs/operators'; import {MatSnackBar} from '@angular/material/snack-bar'; @Injectable({ providedIn: 'root' }) export class AuthService { private isAuthenticatedSubject = new BehaviorSubject(false); public isAuthenticated$ = this.isAuthenticatedSubject.asObservable(); private userClaimsSubject = new BehaviorSubject(null); public userClaims$ = this.userClaimsSubject.asObservable(); constructor( private loginService: LoginService, private cookieService: CookieService, 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 as JsonWebKey); this.isAuthenticatedSubject.next(true); }), catchError(error => { this.snackBar.open('Login failed', '', {duration: 2000}); return throwError(() => error); }) ); } /** * Log the user out by removing the JWT */ public logout(): void { this.cookieService.delete('jwt', '/'); this.isAuthenticatedSubject.next(false); this.userClaimsSubject.next(null); } /** * Check if the user is authenticated */ public checkAuthStatus(): boolean { const jwt = this.getJwt(); if (jwt) { try { const claims = this.extractJwtClaims(jwt as JsonWebKey); // Check if token is expired const currentTime = Math.floor(Date.now() / 1000); if (claims.exp && claims.exp < currentTime) { this.logout(); return false; } this.userClaimsSubject.next(claims); this.isAuthenticatedSubject.next(true); return true; } catch (e) { this.logout(); } } return false; } /** * Get the JWT from cookies */ public getJwt(): string | null { return this.cookieService.check('jwt') ? this.cookieService.get('jwt') : null; } /** * Save the JWT to cookies */ private saveJwt(jwt: JsonWebKey): void { this.cookieService.set('jwt', jwt.toString(), { path: '/', secure: true, sameSite: 'Strict' }); const claims = this.extractJwtClaims(jwt); this.userClaimsSubject.next(claims); } /** * Extract claims from JWT */ private extractJwtClaims(jwt: JsonWebKey): any { const token = jwt.toString(); const base64Url = token.split('.')[1]; const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); return JSON.parse(window.atob(base64)); } /** * Get user authorizations from claims */ public getUserAuthorizations(): string[] { const claims = this.userClaimsSubject.getValue(); return claims?.authorizations || []; } public hasAccess(authorization: string[]): boolean { return this.getUserAuthorizations().filter(entry => authorization.includes(entry)).length > 0; } }