Angular - Logout on 401 Unauthorized or 403 Forbidden HTTP Response with Interceptor
Tutorial built with Angular 14.2.12
This is a quick example of how to automatically logout of an Angular app if an HTTP request returns a 401 Unauthorized
or 403 Forbidden
response.
The below code snippets are from an Angular login tutorial I posted recently which auto logs out if a 401 or 403 error response is returned from the API. For the full tutorial including a live demo of the code see Angular 14 - User Registration and Login Example & Tutorial.
HTTP Error Interceptor with Logout on 401 or 403
The Error Interceptor intercepts all HTTP responses from the API to check if there were any errors. If there is a 401 Unauthorized
or 403 Forbidden
response the user is automatically logged out of the application, all other errors are re-thrown up to the calling service or component.
It's implemented using the Angular HttpInterceptor
interface included in the HttpClientModule
, by implementing the HttpInterceptor interface you can create a custom interceptor to catch all error responses from the API in a single location.
HTTP interceptors are added to the request pipeline in the providers
section of the app.module.ts file.
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AccountService } from '@app/_services';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private accountService: AccountService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(err => {
if ([401, 403].includes(err.status) && this.accountService.userValue) {
// auto logout if 401 or 403 response returned from api
this.accountService.logout();
}
const error = err.error?.message || err.statusText;
console.error(err);
return throwError(() => error);
}))
}
}
Account Service that sends HTTP Requests
The account service handles communication between the Angular app and the backend api for everything related to accounts. It contains methods for the login, logout and registration, as well as standard CRUD methods for retrieving, modifying and deleting user data.
I included it here to show examples of HTTP requests being sent from Angular to the API.
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '@environments/environment';
import { User } from '@app/_models';
@Injectable({ providedIn: 'root' })
export class AccountService {
private userSubject: BehaviorSubject<User | null>;
public user: Observable<User | null>;
constructor(
private router: Router,
private http: HttpClient
) {
this.userSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('user')!));
this.user = this.userSubject.asObservable();
}
public get userValue() {
return this.userSubject.value;
}
login(username: string, password: string) {
return this.http.post<User>(`${environment.apiUrl}/users/authenticate`, { username, password })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
this.userSubject.next(user);
return user;
}));
}
logout() {
// remove user from local storage and set current user to null
localStorage.removeItem('user');
this.userSubject.next(null);
this.router.navigate(['/account/login']);
}
register(user: User) {
return this.http.post(`${environment.apiUrl}/users/register`, user);
}
getAll() {
return this.http.get<User[]>(`${environment.apiUrl}/users`);
}
getById(id: string) {
return this.http.get<User>(`${environment.apiUrl}/users/${id}`);
}
update(id: string, params: any) {
return this.http.put(`${environment.apiUrl}/users/${id}`, params)
.pipe(map(x => {
// update stored user if the logged in user updated their own record
if (id == this.userValue?.id) {
// update local storage
const user = { ...this.userValue, ...params };
localStorage.setItem('user', JSON.stringify(user));
// publish updated user to subscribers
this.userSubject.next(user);
}
return x;
}));
}
delete(id: string) {
return this.http.delete(`${environment.apiUrl}/users/${id}`)
.pipe(map(x => {
// auto logout if the logged in user deleted their own record
if (id == this.userValue?.id) {
this.logout();
}
return x;
}));
}
}
App Module that adds HTTP Interceptor
The app module defines the root module of the Angular app along with metadata about the module.
The ErrorInterceptor
is configured as an HTTP interceptor for the app in the providers
section with these properties:
provide: HTTP_INTERCEPTORS
- an Angular injection token used by the DI provider that maps to the array of HTTP interceptors for the app.useClass: ErrorInterceptor
- the class to add to the HTTP_INTERCEPTORS array.multi: true
- required for HTTP_INTERCEPTORS, it configures the injection token as a multi-provider token that represents an array of values.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
// used to create fake backend
import { fakeBackendProvider } from './_helpers';
import { AppRoutingModule } from './app-routing.module';
import { JwtInterceptor, ErrorInterceptor } from './_helpers';
import { AppComponent } from './app.component';
import { AlertComponent } from './_components';
import { HomeComponent } from './home';
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
HttpClientModule,
AppRoutingModule
],
declarations: [
AppComponent,
AlertComponent,
HomeComponent
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
// provider used to create fake backend
fakeBackendProvider
],
bootstrap: [AppComponent]
})
export class AppModule { };
Need Some Angular Help?
Search fiverr for freelance Angular developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!