简体   繁体   中英

Angular - Waiting for refresh token function to finish before forwarding the request

I'm working on an auth system in angular with a django backend with jwt. I have an angular interceptor which checks within every request if the access token is still valid and if not, it calls a refreshtoken function and refreshes the access token.

Here is the code of the interceptor:

constructor(private authService:AuthService) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    let access_token = localStorage.getItem("access_token");
    
    if(access_token){
      // Check if access token is no longer valid, if so, refresh it with the refresh token
      if(this.authService.isLoggedOut()){
        
        this.authService.refreshToken();
        access_token = localStorage.getItem("access_token");
      }
      const cloned = request.clone({
        headers: request.headers.set("Authorization", "Bearer " + access_token)
      });
      return next.handle(cloned);
    } 
    else{
      return next.handle(request);
    }

It gets the access_token and checks the validity with the help of the auth service. My problem is that if its unvalid its calls the refreshToken() function which looks like this,

  refreshToken(){
    let refresh_token = localStorage.getItem("refresh_token");
    console.log("BEFORE REFRESH: " + localStorage.getItem("access_token"));
 
    return this.http.post(`${apiUrl}token/refresh/`, {"refresh" : refresh_token}).subscribe(res => {
      let access_token = res["access"]
      const expiresAt = this.tokenExpiresAt(access_token);
      localStorage.setItem("access_token", access_token);
      localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()));

      console.log("AFTER REFRESH: " + localStorage.getItem("access_token"));
    });
  }

but it doesn't wait for the refresh token function to finish and returns the handle. So the first request with an invalid token throws an error and only the next requests are fine.

How can I revise this to wait for refreshToken() to finish?

Thanks for your time!

you can use rxjs operators such as switchMap

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    let access_token = localStorage.getItem("access_token");
    if (access_token) {
        // Check if access token is no longer valid, if so, refresh it with the refresh token
        if (this.authService.isLoggedOut()) {
            return this.authService.refreshToken().pipe(
                switchMap(token => {
                    const cloned = request.clone({
                        headers: request.headers.set("Authorization", "Bearer " + access_token)
                    });
                    return next.handle(cloned);
                })
            )
        }
    } else {
        return next.handle(request);
    }
}

and in refreshToken function - make it return Observsble and set the localStorage inside pipe

refreshToken(){
let refresh_token = localStorage.getItem("refresh_token");
console.log("BEFORE REFRESH: " + localStorage.getItem("access_token"));

return this.http.post(`${apiUrl}token/refresh/`, {"refresh" : refresh_token}).pipe(tap(res => {
  let access_token = res["access"]
  const expiresAt = this.tokenExpiresAt(access_token);
  localStorage.setItem("access_token", access_token);
  localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()));

  console.log("AFTER REFRESH: " + localStorage.getItem("access_token"));
});
 )
 }

I combined the answers from multiple questions and finally came up with this solution:

Interceptor:

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    let access_token = localStorage.getItem("access_token");
  
    if (access_token) {
      // Check if access token is no longer valid, if so, refresh it with the refresh token
      if (this.authService.isLoggedOut()) {
        return from(this.authService.refreshToken()).pipe(
          mergeMap((access_token) => {
            localStorage.setItem("access_token", access_token);
            const cloned = request.clone({
              headers: request.headers.set("Authorization", "Bearer " + access_token),
            });
            return next.handle(cloned);
          })
        );
      }
      const cloned = request.clone({
        headers: request.headers.set("Authorization", "Bearer " + access_token),
      });
      return next.handle(cloned);
    } else {
      return next.handle(request);
    }
  }

refreshToken:

async refreshToken(): Promise<string> {
    let refresh_token = localStorage.getItem("refresh_token"); 
    const res$ = this.http
    .post(`${apiUrl}token/refresh/`, { refresh: refresh_token })
    .pipe(map((res) => res["access"]))
    .pipe(first());
    const res = await lastValueFrom(res$);

    const expiresAt = this.tokenExpiresAt(res);
    localStorage.setItem("access_token", res);
    localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()));
    console.log("Refreshed Access Token");
    return res;
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM