Learning About HTTP interceptor with Angular

Posted By : Puneet Sharma | 17-Nov-2018
One of the very good new features in the angular 4.3 is the Httpinterceptor. SO We can add header information to call all new HTTP clients, control responses, capture errors, etc. This fun new material is available for import at '@angular/common/http'. This post is not in an intensive study of how to do an HttpInterceptor. Instead, it will include a way to update the OAuth authentication token using the refresh token in HttpInterceptor.
First of all, an explanation of what is happening with OAuth and Refresh Tokens. When you login, you receive an authorization token and refresh token. The refresh token is configured with a fixed timeout. So when that timeout arrives (despite the activity) you get a 401 (unauthorized) error with your API call. At that point, your code should try to refresh the token by calling OAuth Refresh Token Endpoint (with the refresh token string). This gives you a new authorization token and a new refresh token. Since then, you use the new authorization token for calling your API.
The HttpInterceptor is perfect for this requirement. All this you can add authorization headers to the http call and hold exceptions to hear 401 errors. When a 401 error occurs, it can call refresh token and can retry with the new authorization token.
export class testComponent {
  message: string;
 
  constructor(private httpService: httpService,
              private authService: AuthService) { }
 
  test401() {
    this.httpService.getData()
      .subscribe((response: any) => {
        this.message = `status = ${response.status}`;
      },
      error => this.message = `status = ${error.status}`);
  }
 
  testMultiple() {
    let dataObservable = this.httpService.getData();
    let lookupObservable = this.httpService.getLookup();
 
    Observable.forkJoin(dataObservable, lookupObservable)
      .subscribe(([dataResult, lookupResult]) => {
        this.message = `status = ${dataResult.status} and getLookup status = ${lookupResult.status}`;
      },
      error => this.message = `status = ${error.status}`);
  }
}
Authentication Service
Authentication Service will give us a token. Here's my example code:
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/delay';
 
export class AuthService {
    public authToken: string = 'stale_auth_token';
    public authTokens: string = 'new_auth_token';
    public currentTokens: string;
 
    constructor() {
        this.currentTokens = this.authToken;
    }
 
    getAuthenticationToken() {
        return this.currentTokens;
    }
 
    refreshTokens(): Observable {
 
        this.currentTokens = this.authTokens;
 
        return Observable.of(this.authTokens).delay(200);
    }
}
HttpInterceptor
The Request Interceptor Service will implement HttpInterceptor which is the only one method: intercept. It will add a token to the header on each call and catch any errors that may occur.
@Injectable()
export class interceptorService implements HttpInterceptor {
 
    isNewToken: boolean = false;
    istokenSubject: BehaviorSubject = new BehaviorSubject(null);
 
    constructor(private authService: AuthService) {}
 
    addToken(request: HttpRequest, token: string): HttpRequest {
        return request.clone({ setHeaders: { Authorization: 'auth ' + token }})
    }
401 unauthorized handling
The code is the most important to handle 401 errors.
handle401Error(req: HttpRequest, next: HttpHandler) {
        if (!this.isRefreshingToken) {
            this.isRefreshingToken = true;
            this.istokenSubject.next(null);
 
            return this.authService.refreshTokens()
                .switchMap((newToken: string) => {
                    if (newToken) {
                        this.istokenSubject.next(newToken);
                        return next.handle(this.addToken(this.getNewRequests(req), newToken));
                    }
 
                    return this.logoutUsers();
                })
                .catch(error => {
                    return this.logoutUsers();
                })
                .finally(() => {
                    this.isRefreshingToken = false;
                });
        } else {
            return this.istokenSubject
                .filter(token => token != null)
                .take(1)
                .switchMap(token => {
                    return next.handle(this.addToken(this.getNewRequests(req), token));
                });
        }
    }
    getNewRequests(req: HttpRequest): HttpRequest {
        if (req.url.indexOf('getData') > 0) {
            return new HttpRequest('GET', 'http://private-4002d-testerrorresponses.apiary-mock.com/getData');
        }
 
        return new HttpRequest('GET', 'http://private-4002d-testerrorresponses.apiary-mock.com/getLookup');
    }
 
    logoutUsers() {
 
        return Observable.throw("");
    }
What will happen if the refresh token is out?
Something might be thinking at this point what will happen at the time of the refresh token. Generally, due to an API call, the timeout is configured due to it. Well, what happens is that you should see 400 error with the message 'invalid_grants'. Handle 400 error code is likely to cause the user to log out and direct to the login page. Here is the code that examines this situation:


    handleError400(err) {
        if (err && err.status === 400 && err.error && err.error.error === 'invalid_grant') {
            return this.logoutUsers();
        }

        return Observable.throw(error);
    }
The HttpInterceptor is a new feature in Angular 4.3 which is very powerful and useful in all applications. As you can see, it nicely handles "Refresh the token" behind the scenes so that the user, everything works smoothly.
 

About Author

Author Image
Puneet Sharma

Puneet is an experienced Lead UI Developer, having good knowledge of Angular Js, TypeScript, HTML5, CSS3, Javascript, Jquery, Photoshop.

Request for Proposal

Name is required

Comment is required

Sending message..