-
-
Save umutyerebakmaz/43f0555aa1865f4e7753fcec16650ff1 to your computer and use it in GitHub Desktop.
import { Injectable } from '@angular/core'; | |
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'; | |
import { Observable, BehaviorSubject } from 'rxjs'; | |
import { catchError, filter, switchMap, take } from 'rxjs/operators'; | |
import { AuthService } from '@services/auth.service'; | |
import { Router } from '@angular/router'; | |
import { TokenType } from '@app/graphql.module'; | |
@Injectable() | |
export class AuthInterceptor implements HttpInterceptor { | |
private isRefreshing = false; | |
private newAccessTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null); | |
constructor(private authService: AuthService, private router: Router) {} | |
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |
const accessToken = this.authService.getAccessToken(); | |
if (accessToken === null) { | |
request = request.clone({ | |
setHeaders: { | |
Accept: 'charset=utf-8', | |
}, | |
}); | |
} | |
if (accessToken) { | |
request = this.setRequest(request, accessToken, 'access'); | |
} | |
return next.handle(request).pipe( | |
catchError((error: HttpErrorResponse) => { | |
const e = error.error.errors[0]; | |
if (e.message === 'expired token') { | |
if (!this.isRefreshing) { | |
this.isRefreshing = true; | |
this.newAccessTokenSubject.next(null); | |
const refreshToken = this.authService.getRefreshToken(); | |
return this.authService.refreshAccessToken(refreshToken).pipe( | |
switchMap(res => { | |
this.isRefreshing = false; | |
this.newAccessTokenSubject.next(res.accessToken); | |
const clonedRequest = this.setRequest(request, res?.accessToken, 'refresh'); | |
console.log(clonedRequest); | |
return next.handle(clonedRequest).pipe(); | |
}), | |
catchError(refreshError => { | |
this.isRefreshing = false; | |
this.authService.removeTokens(); | |
this.router.navigate(['login']); | |
throw new Error(refreshError); | |
}) | |
); | |
} else { | |
return this.newAccessTokenSubject.pipe( | |
filter(token => token !== null), | |
take(1), | |
switchMap(subject => { | |
console.log(4, subject); | |
const cloneRequest2 = this.setRequest(request, subject.accessToken, 'access'); | |
return next.handle(cloneRequest2); | |
}) | |
); | |
} | |
} | |
throw new Error(error as any); | |
}) | |
); | |
} | |
private setRequest(request: HttpRequest<any>, token: string, tokenType: TokenType): HttpRequest<any> { | |
return request.clone({ | |
setHeaders: { | |
Accept: 'charset=utf-8', | |
Authorization: `Bearer ${token}`, | |
tokenType: tokenType, | |
}, | |
}); | |
} | |
} |
It looks like the issue is arising because of how you're handling the refreshed token and token type when cloning the request.
You mentioned that on line 43 you want to extend the duration of the access token by sending the refresh token and tokenType information to the server. But in the current logic, you're using the new access token (res?.accessToken) and setting the tokenType to 'refresh' which contradicts your statement. If you want to send the refresh token and 'refresh' tokenType, you should do so.
Can you test it?
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { catchError, filter, switchMap, take } from 'rxjs/operators';
import { AuthService } from '@services/auth.service';
import { Router } from '@angular/router';
import { TokenType } from '@app/graphql.module';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private isRefreshing = false;
private newAccessTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private authService: AuthService, private router: Router) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const accessToken = this.authService.getAccessToken();
if (accessToken === null) {
request = request.clone({
setHeaders: {
Accept: 'charset=utf-8',
},
});
}
if (accessToken) {
request = this.setRequest(request, accessToken, null, 'access');
}
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
const e = error.error.errors[0];
if (e.message === 'expired token') {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.newAccessTokenSubject.next(null);
const refreshToken = this.authService.getRefreshToken();
return this.authService.refreshAccessToken(refreshToken).pipe(
switchMap(res => {
this.isRefreshing = false;
this.newAccessTokenSubject.next(res.accessToken);
const clonedRequest = this.setRequest(request, res?.accessToken, refreshToken, 'refresh');
return next.handle(clonedRequest);
}),
catchError(refreshError => {
this.isRefreshing = false;
this.authService.removeTokens();
this.router.navigate(['login']);
throw new Error(refreshError);
})
);
} else {
return this.newAccessTokenSubject.pipe(
filter(token => token !== null),
take(1),
switchMap(subject => {
const cloneRequest2 = this.setRequest(request, subject, null, 'access');
return next.handle(cloneRequest2);
})
);
}
}
throw new Error(error as any);
})
);
}
private setRequest(request: HttpRequest<any>, accessToken: string, refreshToken: string, tokenType: TokenType): HttpRequest<any> {
const token = tokenType === 'access' ? accessToken : refreshToken;
return request.clone({
setHeaders: {
Accept: 'charset=utf-8',
Authorization: `Bearer ${token}`,
tokenType: tokenType,
},
});
}
}
@aliosmanyuksel Hello, I tested and the result did not change. By default, the interceptor goes as access token and tokenType = 'access' in the header, and if this request gives an error as expired token from the server, it should treat the refreshToken endpoint as refresh token and tokenType = 'refresh'. In the current situation, the mutable is behaving correctly or there is something wrong with my setup.
In this line, I want you to extend the duration of the access token by sending the refresh token and tokenType information to the server, but every time I try, the request is sent with an access token and an access token. Can anyone help me with what I missed?