1. Why do we need HTTP Interceptors?
What is the need?
HTTP interceptors are used to handle common HTTP request/response logic in one central place instead of repeating the same code in every Angular service.
For example, suppose your application has:
ProductService
CustomerService
OrderService
PaymentService
ArticleService
All these services call your ASP.NET Core Web API, and every secured request needs:
Authorization: Bearer <JWT_TOKEN>
Without an interceptor, you would have to manually attach the JWT token in every service.
Without Interceptor
getProducts() {
const token = localStorage.getItem('token');
const headers = {
Authorization: `Bearer ${token}`
};
return this.http.get(
'https://localhost:7001/api/products',
{ headers }
);
}
Then you repeat the same thing for orders:
getOrders() {
const token = localStorage.getItem('token');
const headers = {
Authorization: `Bearer ${token}`
};
return this.http.get(
'https://localhost:7001/api/orders',
{ headers }
);
}
And again for customers, payments, articles, etc.
That creates unnecessary duplication.
With Interceptor
Your services remain simple:
getProducts() {
return this.http.get(
'https://localhost:7001/api/products'
);
}
getOrders() {
return this.http.get(
'https://localhost:7001/api/orders'
);
}
The interceptor automatically adds:
Authorization: Bearer <JWT_TOKEN>
to applicable requests.
Flow
ProductService
│
│ HttpClient.get()
▼
┌─────────────────────────┐
│ Auth Interceptor │
│ │
│ Get JWT token │
│ Add Authorization │
│ header │
└────────────┬────────────┘
│
│ Authorization:
│ Bearer eyJhbG...
▼
┌─────────────────────────┐
│ ASP.NET Core Web API │
└─────────────────────────┘
Common uses of interceptors
Interceptors are useful for cross-cutting concerns such as:
| Requirement | Interceptor responsibility |
|---|---|
| JWT authentication | Add Authorization header |
| Error handling | Handle HTTP errors centrally |
| Logging | Log requests/responses |
| Correlation ID | Add correlation ID header |
| Loading indicator | Show/hide spinner |
| Token refresh | Handle expired access tokens |
| Custom headers | Add application-wide headers |
| API timing | Measure request duration |
Main advantage
Instead of:
Service A → JWT logic
Service B → JWT logic
Service C → JWT logic
Service D → JWT logic
we have:
Service A ─┐
Service B ─┤
Service C ─┼──→ Interceptor → Web API
Service D ─┘
This follows the DRY (Don't Repeat Yourself) principle.
2. How do you attach a JWT token to every API request?
For a modern standalone Angular application, you can create a functional interceptor.
Full Simple Example
Assume the JWT token is stored after login.
auth.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
getToken(): string | null {
return localStorage.getItem('accessToken');
}
setToken(token: string): void {
localStorage.setItem('accessToken', token);
}
removeToken(): void {
localStorage.removeItem('accessToken');
}
}
auth.interceptor.ts
import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
import { AuthService } from './auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
const token = authService.getToken();
if (token) {
const authRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next(authRequest);
}
return next(req);
};
app.config.ts
Register the interceptor:
import { ApplicationConfig } from '@angular/core';
import {
provideHttpClient,
withInterceptors
} from '@angular/common/http';
import { authInterceptor } from './auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
authInterceptor
])
)
]
};
product.service.ts
Now the service doesn't need to know anything about the JWT token.
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface Product {
id: number;
name: string;
price: number;
}
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private apiUrl =
'https://localhost:7001/api/products';
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(
this.apiUrl
);
}
}
Notice that we did not write:
Authorization: `Bearer ${token}`
inside ProductService.
The interceptor handles it automatically.
How does the JWT interceptor work?
Suppose login returns:
{
"accessToken": "eyJhbGciOiJIUzI1NiIs..."
}
You save it:
localStorage.setItem(
'accessToken',
response.accessToken
);
Later:
this.http.get(
'https://localhost:7001/api/products'
);
Angular passes the request through the interceptor.
Step 1 — Get token
const token = authService.getToken();
Result:
eyJhbGciOiJIUzI1NiIs...
Step 2 — Clone the request
const authRequest = req.clone({
Angular HTTP requests are immutable, so you don't modify the original request directly.
Step 3 — Add Authorization header
setHeaders: {
Authorization: `Bearer ${token}`
}
The outgoing request becomes:
GET /api/products HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Step 4 — Continue the request
return next(authRequest);
next() passes the modified request to the next interceptor or ultimately to the server.
Complete Flow
User Login
│
▼
ASP.NET Core Web API
│
│ returns JWT
▼
Angular
│
▼
Store Access Token
│
│
│ Later...
▼
ProductService
│
│ GET /api/products
▼
HttpClient
│
▼
Auth Interceptor
│
├── Get JWT
│
├── Clone Request
│
└── Add Authorization Header
│
▼
Authorization: Bearer <JWT>
│
▼
ASP.NET Core Web API
│
▼
JWT Authentication
│
▼
ProductsController
Important: Should every request receive the JWT?
Not necessarily.
For example, you may have:
POST /api/account/login
GET /api/products
GET /api/orders
The login request obviously doesn't need the existing access token.
You can exclude certain endpoints:
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
if (req.url.includes('/api/account/login')) {
return next(req);
}
const token = authService.getToken();
if (!token) {
return next(req);
}
const authRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next(authRequest);
};
In a production application, it is also sensible to ensure you attach credentials only to API origins you trust.
Key Points
- Interceptors provide centralized HTTP processing.
- They avoid repeating JWT/header/error-handling code in every service.
- JWT tokens are commonly attached through an authentication interceptor.
- Modern Angular supports functional interceptors with:
HttpInterceptorFn
- Register functional interceptors using:
provideHttpClient(
withInterceptors([authInterceptor])
)
- Read the token:
const token = authService.getToken();
- Clone the request:
req.clone()
- Add the JWT:
setHeaders: {
Authorization: `Bearer ${token}`
}
- Continue the pipeline:
next(authRequest)
- The interceptor does not provide security by itself. The ASP.NET Core API must validate the JWT and perform authorization.
Interview Questions and Answers
Q1. Why do we need HTTP interceptors?
HTTP interceptors centralize common request and response processing such as authentication, logging, error handling, correlation IDs, and token refresh, avoiding duplicated code across services.
Q2. How do you attach a JWT token to every API request?
Create an HttpInterceptorFn, retrieve the token, clone the HTTP request, add the Authorization: Bearer <token> header, and register the interceptor using withInterceptors().
const authRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next(authRequest);
Q3. Why do we clone the HTTP request?
Because Angular HttpRequest objects are immutable. To change headers or other request properties, we create a modified copy using req.clone().
Q4. What does next(authRequest) mean?
It forwards the modified request to the next interceptor in the chain, or to the HTTP backend if there are no more interceptors.
Q5. Should JWT validation happen in Angular?
No. Angular sends the JWT, but the backend must perform the actual authentication and authorization.
Angular
│
│ JWT
▼
Interceptor
│
│ Authorization: Bearer <JWT>
▼
ASP.NET Core API
│
├── Validate signature
├── Validate issuer
├── Validate audience
├── Validate expiration
└── Apply authorization
Interview one-line answer: An Angular HTTP interceptor provides a centralized place to process HTTP requests and responses; for JWT authentication, it can automatically clone outgoing API requests and add the Authorization: Bearer <token> header before forwarding them to the backend.