Hi all. I am here looking for some guidance regarding Keycloak. Currently my frontend uses Keycloak to authenticate users. Once user is logged in, the JWT token will be returned by Keycloak. I want to bind this JWT token as the Bearer Token to the Authorization Header when I am making API call, so that my backend can receive the JWT token and determine the authenticity of the API calls.
However, I followed the Keycloak documentation but I failed to bind the Bearer Token to my API calls. It only currently binds to the first API call, and the subsequent API calls do not contain the Bearer Token. You can see in the screenshots below. Only the first API call succeeds with Bearer Token attached, and my subsequent API calls fail due to the lack of Bearer Token.
I am using Angular v19 and Keycloak Angular v19 as well. So, KeycloakService is deprecated. Below is my code setup.
keycloak.config.ts
import {
AutoRefreshTokenService,
createInterceptorCondition,
INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG,
IncludeBearerTokenCondition,
provideKeycloak,
UserActivityService,
withAutoRefreshToken,
} from 'keycloak-angular';
import { environment } from '../../../environments/environment';
const urlCondition = createInterceptorCondition<IncludeBearerTokenCondition>({
urlPattern: /^(.*)?$/i, //change according to your backend url
});
export const provideKeycloakAngular = () =>
provideKeycloak({
config: environment.keycloak,
initOptions: {
onLoad: 'login-required',
checkLoginIframe: false,
pkceMethod: 'S256',
},
features: [
withAutoRefreshToken({
onInactivityTimeout: 'logout',
sessionTimeout: 3600000,
}),
],
providers: [
AutoRefreshTokenService,
UserActivityService,
{
provide: INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG,
useValue: [urlCondition],
},
],
});
app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideKeycloakAngular(),
provideHttpClient(
withInterceptors([includeBearerTokenInterceptor]),
withInterceptorsFromDi()
),
{
provide: HTTP_INTERCEPTORS,
useClass: HttpRequestInterceptor,
multi: true,
},
]
}
I am using a custom HTTP Interceptor too.
import {
HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpResponse,
HttpStatusCode,
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import {
catchError,
finalize,
map,
Observable,
retry,
tap,
throwError,
timer,
} from 'rxjs';
import { RETRY_EXPO_CONFIG } from '../../shared/consts/ips-constants';
import { AppRoute } from '../../shared/enums/app-route.enum';
import { AppBaseRepository } from '../repository/app-base.repository';
@Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private appBaseRepository: AppBaseRepository
) {}
intercept(
req: HttpRequest<any>,
handler: HttpHandler
): Observable<HttpEvent<any>> {
return handler.handle(req).pipe(
tap(() => {
if (!this.appBaseRepository.intermittentViewConfigurations)
this.appBaseRepository.toggleSpinner(true);
}),
retry({
count: RETRY_EXPO_CONFIG.ATTEMPTS,
delay: (err, retryCount) => {
switch (err.status) {
case 400:
case 408:
case 500:
case 502:
case 503:
case 504:
return timer(
RETRY_EXPO_CONFIG.TIME_INITIAL * (retryCount * retryCount)
);
default:
return throwError(() => err);
}
},
}),
map((e: HttpEvent<HttpResponse<any>>) => {
if (e instanceof HttpResponse) {
this.appBaseRepository.triggerToast(null, HttpStatusCode.Ok);
}
return e;
}),
catchError((e: HttpErrorResponse) => {
this.errorHandler(e);
return throwError(() => e);
}),
finalize(() => {
this.appBaseRepository.updateIntermittentViewConfigurations(null);
this.appBaseRepository.toggleSpinner(false);
})
);
}
private errorHandler = (error: HttpErrorResponse): void => {
switch (error.status) {
case 404:
this.router.navigate([AppRoute.NotFound]);
break;
case 403:
this.router.navigate([AppRoute.RestrictedAccess]);
break;
case 500:
this.router.navigate([AppRoute.Error]);
break;
default:
}
this.appBaseRepository.triggerToast(null, HttpStatusCode.BadRequest);
};
}
Hope to get some help here. Thanks in advance.