HTTP Client Authentication Patterns in Angular
The Story of the Secret Clubhouse
Imagine you have a secret clubhouse. Not everyone can enter! You need a special wristband to get in. This wristband proves who you are.
In the web world, this wristband is called a JWT (JSON Web Token). And the guards at the door? Those are Route Guards!
What is JWT?
JWT stands for JSON Web Token. Think of it as a digital ID card.
When you log into an app:
- The server checks your username and password
- If correct, the server gives you a JWT token
- You show this token every time you want something
Real Life Example:
- You buy a movie ticket (login)
- You get a ticket stub (JWT token)
- You show it to enter any theater room (access resources)
What’s Inside a JWT?
A JWT has 3 parts separated by dots:
header.payload.signature
graph TD A["JWT Token"] --> B["Header"] A --> C["Payload"] A --> D["Signature"] B --> E["Algorithm type"] C --> F["User data"] D --> G["Verification"]
Interceptors: Your Automatic Helper
Imagine having a helper robot that:
- Automatically puts your wristband on every request
- You never forget to show it!
This is what an HTTP Interceptor does in Angular.
Creating a JWT Interceptor
import { Injectable } from
'@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler
} from '@angular/common/http';
@Injectable()
export class AuthInterceptor
implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
) {
// Get token from storage
const token = localStorage
.getItem('jwt_token');
// If we have a token
if (token) {
// Clone request, add token
const authReq = req.clone({
setHeaders: {
Authorization:
`Bearer ${token}`
}
});
return next.handle(authReq);
}
return next.handle(req);
}
}
Registering the Interceptor
In your app.config.ts or module:
import {
provideHttpClient,
withInterceptors
} from '@angular/common/http';
import { authInterceptor }
from './auth.interceptor';
export const appConfig = {
providers: [
provideHttpClient(
withInterceptors([
authInterceptor
])
)
]
};
How It Works
graph TD A["Your Request"] --> B["Interceptor"] B --> C{Has Token?} C -->|Yes| D["Add Token to Header"] C -->|No| E["Send Original Request"] D --> F["Send to Server"] E --> F
Route Guards: The Door Protectors
Remember the guards at the clubhouse? In Angular, they’re called Route Guards.
Guards answer one question: “Can this person enter?”
Types of Guards
| Guard Type | Question Asked |
|---|---|
CanActivate |
Can they enter? |
CanDeactivate |
Can they leave? |
CanMatch |
Can they see this route? |
Creating an Auth Guard
import { inject } from
'@angular/core';
import { Router } from
'@angular/router';
import { AuthService } from
'./auth.service';
export const authGuard = () => {
const authService =
inject(AuthService);
const router =
inject(Router);
if (authService.isLoggedIn()) {
return true;
}
// Redirect to login page
router.navigate(['/login']);
return false;
};
Using Guards in Routes
import { Routes } from
'@angular/router';
import { authGuard } from
'./auth.guard';
export const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [authGuard]
},
{
path: 'profile',
component: ProfileComponent,
canActivate: [authGuard]
},
{
path: 'login',
component: LoginComponent
}
];
Guard Flow
graph TD A["User clicks link"] --> B["Router checks guard"] B --> C{Guard returns?} C -->|true| D["Go to page"] C -->|false| E["Redirect to login"]
The Complete Authentication Flow
Let’s see how JWT and Guards work together!
graph TD A["User enters site"] --> B{Has valid token?} B -->|No| C["Show Login Page"] B -->|Yes| D["Guard allows entry"] C --> E["User logs in"] E --> F["Server sends JWT"] F --> G["Store token locally"] G --> H["User accesses pages"] H --> I["Interceptor adds token"] I --> J["Server validates"]
Step by Step
- User logs in with username/password
- Server validates and returns JWT
- App stores JWT in localStorage
- User navigates to protected page
- Guard checks if token exists
- Interceptor attaches token to all requests
- Server validates token on each request
Building the Auth Service
The Auth Service is the brain of authentication:
import { Injectable } from
'@angular/core';
import { HttpClient } from
'@angular/common/http';
import { BehaviorSubject } from
'rxjs';
import { tap } from
'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private loggedIn =
new BehaviorSubject<boolean>(
this.hasToken()
);
constructor(
private http: HttpClient
) {}
login(email: string, pw: string) {
return this.http.post<{token: string}>(
'/api/login',
{ email, password: pw }
).pipe(
tap(response => {
localStorage.setItem(
'jwt_token',
response.token
);
this.loggedIn.next(true);
})
);
}
logout() {
localStorage.removeItem(
'jwt_token'
);
this.loggedIn.next(false);
}
isLoggedIn(): boolean {
return this.hasToken();
}
private hasToken(): boolean {
return !!localStorage
.getItem('jwt_token');
}
getToken(): string | null {
return localStorage
.getItem('jwt_token');
}
}
Handling Token Expiration
JWT tokens expire! Like a movie ticket that’s only valid today.
Checking Expiration
isTokenExpired(): boolean {
const token = this.getToken();
if (!token) return true;
// JWT has 3 parts
const payload = token
.split('.')[1];
const decoded = JSON.parse(
atob(payload)
);
// exp is expiration time
const expiry = decoded.exp;
const now = Math.floor(
Date.now() / 1000
);
return expiry < now;
}
Interceptor with Expiration Check
intercept(req, next) {
const token = this.auth.getToken();
if (token) {
if (this.auth.isTokenExpired()) {
// Token expired, logout
this.auth.logout();
this.router.navigate(['/login']);
return EMPTY;
}
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(authReq);
}
return next.handle(req);
}
Error Handling in Interceptor
What if the server rejects our token?
intercept(req, next) {
const token = this.auth.getToken();
let authReq = req;
if (token) {
authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return next.handle(authReq).pipe(
catchError((error) => {
if (error.status === 401) {
// Unauthorized - token invalid
this.auth.logout();
this.router.navigate(['/login']);
}
return throwError(() => error);
})
);
}
Quick Summary
| Concept | What It Does |
|---|---|
| JWT | Digital ID card from server |
| Interceptor | Automatically adds token to requests |
| Guard | Protects routes from unauthorized access |
| Auth Service | Manages login, logout, and token storage |
Remember This!
- JWT = Your ticket to the clubhouse
- Interceptor = Helper that shows your ticket automatically
- Guard = The door security checking your ticket
- Auth Service = The manager who gives and takes tickets
You’re now ready to secure your Angular app like a pro! Every request is protected, every route is guarded, and your users are safe.
