In modern web applications, secure authentication is paramount. JSON Web Tokens (JWT) have become a standard for securely transmitting information between parties as a JSON object. They are compact, self-contained, and can be easily used for authentication and information exchange. This article explores how to implement JWT-based authentication in Angular, ensuring secure user data handling and robust session management.
Understanding JWT JWT is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Implementing JWT in Angular Angular provides various libraries and tools to implement JWT authentication. Here’s a step-by-step guide to integrating JWT-based authentication in an Angular application.
1. Setting Up the Angular Project Before diving into JWT, ensure that you have an Angular project set up. You can create a new Angular project using the Angular CLI.
```bash
ng new angular-jwt-auth
cd angular-jwt-auth
```
```bash
ng new angular-jwt-auth
cd angular-jwt-auth
```
Install the necessary packages:
npm install @auth0/angular-jwt
npm install --save angular-oauth2-oidc
```bash
npm install @auth0/angular-jwt
npm install --save angular-oauth2-oidc
```
```bash
npm install @auth0/angular-jwt
npm install --save angular-oauth2-oidc
```
2. Creating an Authentication Service The authentication service will handle the login, logout, and token management. Below is a simple example of how to create an authentication service in Angular.
import { Injectable } from '@angular/core' ;
import { HttpClient } from '@angular/common/http' ;
import { Router } from '@angular/router' ;
import { JwtHelperService } from '@auth0/angular-jwt' ;
export class AuthService {
private apiUrl = 'https://example.com/api/auth' ;
private tokenKey = 'auth_token' ;
constructor ( private http: HttpClient, private router: Router, private jwtHelper: JwtHelperService ) {}
login ( credentials: any ) {
return this . http . post ( `$ { this . apiUrl } /login`, credentials )
. subscribe (( response: any ) = > {
localStorage. setItem ( this . tokenKey , response. token ) ;
this . router . navigate ([ '/dashboard' ]) ;
localStorage. removeItem ( this . tokenKey ) ;
this . router . navigate ([ '/login' ]) ;
isAuthenticated () : boolean {
const token = localStorage. getItem ( this . tokenKey ) ;
return token ? ! this . jwtHelper . isTokenExpired ( token ) : false ;
getToken () : string | null {
return localStorage. getItem ( this . tokenKey ) ;
```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { JwtHelperService } from '@auth0/angular-jwt';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private apiUrl = 'https://example.com/api/auth';
private tokenKey = 'auth_token';
constructor(private http: HttpClient, private router: Router, private jwtHelper: JwtHelperService) {}
login(credentials: any) {
return this.http.post(`${this.apiUrl}/login`, credentials)
.subscribe((response: any) => {
localStorage.setItem(this.tokenKey, response.token);
this.router.navigate(['/dashboard']);
});
}
logout() {
localStorage.removeItem(this.tokenKey);
this.router.navigate(['/login']);
}
isAuthenticated(): boolean {
const token = localStorage.getItem(this.tokenKey);
return token ? !this.jwtHelper.isTokenExpired(token) : false;
}
getToken(): string | null {
return localStorage.getItem(this.tokenKey);
}
}
```
```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { JwtHelperService } from '@auth0/angular-jwt';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private apiUrl = 'https://example.com/api/auth';
private tokenKey = 'auth_token';
constructor(private http: HttpClient, private router: Router, private jwtHelper: JwtHelperService) {}
login(credentials: any) {
return this.http.post(`${this.apiUrl}/login`, credentials)
.subscribe((response: any) => {
localStorage.setItem(this.tokenKey, response.token);
this.router.navigate(['/dashboard']);
});
}
logout() {
localStorage.removeItem(this.tokenKey);
this.router.navigate(['/login']);
}
isAuthenticated(): boolean {
const token = localStorage.getItem(this.tokenKey);
return token ? !this.jwtHelper.isTokenExpired(token) : false;
}
getToken(): string | null {
return localStorage.getItem(this.tokenKey);
}
}
```
3. Adding HTTP Interceptors for Token Management To ensure that every HTTP request includes the JWT, you can use an HTTP interceptor.
import { Injectable } from '@angular/core' ;
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http' ;
import { Observable } from 'rxjs' ;
import { AuthService } from './auth.service' ;
export class JwtInterceptor implements HttpInterceptor {
constructor ( private authService: AuthService ) {}
intercept ( req: HttpRequest < any > , next: HttpHandler ) : Observable < HttpEvent < any >> {
const token = this . authService . getToken () ;
const cloned = req. clone ({
headers: req. headers . set ( 'Authorization' , `Bearer $ { token } ` )
return next. handle ( cloned ) ;
```typescript
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken();
if (token) {
const cloned = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
return next.handle(cloned);
}
return next.handle(req);
}
}
```
```typescript
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken();
if (token) {
const cloned = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
return next.handle(cloned);
}
return next.handle(req);
}
}
```
In your Angular module, ensure that the interceptor is provided:
import { HTTP_INTERCEPTORS } from '@angular/common/http' ;
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }
export class AppModule {}
```typescript
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }
]
})
export class AppModule {}
```
```typescript
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }
]
})
export class AppModule {}
```
4. Protecting Routes with Guards To protect certain routes and ensure only authenticated users can access them, use Angular route guards.
import { Injectable } from '@angular/core' ;
import { CanActivate, Router } from '@angular/router' ;
import { AuthService } from './auth.service' ;
export class AuthGuard implements CanActivate {
constructor ( private authService: AuthService, private router: Router ) {}
if ( this . authService . isAuthenticated ()) {
this . router . navigate ([ '/login' ]) ;
```typescript
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.isAuthenticated()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
```
```typescript
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.isAuthenticated()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
```
Use the guard in your routing module:
import { AuthGuard } from './auth.guard' ;
{ path: 'dashboard' , component: DashboardComponent, canActivate: [ AuthGuard ] } ,
{ path: 'login' , component: LoginComponent } ,
{ path: '' , redirectTo: 'login' }
```typescript
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
{ path: '', redirectTo: 'login' }
];
```
```typescript
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
{ path: '', redirectTo: 'login' }
];
```
5. Handling Token Expiration and Refresh JWT tokens are typically short-lived. To handle token expiration, you can implement a token refresh mechanism.
Example of handling token refresh:
import { Injectable } from '@angular/core' ;
import { HttpClient } from '@angular/common/http' ;
import { JwtHelperService } from '@auth0/angular-jwt' ;
export class TokenService {
private refreshTokenUrl = 'https://example.com/api/auth/refresh-token' ;
constructor ( private http: HttpClient, private jwtHelper: JwtHelperService ) {}
const token = localStorage. getItem ( 'auth_token' ) ;
if ( token && this . jwtHelper . isTokenExpired ( token )) {
return this . http . post ( this . refreshTokenUrl , { token }) ;
```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';
@Injectable({
providedIn: 'root'
})
export class TokenService {
private refreshTokenUrl = 'https://example.com/api/auth/refresh-token';
constructor(private http: HttpClient, private jwtHelper: JwtHelperService) {}
refreshToken() {
const token = localStorage.getItem('auth_token');
if (token && this.jwtHelper.isTokenExpired(token)) {
return this.http.post(this.refreshTokenUrl, { token });
}
return null;
}
}
```
```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';
@Injectable({
providedIn: 'root'
})
export class TokenService {
private refreshTokenUrl = 'https://example.com/api/auth/refresh-token';
constructor(private http: HttpClient, private jwtHelper: JwtHelperService) {}
refreshToken() {
const token = localStorage.getItem('auth_token');
if (token && this.jwtHelper.isTokenExpired(token)) {
return this.http.post(this.refreshTokenUrl, { token });
}
return null;
}
}
```
Conclusion Implementing JWT-based authentication in Angular provides a robust and scalable method for securing user data. By following the steps outlined in this article, you can ensure that your Angular application is protected against unauthorized access while maintaining a seamless user experience.
Further Reading: Angular Official Documentation Auth0 JWT Documentation Angular HTTP Interceptors