@@ -9,9 +9,15 @@ import { BehaviorSubject, throwError } from 'rxjs';
99import { catchError , filter , switchMap , take } from 'rxjs/operators' ;
1010import { inject } from '@angular/core' ;
1111import { Router } from '@angular/router' ;
12- import { AuthenticationService } from '~features/authentication/services/authentication.service' ;
12+ import {
13+ ACCESS_TOKEN_KEY ,
14+ AuthenticationService ,
15+ } from '~features/authentication/services/authentication.service' ;
1316import { AppError } from '~core/enums/app-error.enum' ;
1417import { AUTH_URLS } from '~core/constants/urls.constants' ;
18+ import { LOCAL_STORAGE } from '~core/providers/local-storage' ;
19+ import { translations } from '../../../locale/translations' ;
20+ import { AlertService } from '~core/services/alert.service' ;
1521
1622const isRefreshing = new BehaviorSubject < boolean > ( false ) ;
1723
@@ -20,24 +26,74 @@ export function authenticationInterceptor(
2026 next : HttpHandlerFn ,
2127) : Observable < HttpEvent < unknown > > {
2228 const authenticationService = inject ( AuthenticationService ) ;
29+ const alertService = inject ( AlertService ) ;
30+ const storageService = inject ( LOCAL_STORAGE ) ;
2331 const router = inject ( Router ) ;
24- return next ( request ) . pipe (
25- catchError ( ( errorResponse : HttpErrorResponse ) => {
26- if ( isAccessTokenError ( errorResponse ) ) {
27- return tryRefreshToken ( request , next , authenticationService ) ;
28- }
29-
30- if ( isRefreshTokenError ( errorResponse ) ) {
31- authenticationService . logOut ( ) ;
32- void router . navigate ( [ AUTH_URLS . logIn ] ) ;
33- return throwError ( ( ) => new Error ( 'Session expired. Please log in again.' ) ) ;
34- }
35-
36- return throwError ( ( ) => errorResponse ) ;
37- } ) ,
32+
33+ const clonedRequest = attachAccessToken ( request , storageService ) ;
34+ return handleRequest ( {
35+ request : clonedRequest ,
36+ next,
37+ authenticationService,
38+ alertService,
39+ storageService,
40+ router,
41+ } ) ;
42+ }
43+
44+ function attachAccessToken (
45+ request : HttpRequest < unknown > ,
46+ storageService : Storage | null ,
47+ ) : HttpRequest < unknown > {
48+ const accessToken = storageService ?. getItem ( ACCESS_TOKEN_KEY ) ;
49+ if ( accessToken ) {
50+ return request . clone ( {
51+ setHeaders : { Authorization : `Bearer ${ accessToken } ` } ,
52+ } ) ;
53+ }
54+ return request ;
55+ }
56+
57+ function handleRequest ( parameters : {
58+ request : HttpRequest < unknown > ;
59+ next : HttpHandlerFn ;
60+ authenticationService : AuthenticationService ;
61+ alertService : AlertService ;
62+ storageService : Storage | null ;
63+ router : Router ;
64+ } ) : Observable < HttpEvent < unknown > > {
65+ return parameters . next ( parameters . request ) . pipe (
66+ catchError ( ( errorResponse : HttpErrorResponse ) =>
67+ handleErrors ( {
68+ errorResponse,
69+ ...parameters ,
70+ } ) ,
71+ ) ,
3872 ) ;
3973}
4074
75+ function handleErrors ( parameters : {
76+ request : HttpRequest < unknown > ;
77+ next : HttpHandlerFn ;
78+ authenticationService : AuthenticationService ;
79+ alertService : AlertService ;
80+ storageService : Storage | null ;
81+ router : Router ;
82+ errorResponse : HttpErrorResponse ;
83+ } ) : Observable < HttpEvent < unknown > > {
84+ if ( isAccessTokenError ( parameters . errorResponse ) ) {
85+ return tryRefreshToken ( parameters ) ;
86+ }
87+
88+ if ( isRefreshTokenError ( parameters . errorResponse ) ) {
89+ parameters . authenticationService . logOut ( ) ;
90+ void parameters . router . navigate ( [ AUTH_URLS . logIn ] ) ;
91+ return throwError ( ( ) => new Error ( 'Session expired. Please log in again.' ) ) ;
92+ }
93+
94+ return throwError ( ( ) => parameters . errorResponse ) ;
95+ }
96+
4197function isAccessTokenError ( errorResponse : HttpErrorResponse ) : boolean {
4298 return (
4399 errorResponse . status === 401 &&
@@ -56,30 +112,76 @@ function isRefreshTokenError(errorResponse: HttpErrorResponse): boolean {
56112 ) ;
57113}
58114
59- // eslint-disable-next-line @typescript-eslint/max-params
60- function tryRefreshToken (
61- request : HttpRequest < unknown > ,
62- next : HttpHandlerFn ,
63- authenticationService : AuthenticationService ,
64- ) : Observable < HttpEvent < unknown > > {
115+ function tryRefreshToken ( parameters : {
116+ request : HttpRequest < unknown > ;
117+ next : HttpHandlerFn ;
118+ authenticationService : AuthenticationService ;
119+ alertService : AlertService ;
120+ storageService : Storage | null ;
121+ router : Router ;
122+ } ) : Observable < HttpEvent < unknown > > {
65123 if ( ! isRefreshing . getValue ( ) ) {
66- isRefreshing . next ( true ) ;
67-
68- return authenticationService . refreshToken ( ) . pipe (
69- switchMap ( ( ) => {
70- isRefreshing . next ( false ) ;
71- return next ( request . clone ( { withCredentials : true } ) ) ;
72- } ) ,
73- catchError ( ( error : HttpErrorResponse ) => {
74- isRefreshing . next ( false ) ;
75- return throwError ( ( ) => error ) ;
76- } ) ,
77- ) ;
124+ return handleTokenRefresh ( parameters ) ;
78125 }
79126
127+ return waitForTokenRefresh ( parameters ) ;
128+ }
129+
130+ function handleTokenRefresh ( parameters : {
131+ request : HttpRequest < unknown > ;
132+ next : HttpHandlerFn ;
133+ authenticationService : AuthenticationService ;
134+ alertService : AlertService ;
135+ storageService : Storage | null ;
136+ router : Router ;
137+ } ) : Observable < HttpEvent < unknown > > {
138+ isRefreshing . next ( true ) ;
139+
140+ return parameters . authenticationService . refreshToken ( ) . pipe (
141+ switchMap ( ( ) => {
142+ isRefreshing . next ( false ) ;
143+ return retryRequestWithRefreshedToken ( parameters ) ;
144+ } ) ,
145+ catchError ( ( error : HttpErrorResponse ) => {
146+ isRefreshing . next ( false ) ;
147+ handleRefreshError ( parameters ) ;
148+ return throwError ( ( ) => error ) ;
149+ } ) ,
150+ ) ;
151+ }
152+
153+ function waitForTokenRefresh ( parameters : {
154+ request : HttpRequest < unknown > ;
155+ next : HttpHandlerFn ;
156+ storageService : Storage | null ;
157+ } ) : Observable < HttpEvent < unknown > > {
80158 return isRefreshing . pipe (
81159 filter ( ( refreshing ) => ! refreshing ) ,
82160 take ( 1 ) ,
83- switchMap ( ( ) => next ( request . clone ( { withCredentials : true } ) ) ) ,
161+ switchMap ( ( ) => retryRequestWithRefreshedToken ( parameters ) ) ,
84162 ) ;
85163}
164+
165+ function retryRequestWithRefreshedToken ( parameters : {
166+ request : HttpRequest < unknown > ;
167+ next : HttpHandlerFn ;
168+ storageService : Storage | null ;
169+ } ) : Observable < HttpEvent < unknown > > {
170+ const refreshedToken = parameters . storageService ?. getItem ( ACCESS_TOKEN_KEY ) ;
171+ const clonedRequest = refreshedToken
172+ ? parameters . request . clone ( {
173+ setHeaders : { Authorization : `Bearer ${ refreshedToken } ` } ,
174+ } )
175+ : parameters . request ;
176+ return parameters . next ( clonedRequest ) ;
177+ }
178+
179+ function handleRefreshError ( parameters : {
180+ authenticationService : AuthenticationService ;
181+ alertService : AlertService ;
182+ router : Router ;
183+ } ) : void {
184+ parameters . authenticationService . logOut ( ) ;
185+ parameters . alertService . createErrorAlert ( translations . sessionExpired ) ;
186+ void parameters . router . navigate ( [ AUTH_URLS . logIn ] ) ;
187+ }
0 commit comments