I have HTTP interceptor that intercepts http requests for api
First it check's is this api that requires authorization, after that checks for session variables to see is there token saved already and checks is it valid
If there is no token or token is expired, it should get new token and then carry on as usual
The rest of code gets executed before token is retrieved, and then it doesn't add required auth header. I don't know how to make it wait.
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
let authReq = request;
const token = this.checkUrl(authReq.url)
if (token){
authReq = request.clone({
headers: request.headers.set(this.TOKEN_HEADER_KEY, 'Bearer ' token)
});
}
return next.handle(authReq);
}
checkUrl
method that gets token and checks if auth is required
checkUrl(url: string): string {
if (url.includes('/login')){
return ''
}
for (let x of this.includedList) {
if (url.includes(x.url)){
return this.auth.getApiToken()
//break;
}
}
return ''
}
getApiToken
method that gets token from session storage or from login endpoint
getApiToken(): string{
let token = <string>sessionStorage.getItem('auth-token');
if (!token || this.jwt.isTokenExpired(token)){
this.apiLogin().subscribe({
next: data => {
console.log(data)
sessionStorage.setItem("auth-token", data.token)
token = data.token
},
error: err => {
console.log(err)
token = ''
}
})
}
return token;
}
And finally apiLogin
method that gets new token from api
apiLogin(): Observable<any> {
const loginData = {
username: environment.apiLoginData.username,
password: environment.apiLoginData.password
}
return this.http.post(AUTH_API, loginData);
}
EDIT
Solution I tried with Observable and map
interceptor
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
let authReq = request;
let token = ''
this.checkUrlV2(authReq.url).pipe(map(data => {
console.log(data)
token = data
}));
console.log('token')
console.log(token)
if (token){
authReq = request.clone({
headers: request.headers.set(this.TOKEN_HEADER_KEY, 'Bearer ' token)
});
}
return next.handle(authReq);
}
checkUrlV2
checkUrlV2(url: string): Observable<string> {
if (url.includes('/login')){
return of('')
}
for (let x of this.includedList) {
if (url.includes(x.url)){
this.auth.apiTokenV2().pipe(map(data => {
console.log(data)
return of(data)
}));
//break;
}
}
return of('')
}
apiTokenV2
apiTokenV2() {
let token = ''
if (!token || this.jwt.isTokenExpired(token)){
this.apiLogin().pipe(map(data => {
console.log(data)
sessionStorage.setItem("auth-token", data.token)
return of(data.token)
}));
}
return of(token)
}
CodePudding user response:
Found solution that works for my case. It does request without checking token, then refreshes it if it gets error
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = this.addAuthenticationToken(req);
return <Observable<HttpEvent<any>>>next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error && error.status === 401) {
if (this.refreshTokenInProgress) {
// If refreshTokenInProgress is true, we will wait until refreshTokenSubject has a non-null value which means the new token is ready and we can retry the request again
return this.refreshTokenSubject.pipe(
filter(result => result !== null),
take(1),
switchMap(() => next.handle(this.addAuthenticationToken(req)))
);
} else {
this.refreshTokenInProgress = true;
// Set the refreshTokenSubject to null so that subsequent API calls will wait until the new token has been retrieved
this.refreshTokenSubject.next(null);
return this.refreshAccessToken().pipe(
switchMap((success: boolean) => {
this.refreshTokenSubject.next(success);
return next.handle(this.addAuthenticationToken(req));
}),
// When the call to refreshToken completes we reset the refreshTokenInProgress to false for the next time the token needs to be refreshed
finalize(() => this.refreshTokenInProgress = false)
);
}
} else {
return throwError(() => new Error(error.message));
}
})
);
}
private refreshAccessToken(): Observable<any> {
return this.auth.apiLogin().pipe(map(authData => {
console.log('Token refreshed')
sessionStorage.setItem("auth-token", authData.token)
return authData.token
}))
}
private addAuthenticationToken(request: HttpRequest<any>): HttpRequest<any> {
const token = <string>sessionStorage.getItem('auth-token')
for (let x of this.excludedUrl){
if (request.url.includes(x)){
return request;
}
}
for (let x of this.includedList) {
if (request.url.includes(x.url)){
console.log('Bearer token added')
return request.clone({
headers: request.headers.set(this.TOKEN_HEADER_KEY, 'Bearer ' token)
});
}
}
return request;
}