Files
AltitudeWeb/frontend/src/app/services/auth.service.ts
T

153 lines
3.9 KiB
TypeScript

import {Injectable, signal} from '@angular/core';
import {LoginService} from '@api';
import {Observable, throwError, timer} 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'
@Injectable({
providedIn: 'root'
})
export class AuthService {
private isAuthenticatedSubject = signal<boolean>(false);
public readonly isAuthenticated$ = this.isAuthenticatedSubject.asReadonly();
private userClaimsSubject = signal<JwtClaims | null>(null);
private jwtHelper = new JwtHelperService();
private _username = signal<string | null>(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();
timer(1000).subscribe(() => {
this.reloadUsername();
});
}
/**
* Attempt to login with the provided code
*/
public login(code: string): Observable<any> {
return this.loginService.login(code).pipe(
tap(jwt => {
this.saveJwt(jwt);
this.isAuthenticatedSubject.set(true);
this.reloadUsername();
}),
catchError(error => {
this.snackBar.open('Login failed', '', {duration: 2000});
return throwError(() => error);
})
);
}
private reloadUsername() {
if (window.location.hostname === 'localhost') {
console.log("Reloading username for localhost");
this._username.set('developer');
}
if (!this.isAuthenticated$()) {
return;
}
this.loginService.getUsername().subscribe({
next: (username) => {
console.log("Username retrieved: " + username.username);
this._username.set(username.username);
},
error: (error) => {
console.error("Error retrieving username:", error);
return throwError(() => error);
}
});
}
/**
* Log the user out by removing the JWT
*/
public logout(): void {
localStorage.removeItem('jwt');
this.isAuthenticatedSubject.set(false);
this.userClaimsSubject.set(null);
this._username.set(null);
}
/**
* Check if the user is authenticated
*/
private 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);
this.userClaimsSubject.set(claims);
this.isAuthenticatedSubject.set(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);
this.userClaimsSubject.set(claims);
}
/**
* Extract claims from JWT
*/
private extractJwtClaims(jwt: string): JwtClaims {
return <JwtClaims>this.jwtHelper.decodeToken(jwt);
}
/**
* Get user authorizations from claims
*/
public getUserAuthorizations(): string[] {
const claims = this.userClaimsSubject();
return claims?.authorities || [];
}
public hasAccess(requiredAuthorizations: string[]): boolean {
const userAuthorizations = this.getUserAuthorizations();
return requiredAuthorizations.some(auth => userAuthorizations.includes(auth));
}
public getUuid(): string | null {
const jwtClaims = this.userClaimsSubject();
if (jwtClaims === null) {
return null;
}
return jwtClaims.sub ?? null;
}
}