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
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
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);
}));
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",
});
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With