The switch in 3.0 to HttpClient breaks exisiting interceptors which neeeds the OAuthService and HttpClient. (Circular dependencies). This interceptors are used to automate the header settings to authenticate request.
But I did not find a configuration / helper to let the OAuthService to intercept the HTTPClient itself.
The current documentation / examples says that we have to add the headers on each single call.
And I did not find an Interceptor in angular-oath2-oidc (or I missed a point).
I was also having issues using an interceptor with the OAuthService. For example, in my interceptor, if I receive a 401 status, I want to call "initImplicitFlow()". However, injecting the OatuhService into the Interceptor creates a circular dependency.
I was able to resolve this by using the router to redirect to a LoginComponent. The LoginComponent then injected the OAuthService and called initImplicitFlow().
Not sure if any of this helps you.
This is a known issue - see https://github.com/angular/angular/issues/18224
A workaround can be:
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
private authService: AuthService;
// Would like to inject authService directly but it causes a cyclic dependency error
// see https://github.com/angular/angular/issues/18224
constructor(private injector: Injector) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone({
setHeaders: {
Authorization: this.getAuthService().authorizationHeader
}
});
return next.handle(request);
}
getAuthService(): AuthService {
if (typeof this.authService === 'undefined') {
this.authService = this.injector.get(AuthService);
}
return this.authService;
}
}
request = request.clone({
setHeaders: {
Authorization: this.getAuthService().authorizationHeader
}
});
should be:
request = request.clone({
setHeaders: {
Authorization: this.getAuthService().authorizationHeader()
}
});
as .authorizationHeader is a method. Works otherwise, thanks.
Thx for this question and those answers.
Another solution I like to prevent the circular dependency is using the OAuthStore instead of the whole service:
import { Injectable, Inject } from '@angular/core';
import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse, HttpErrorResponse } from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import { BASE_URL } from '../../crud-helper/base-url.token';
import { map, catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
import { Router } from '@angular/router';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(
private authStorage: OAuthStorage,
@Inject(BASE_URL) private baseUrl: string,
private router: Router) {
}
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let url = req.url.toLowerCase();
if (url.startsWith(this.baseUrl)) {
let token = this.authStorage.getItem('access_token');
let header = 'Bearer ' + token;
let headers = req.headers
.set('Authorization', header);
req = req.clone({ headers });
}
return next.handle(req).pipe(
map(event => event),
catchError(err => this.handleError(err))
)
}
handleError(error: HttpErrorResponse) {
console.error('error intercepted', error);
if (error.status === 401 || error.status === 403) {
this.router.navigate(['/home', {needsLogin: true}]);
return of(null);
}
return of(error);
}
}
To make this work, we currently need to explicitly register an OAuthStorage, even though when we still want to stick with the default implementation used which is the Browser's sessionStorage:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: OAuthStorage, useValue: sessionStorage }
],
In future versions this won't be necessary anymore.
Most helpful comment
This is a known issue - see https://github.com/angular/angular/issues/18224
A workaround can be: