feat: create AuthInterceptor and integrate it; adjust AuthGuards accordingly
This commit is contained in:
parent
c58d5c026b
commit
cf4a994ad0
7 changed files with 98 additions and 48 deletions
|
|
@ -19,6 +19,7 @@ import { ToastrModule } from 'ngx-toastr';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { HttpErrorInterceptor } from './interceptors/http-interceptor';
|
import { HttpErrorInterceptor } from './interceptors/http-interceptor';
|
||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
import { AuthInterceptor } from './interceptors/auth-interceptor';
|
||||||
|
|
||||||
export function createTranslateLoader(http: HttpClient) {
|
export function createTranslateLoader(http: HttpClient) {
|
||||||
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
||||||
|
|
@ -68,5 +69,10 @@ export const appConfig: ApplicationConfig = {
|
||||||
useClass: HttpErrorInterceptor,
|
useClass: HttpErrorInterceptor,
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: AuthInterceptor,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { LanguageService } from '../../services/language.service';
|
import { LanguageService } from '../../services/language.service';
|
||||||
import { SidebarMobileComponent } from '../../shared/components/sidebar/sidebar-mobile/sidebar-mobile.component';
|
import { SidebarMobileComponent } from '../../shared/components/sidebar/sidebar-mobile/sidebar-mobile.component';
|
||||||
import { SidebarComponent } from '../../shared/components/sidebar/sidebar.component';
|
import { SidebarComponent } from '../../shared/components/sidebar/sidebar.component';
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,33 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { CanActivate, Router } from '@angular/router';
|
||||||
import { catchError, map, Observable, of } from 'rxjs';
|
import { map, Observable } from 'rxjs';
|
||||||
import { AuthService } from '../services/auth.service';
|
import { AuthService } from '../services/auth.service';
|
||||||
import { TokenService } from '../services/token.service';
|
import { TokenService } from '../services/token.service';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
|
||||||
import { ToastNotificationService } from '../services/toast-notification.service';
|
import { ToastNotificationService } from '../services/toast-notification.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AuthenticatedGuard {
|
export class AuthenticatedGuard implements CanActivate {
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private tokenService: TokenService,
|
private tokenService: TokenService,
|
||||||
private router: Router,
|
private toastNotificationService: ToastNotificationService,
|
||||||
private toastrService: ToastrService,
|
private router: Router
|
||||||
private toastNotificationService: ToastNotificationService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
|
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
const authToken = this.tokenService.getAuthToken();
|
if (
|
||||||
|
!this.tokenService.isTokenAvailable() ||
|
||||||
if (!authToken || !this.tokenService.isTokenExpired(authToken)) {
|
!this.tokenService.isUserIdAvailable()
|
||||||
|
) {
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/login']);
|
||||||
|
this.toastNotificationService.showSessionExpiredToast();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.authService.checkAuthUser().pipe(
|
return this.authService
|
||||||
map((isAuthenticated) => {
|
.checkAuthUser()
|
||||||
if (isAuthenticated) {
|
.pipe(map((isAuthenticated) => isAuthenticated));
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
this.toastNotificationService.showSessionExpiredMessage();
|
|
||||||
this.router.navigate(['/login']);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
catchError(() => {
|
|
||||||
this.toastNotificationService.showSessionExpiredMessage();
|
|
||||||
this.router.navigate(['/login']);
|
|
||||||
return of(false);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { CanActivate, Router } from '@angular/router';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, map } from 'rxjs';
|
||||||
import { map, catchError } from 'rxjs/operators';
|
|
||||||
import { AuthService } from '../services/auth.service';
|
import { AuthService } from '../services/auth.service';
|
||||||
import { TokenService } from '../services/token.service';
|
import { TokenService } from '../services/token.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class RedirectIfAuthenticatedGuard {
|
export class RedirectIfAuthenticatedGuard implements CanActivate {
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private tokenService: TokenService,
|
private tokenService: TokenService,
|
||||||
|
|
@ -16,22 +15,20 @@ export class RedirectIfAuthenticatedGuard {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
|
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
const authToken = this.tokenService.getAuthToken();
|
if (
|
||||||
|
!this.tokenService.isTokenAvailable() ||
|
||||||
if (!authToken || !this.tokenService.isTokenExpired(authToken)) {
|
!this.tokenService.isUserIdAvailable()
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.authService.checkAuthUser().pipe(
|
return this.authService.checkAuthUser().pipe(
|
||||||
map((isAuthenticated) => {
|
map((isAuthenticated) => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
|
this.router.navigate(['/summary']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}),
|
|
||||||
catchError(() => {
|
|
||||||
this.router.navigate(['/login']);
|
|
||||||
return of(false);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
src/app/interceptors/auth-interceptor.ts
Normal file
64
src/app/interceptors/auth-interceptor.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpInterceptor,
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpErrorResponse,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable, throwError } from 'rxjs';
|
||||||
|
import { catchError, filter } from 'rxjs/operators';
|
||||||
|
import { Router, NavigationStart } from '@angular/router';
|
||||||
|
import { TokenService } from '../services/token.service';
|
||||||
|
import { ToastNotificationService } from '../services/toast-notification.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
private isNavigatingToLogin = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private tokenService: TokenService,
|
||||||
|
private router: Router,
|
||||||
|
private toast: ToastNotificationService
|
||||||
|
) {
|
||||||
|
this.router.events
|
||||||
|
.pipe(
|
||||||
|
filter(
|
||||||
|
(event): event is NavigationStart => event instanceof NavigationStart
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe((event) => {
|
||||||
|
if (event.url === '/login') {
|
||||||
|
this.isNavigatingToLogin = true;
|
||||||
|
} else {
|
||||||
|
this.isNavigatingToLogin = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(
|
||||||
|
req: HttpRequest<any>,
|
||||||
|
next: HttpHandler
|
||||||
|
): Observable<HttpEvent<any>> {
|
||||||
|
const token = this.tokenService.getAuthToken();
|
||||||
|
let clonedReq = req;
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
clonedReq = req.clone({
|
||||||
|
setHeaders: {
|
||||||
|
Authorization: `Token ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return next.handle(clonedReq).pipe(
|
||||||
|
catchError((error: HttpErrorResponse) => {
|
||||||
|
if (error.status === 401 && !this.isNavigatingToLogin) {
|
||||||
|
this.toast.showSessionExpiredToast();
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
}
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ export class ToastNotificationService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
showSessionExpiredMessage(): void {
|
showSessionExpiredToast(): void {
|
||||||
this.createInfoToast(
|
this.createInfoToast(
|
||||||
'Your session has expired, please log in again.',
|
'Your session has expired, please log in again.',
|
||||||
'Session Expired'
|
'Session Expired'
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,11 @@ export class TokenService {
|
||||||
sessionStorage.removeItem(this.USER_ID_KEY);
|
sessionStorage.removeItem(this.USER_ID_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
isTokenExpired(token: string): boolean {
|
isTokenAvailable(): boolean {
|
||||||
try {
|
return !!this.getAuthToken();
|
||||||
const payload = JSON.parse(atob(token.split('.')[1]));
|
|
||||||
const expiry = payload.exp;
|
|
||||||
if (!expiry) return true;
|
|
||||||
return expiry * 1000 < Date.now();
|
|
||||||
} catch (e) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isUserIdAvailable(): boolean {
|
||||||
|
return !!this.getUserId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue