Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I redirect to login page on 401 response in Angular 5?

I'm working on a Angular 5 application using OAuth2 implicit flow.

I have services that perform HTTP calls, following one example of my services:

@Injectable()
export class MyService {

  constructor(public http: HttpClient) { }

  public getAll(): Observable<Persona[]> {
    return this.http.get<Persona[]>("http://mywebservice/persone");
  }
}

I'm using interceptors for authorization and add custom attributes. Following my auth interceptor:

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from "rxjs";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor() {

  }
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let accessToken = sessionStorage.getItem("access_token");
    if(accessToken)
    {
        request = request.clone({
        setHeaders: {
            Authorization: `Bearer ${accessToken}`
        }
        });
    }

    return next.handle(request);
  }
}

And following how I consume my services:

public myMethod() {
    this.myService.getAll().subscribe(
        result => {
            console.log(result);
        }, error => {
            // I don't want add redirection there...
            console.error(error);
        });
}

Now my need is that when any HTTP call receive 401 result, the application redirects the user to login page.

How can I obtain this result without code duplication?

Thank you a lot

like image 649
ilMattion Avatar asked Jun 25 '18 08:06

ilMattion


4 Answers

I resolved my problem changing my interceptor like following:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor() {

  }
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let accessToken = sessionStorage.getItem("access_token");
    if(accessToken)
    {
        request = request.clone({
        setHeaders: {
            Authorization: `Bearer ${accessToken}`
        }
        });
    }

    return next.handle(request).do((event: HttpEvent<any>) => {
      if (event instanceof HttpResponse) {
      }
    }, (err: any) => {
      if (err instanceof HttpErrorResponse) {
        if (err.status === 401) {
            this.router.navigate(['login']);
        }
      }
    });
  }
}

I found the solution there: https://medium.com/@ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8

like image 131
ilMattion Avatar answered Nov 09 '22 13:11

ilMattion


Only to support new readers, notice that in angular 7 you should use pipe() instead of do() or catch():

return next.handle(request).pipe(catchError(err => {
    if (err.status === 401) {
        MyComponent.logout();
    }
    const error = err.error.message || err.statusText;
        return throwError(error);
}));
like image 39
Jalaleddin Hosseini Avatar answered Nov 09 '22 12:11

Jalaleddin Hosseini


Here is for Angular 11

import {
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
} from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable, throwError } from "rxjs";
import { catchError, map } from "rxjs/operators";
import HttpStatusCode from "src/app/models/enums/HttpStatusCode";
import { AuthenticationService } from "./authentication.service";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authenticationService: AuthenticationService) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      map((event: HttpEvent<any>) => {
        return event;
      }),
      catchError(
        (
          httpErrorResponse: HttpErrorResponse,
          _: Observable<HttpEvent<any>>
        ) => {
          if (httpErrorResponse.status === HttpStatusCode.UNAUTHORIZED) {
            this.authenticationService.redirectToLogin();
          }
          return throwError(httpErrorResponse);
        }
      )
    );
  }
}

The following method declared in AuthenticationService

public redirectToLogin(path: string, queryParams: any) {
    this.router.navigate([path], {
        queryParams,
        queryParamsHandling: "merge",
    });
}
like image 3
Teoman shipahi Avatar answered Nov 09 '22 12:11

Teoman shipahi


Attach the error handling to the common request handler:

return next.handle(request).catch(err => {
    if (err.status === 401) {
         // Redirect here
    }
}

You can import the router directly in the interceptor, but the proper way to do it is to create an authentication service or similar that imports the router and call it to do the redirection

like image 1
Siro Avatar answered Nov 09 '22 13:11

Siro