Refactor permission handling and authentication services
Replaced `PermissionClaim` enum with an OpenAPI-defined schema `PermissionClaimDto` for consistency across frontend and backend. Refactored authentication flow to utilize `AuthService` on the frontend, consolidating JWT handling logic. Removed redundant methods like `saveJwt` and integrated robust permission management throughout the application.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
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<boolean>(false);
|
||||
public isAuthenticated$ = this.isAuthenticatedSubject.asObservable();
|
||||
|
||||
private userClaimsSubject = new BehaviorSubject<any>(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<any> {
|
||||
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 || [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user