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

  1. Token Storage: Consider using more secure storage methods than localStorage for production (like HttpOnly cookies)

  2. Token Expiration: Always check token expiration in your AuthService

  3. Refresh Tokens: Implement token refresh mechanism for better UX

  4. Error Handling: Handle 401 unauthorized responses globally

  5. Secure Routes: Protect both frontend and backend routes

Common Issues

  1. Interceptor Not Working: Make sure you've provided it in the AppModule

  2. Route Protection Issues: Verify the guard is properly implemented and added to routes

  3. Token Not Attached: Check if the token exists before cloning the request

  4. CORS Problems: Ensure your backend accepts requests with Authorization header

Comments

Popular posts from this blog

Interview Tips: Dot NET Framework vs Net CORE

FREE Webinar: Run Your Own Independent DeepSeek LLM

Delegates and Events