Introduction
Authentication and authorization are crucial aspects of modern web applications. They ensure that only authenticated users can access certain resources and that users have the appropriate permissions to perform actions. JSON Web Token (JWT) is a widely adopted authentication mechanism that allows secure data transmission between a client and a server. In this article, we will explore how to implement authentication and authorization in Angular using JWT.
Understanding JWT
JWT is an open standard (RFC 7519) that defines a compact, self-contained way to securely transmit information between parties as a JSON object. It is widely used for authentication because of its stateless nature and ability to carry claims. A typical JWT consists of three parts:
- Header – Contains metadata about the token, such as the algorithm used for signing.
- Payload – Contains the claims, such as user information and permissions.
- Signature – Ensures the integrity of the token.
A JWT looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvbiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Setting Up Angular Authentication with JWT
Step 1: Setting Up an Angular Application
First, create an Angular application using the Angular CLI:
npm install -g @angular/cli
ng new jwt-auth-app
cd jwt-auth-app
Install necessary dependencies:
npm install @auth0/angular-jwt
Step 2: Creating Authentication Service
Create an auth.service.ts
file inside the services
folder:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class AuthService {
private apiUrl = 'https://your-api-url.com/auth';
constructor(private http: HttpClient) {}
login(credentials: { email: string; password: string }): Observable<any> {
return this.http.post<any>(`${this.apiUrl}/login`, credentials).pipe(
tap(response => {
localStorage.setItem('token', response.token);
})
);
}
logout(): void {
localStorage.removeItem('token');
}
getToken(): string | null {
return localStorage.getItem('token');
}
}
Step 3: Implementing JWT Interceptor
Create an HTTP interceptor to attach the JWT to requests.
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { AuthService } from './auth.service';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const token = this.authService.getToken();
if (token) {
req = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
}
return next.handle(req);
}
}
Add the interceptor to the Angular providers in app.module.ts
:
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { JwtInterceptor } from './services/jwt-interceptor';
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }
]
Step 4: Protecting Routes with Route Guards
To protect specific routes, use Angular route guards.
Create auth.guard.ts
:
import { Injectable } from '@angular/core';
import { CanActivate, 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(): boolean {
if (this.authService.getToken()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
Apply the guard to protected routes:
const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] }
];
Step 5: Implementing Role-Based Authorization
Modify the authentication service to extract roles:
import jwtDecode from 'jwt-decode';
getUserRole(): string | null {
const token = this.getToken();
if (token) {
const decodedToken: any = jwtDecode(token);
return decodedToken.role;
}
return null;
}
Create an admin.guard.ts
for role-based access:
@Injectable({ providedIn: 'root' })
export class AdminGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean {
if (this.authService.getUserRole() === 'admin') {
return true;
} else {
this.router.navigate(['/forbidden']);
return false;
}
}
}
Use AdminGuard
for admin routes:
const routes: Routes = [
{ path: 'admin', component: AdminComponent, canActivate: [AdminGuard] }
];
Conclusion
Implementing JWT-based authentication in Angular improves security and scalability by providing a stateless authentication mechanism. This approach enables secure authentication, role-based access control, and protection against common security threats. By following best practices such as using route guards and interceptors, developers can ensure a robust authentication system in their Angular applications.