angular-authentication icon indicating copy to clipboard operation
angular-authentication copied to clipboard

refresh access token

Open bensaadmohamed opened this issue 10 months ago • 4 comments

Description

Motivation

Suggested Implementation

Alternate Implementations

bensaadmohamed avatar Apr 05 '25 21:04 bensaadmohamed

hello and thanks for this repository i see that you didnt implement access token refresh if its expired or the server return a 401 error before page refresh?,or i am wrong!

bensaadmohamed avatar Apr 05 '25 21:04 bensaadmohamed

Hi! Thanks a lot for checking out the repository and for your thoughtful comment.

You're absolutely right — token refresh on expiry or 401 responses isn't currently implemented. It's definitely something I’ve considered as a future enhancement.

That said, I’d really welcome a PR if you’re up for contributing! Thanks again! 🙌

nikosanif avatar Apr 07 '25 06:04 nikosanif

hello and thanks for your replay here is an implementation of an error interceptor that handle 401 server response

export const errorInterceptor: HttpInterceptorFn = (
  req: HttpRequest<unknown>,
  next: HttpHandlerFn
): Observable<HttpEvent<any>> => {

  const router = inject(Router);
  const authService = inject(AuthService);
  const authFacade = inject(AUTH_FACADE);
  const store = inject(Store);
  let isRefreshing = false;

  return next(req).pipe(
    retry({ count: maxRetries, delay: (error) => shouldRetry(error) }),
    catchError((error) => {

      if (error instanceof HttpErrorResponse) {
        if (error.error instanceof ErrorEvent) {
          console.error(`Client-side error occurred : ${error.error.message}`);
        } else {
          if (error instanceof HttpErrorResponse &&
            !(req.url.includes('sign-in') || req.url.includes('refresh') || req.url.includes('logout')) &&
            error.status === 401) {
            if (!isRefreshing) {
              isRefreshing = true;
              if (store.select(AuthSelectors.isLoggedIn)) {
                store.dispatch(new RefreshToken());
                return store.select(AuthSelectors.auth).pipe(
                  filter(
                    auth =>
                      auth.refreshTokenStatus === TokenStatus.INVALID ||
                      (auth.refreshTokenStatus === TokenStatus.VALID && !!auth.user)
                  ),
                  take(1)
                  ,switchMap(() => {
                    isRefreshing = false;

                    return next(req);
                  }),
                  catchError((error) => {
                    isRefreshing = false;
                    if (error.status == '403') {
                      router.navigateByUrl("error/access-denied");
                    }

                    return throwError(() => error);
                  })
                );


              }

            }
          }
        }
      } else {
        console.error('An unexpected error has occurred!.');
      }
      throw error;
    })
  )
}

function shouldRetry(error: HttpHeaderResponse): ObservableInput<any> {
  if (error.status === 500) {
    return timer(delayMs);
  }
  return timer(0);
}

bensaadmohamed avatar Apr 07 '25 21:04 bensaadmohamed

Thanks @bensaadmohamed for your example! You can raise a PR following the Contributor Guide. If you need any further help, feel free to ask me.

nikosanif avatar Apr 28 '25 05:04 nikosanif