Fix authentication errors handling.
This commit is contained in:
18
frontend/src/app/core/guard/authentication.guard.ts
Normal file
18
frontend/src/app/core/guard/authentication.guard.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { inject } from "@angular/core";
|
||||
import { CanActivateFn, Router } from "@angular/router";
|
||||
import { AuthenticationService } from "../service/authentication.service";
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
|
||||
export const authenticationGuard: CanActivateFn = () => {
|
||||
const authenticationService = inject(AuthenticationService);
|
||||
const router = inject(Router);
|
||||
const snackBar = inject(MatSnackBar);
|
||||
|
||||
if (authenticationService.isAuthenticated()) {
|
||||
return true;
|
||||
} else {
|
||||
router.navigate(['/login']);
|
||||
snackBar.open('You are unauthenticated. Please, log-in first.', 'Close', { duration: 5000 });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,88 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
|
||||
import { catchError, filter, Observable, Subject, switchMap, take, throwError } from 'rxjs';
|
||||
import { RefreshTokenRequest } from '../rest-services/user/model/refresh-token.model';
|
||||
import { UserRestService } from '../rest-services/user/user.rest-service';
|
||||
import { AuthenticationService } from '../service/authentication.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
@Injectable()
|
||||
export class JwtInterceptor implements HttpInterceptor {
|
||||
private readonly authenticationService = inject(AuthenticationService);
|
||||
private readonly router = inject(Router);
|
||||
private readonly userRestService = inject(UserRestService);
|
||||
private readonly snackBar = inject(MatSnackBar);
|
||||
private isRefreshingToken = false;
|
||||
private refreshTokenSubject = new Subject<string | undefined>();
|
||||
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
if (this.authenticationService.isTokenExpired()) {
|
||||
this.authenticationService.renewToken();
|
||||
}
|
||||
let requestWithAuthentication = request;
|
||||
|
||||
const jwt = this.authenticationService.getToken();
|
||||
|
||||
if (jwt) {
|
||||
const cloned = request.clone({
|
||||
headers: request.headers.set('Authorization', `Bearer ${jwt}`)
|
||||
});
|
||||
|
||||
return next.handle(cloned);
|
||||
} else {
|
||||
this.authenticationService.unauthenticate();
|
||||
requestWithAuthentication = this.addTokenInHeaders(request, jwt);
|
||||
}
|
||||
|
||||
return next.handle(request);
|
||||
return next.handle(requestWithAuthentication)
|
||||
.pipe(
|
||||
catchError(error => {
|
||||
if (error instanceof HttpErrorResponse && error.status === 401) {
|
||||
return this.handleAunauthorizedError(request, next, error);
|
||||
}
|
||||
|
||||
return throwError(() => error);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private handleAunauthorizedError(request: HttpRequest<any>, next: HttpHandler, initialError: any): Observable<HttpEvent<any>> {
|
||||
if (!this.isRefreshingToken) {
|
||||
this.isRefreshingToken = true;
|
||||
this.refreshTokenSubject.next(undefined);
|
||||
|
||||
const refreshToken = this.authenticationService.getRefreshToken();
|
||||
if (refreshToken) {
|
||||
const refreshTokenRequest: RefreshTokenRequest = {
|
||||
refreshTokenValue: refreshToken
|
||||
};
|
||||
this.userRestService.refreshToken(refreshTokenRequest)
|
||||
.then(refreshTokenResponse => {
|
||||
this.authenticationService.authenticate(refreshTokenResponse.accessToken, refreshTokenResponse.refreshToken);
|
||||
})
|
||||
.catch(() => {
|
||||
return this.handleNoRefreshToken(initialError);
|
||||
})
|
||||
.finally(() => this.isRefreshingToken = false);
|
||||
} else {
|
||||
return this.handleNoRefreshToken(initialError);
|
||||
}
|
||||
}
|
||||
|
||||
return this.refreshTokenSubject.pipe(
|
||||
filter(token => !!token),
|
||||
take(1),
|
||||
switchMap(token => {
|
||||
let requestWithAuthentication = request;
|
||||
if (token) {
|
||||
requestWithAuthentication = this.addTokenInHeaders(request, token)
|
||||
}
|
||||
return next.handle(requestWithAuthentication);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private addTokenInHeaders(request: HttpRequest<any>, token: string): HttpRequest<any> {
|
||||
return request.clone({
|
||||
headers: request.headers.set('Authorization', `Bearer ${token}`)
|
||||
});
|
||||
}
|
||||
|
||||
private handleNoRefreshToken(initialError: any): Observable<HttpEvent<any>> {
|
||||
this.router.navigate(['/login']);
|
||||
this.refreshTokenSubject.next(undefined);
|
||||
this.authenticationService.unauthenticate();
|
||||
this.snackBar.open('You are unauthenticated. Please, re-authenticate before retrying your action.', 'Close', { duration: 5000 });
|
||||
return throwError(() => initialError);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ interface UserDetails {
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthenticationService {
|
||||
private userRestService = inject(UserRestService);
|
||||
|
||||
authenticate(token: string, refreshToken: string): void {
|
||||
localStorage.setItem(JWT_PARAM, token);
|
||||
@@ -50,6 +49,10 @@ export class AuthenticationService {
|
||||
return localStorage.getItem(JWT_PARAM) ?? undefined;
|
||||
}
|
||||
|
||||
getRefreshToken(): string | undefined {
|
||||
return localStorage.getItem(REFRESH_TOKEN_PARAM) ?? undefined;
|
||||
}
|
||||
|
||||
isTokenExpired(): boolean {
|
||||
let result = false;
|
||||
|
||||
@@ -65,19 +68,6 @@ export class AuthenticationService {
|
||||
return result;
|
||||
}
|
||||
|
||||
renewToken(): void {
|
||||
const refreshToken = localStorage.getItem(REFRESH_TOKEN_PARAM);
|
||||
if (refreshToken) {
|
||||
const request: RefreshTokenRequest = {
|
||||
refreshTokenValue: refreshToken
|
||||
};
|
||||
this.userRestService.refreshToken(request)
|
||||
.then(refreshTokenResponse => {
|
||||
this.authenticate(refreshTokenResponse.accessToken, refreshTokenResponse.refreshToken);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private extractUserFromLocalStorage(): User | undefined {
|
||||
let result: User | undefined = undefined;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user