Angular: Guards and Interceptor
JWT Token Authentication in Angular:
Guards and Interceptors
Understanding the Concepts
JWT (JSON Web Token): A compact, URL-safe means of representing claims to be transferred between two parties. In authentication, it's used as a token that proves the user's identity.
AuthGuard: A service in Angular that determines if a route can be activated based on certain conditions (like whether the user is authenticated).
Interceptor: A service that can intercept and modify HTTP requests globally before they are sent to the server.
Why: Purpose of Implementation
Security: Protect routes from unauthorized access
Efficiency: Automatically attach tokens to outgoing requests
Consistency: Centralize authentication logic
User Experience: Redirect unauthorized users appropriately
Step-by-Step Implementation
Step 1: Set Up Angular Project
ng new angular-jwt-auth
cd angular-jwt-auth
Step 2: Install Required Packages
npm install @auth0/angular-jwt
Step 3: Create Auth Service
Create auth.service.ts:
import { Injectable } from '@angular/core';import { Router } from '@angular/router'; @Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private router: Router) {}
// Store JWT token in localStorage
setToken(token: string): void {
localStorage.setItem('jwt_token', token);
}
// Retrieve JWT token from localStorage
getToken(): string | null {
return localStorage.getItem('jwt_token');
}
// Check if user is logged in (token exists and not expired)
isLoggedIn(): boolean {
const token = this.getToken();
if (!token) return false;
// In a real app, you should also check token expiration here
return true;
}
// Logout user by removing token
logout(): void {
localStorage.removeItem('jwt_token');
this.router.navigate(['/login']);
}
}
Step 4: Create Auth Guard
Create auth.guard.ts:
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { constructor( private authService: AuthService, private router: Router ) {} canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): boolean { if (this.authService.isLoggedIn()) { return true; // Allow access if user is authenticated } // Redirect to login page if not authenticated this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); return false; } }
Step 5: Create JWT Interceptor
Create jwt.interceptor.ts:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { Observable } from 'rxjs'; import { AuthService } from './auth.service'; @Injectable() export class JwtInterceptor implements HttpInterceptor { constructor(private authService: AuthService) {} intercept( request: HttpRequest<unknown>, next: HttpHandler ): Observable<HttpEvent<unknown>> { // Get the token from auth service const token = this.authService.getToken(); // Clone the request and add the authorization header if token exists if (token) { request = request.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } // Pass the cloned request to the next handler return next.handle(request); } }
Step 6: Register the Interceptor
In app.module.ts:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { JwtInterceptor } from './jwt.interceptor'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule, AppRoutingModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule {}
Step 7: Protect Routes in Routing Module
In app-routing.module.ts:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { LoginComponent } from './login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard] // Protected route
},
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Step 8: Implement Login Functionality
In your login component:
import { Component } from '@angular/core'; import { AuthService } from '../auth.service'; import { Router } from '@angular/router'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent { constructor( private authService: AuthService, private router: Router ) {} login(username: string, password: string): void { // In a real app, you would call your authentication API here // For demo purposes, we'll just simulate a successful login const fakeToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // This would come from your API // Store the token this.authService.setToken(fakeToken); // Redirect to dashboard or returnUrl this.router.navigate(['/dashboard']); } }
Step 9: Implement Logout Functionality
In your navbar or wherever you have logout:
logout(): void {
this.authService.logout();
}
Best Practices
Token Storage: Consider using more secure storage methods than localStorage for production (like HttpOnly cookies)
Token Expiration: Always check token expiration in your AuthService
Refresh Tokens: Implement token refresh mechanism for better UX
Error Handling: Handle 401 unauthorized responses globally
Secure Routes: Protect both frontend and backend routes
Common Issues
Interceptor Not Working: Make sure you've provided it in the AppModule
Route Protection Issues: Verify the guard is properly implemented and added to routes
Token Not Attached: Check if the token exists before cloning the request
CORS Problems: Ensure your backend accepts requests with Authorization header
Comments
Post a Comment