Angular Authentication Interceptor
In modern web applications, securing API endpoints is crucial. This challenge focuses on implementing an authentication interceptor in Angular to automatically attach authentication tokens to outgoing HTTP requests. This pattern simplifies token management and ensures that all protected API calls are authenticated without manual intervention in each service.
Problem Description
You are tasked with creating an HttpInterceptor in Angular that intercepts all outgoing HTTP requests. This interceptor should check if a user is authenticated and, if so, add an authorization header (e.g., Authorization: Bearer <token>) to the request before it is sent to the server.
Key Requirements:
- Intercept Outgoing Requests: The interceptor must be able to capture and modify outgoing
HttpRequestobjects. - Check for Authentication: The interceptor needs a mechanism to determine if a user is currently authenticated. For this challenge, assume you have a simple
AuthServicewith anisAuthenticated()method and agetToken()method. - Add Authorization Header: If the user is authenticated, append an
Authorizationheader. The header value should beBearerfollowed by the authentication token obtained from theAuthService. - Handle Unauthenticated Requests: If the user is not authenticated, the request should proceed without the
Authorizationheader. - Continue the Request Chain: The interceptor must pass the modified (or unmodified) request to the next interceptor in the chain or to the
HttpClientitself. - Handle Errors Gracefully: If there's an issue retrieving the token or adding the header, the interceptor should not break the application.
Expected Behavior:
- Requests to API endpoints requiring authentication will have the
Authorization: Bearer <token>header. - Requests to public API endpoints will not have this header.
- The application should function normally for both authenticated and unauthenticated users.
Edge Cases:
- What happens if
AuthService.getToken()returnsnullorundefined? The interceptor should handle this gracefully and not throw an error. - Consider requests that might already have an
Authorizationheader. The interceptor should ideally append to or overwrite it as per specific application requirements. For this challenge, we'll assume it should add if not present or if the service indicates it's managed.
Examples
Example 1: Authenticated Request
// AuthService (mock)
class AuthService {
isAuthenticated(): boolean { return true; }
getToken(): string | null { return 'my-secret-token'; }
}
// An outgoing GET request to '/api/user/profile'
// The AuthService.isAuthenticated() returns true, and AuthService.getToken() returns 'my-secret-token'.
// Expected modified request sent to the server:
// Method: GET
// URL: /api/user/profile
// Headers:
// Authorization: Bearer my-secret-token
// Content-Type: application/json (or other default headers)
Explanation:
Since the user is authenticated, the interceptor retrieves the token 'my-secret-token' and adds it to the request headers as Authorization: Bearer my-secret-token.
Example 2: Unauthenticated Request
// AuthService (mock)
class AuthService {
isAuthenticated(): boolean { return false; }
getToken(): string | null { return null; } // Or could be undefined
}
// An outgoing GET request to '/api/products'
// The AuthService.isAuthenticated() returns false.
// Expected request sent to the server:
// Method: GET
// URL: /api/products
// Headers:
// Content-Type: application/json (or other default headers)
// (No Authorization header is present)
Explanation:
The user is not authenticated, so the interceptor does not add an Authorization header to the request.
Example 3: Edge Case - Token is null
// AuthService (mock)
class AuthService {
isAuthenticated(): boolean { return true; } // User *thinks* they are authenticated
getToken(): string | null { return null; } // But token is missing
}
// An outgoing POST request to '/api/items'
// The AuthService.isAuthenticated() returns true, but AuthService.getToken() returns null.
// Expected request sent to the server:
// Method: POST
// URL: /api/items
// Headers:
// Content-Type: application/json (or other default headers)
// (No Authorization header is present, to avoid sending 'Bearer null')
Explanation:
Even though isAuthenticated() returns true, getToken() returns null. The interceptor should detect this and not add an invalid Authorization header (like Bearer null). The request proceeds without the header.
Constraints
- The interceptor must be implemented in TypeScript.
- You will need to leverage Angular's
HttpInterceptorinterface. - Assume the existence of an
AuthServicewithisAuthenticated()andgetToken()methods. You do not need to implement theAuthServiceitself; you can mock it for testing purposes. - The interceptor should not introduce significant performance overhead.
Notes
- Remember to register your interceptor in your Angular module.
- Consider how your interceptor will handle different HTTP methods (GET, POST, PUT, DELETE, etc.). The logic should be consistent.
- The
AuthServiceis a simplification. In a real application, this service might interact withlocalStorage,sessionStorage, cookies, or a state management solution. - Think about how to clone the original request when modifying it. The
HttpRequestobject is immutable.