Overview
The TokenStorageService is a utility service that manages JWT (JSON Web Token) authentication tokens in the browser’s localStorage. It provides methods to save, retrieve, and remove tokens, as well as check authentication status.
Location: src/app/core/services/token-storage.service.ts
Constructor
No dependencies required. Uses browser’s native window.localStorage API.
Constants
TOKEN_KEY
string
default:"'auth-token'"
The localStorage key used to store the JWT token
Methods
saveToken()
Saves the JWT token to localStorage.
saveToken(token: string): void
The JWT token string to store
Returns: void
Behavior:
- Removes any existing token from localStorage
- Saves the new token to localStorage with key
'auth-token'
Example:
const jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
this.tokenStorage.saveToken(jwtToken);
console.log('Token saved successfully');
Use Case: Called by AuthService after successful login
getToken()
Retrieves the JWT token from localStorage.
getToken(): string | null
Returns: string | null - The stored JWT token, or null if not found
Example:
const token = this.tokenStorage.getToken();
if (token) {
console.log('User is authenticated');
// Use token for API requests
} else {
console.log('No token found - user not authenticated');
this.router.navigate(['/login']);
}
Use Case:
- Retrieving token for HTTP request headers
- Checking if user is logged in
- Used by AuthInterceptor to add token to API requests
signOut()
Removes the JWT token from localStorage, effectively logging out the user.
Returns: void
Behavior: Removes the token from localStorage using the TOKEN_KEY
Example:
onLogout() {
this.tokenStorage.signOut();
console.log('User logged out');
this.router.navigate(['/login']);
}
Use Case: Called by AuthService.logout() to clear authentication data
isLoggedIn()
Checks whether a user is currently authenticated.
Returns: boolean - true if token exists, false otherwise
Implementation: Returns true if getToken() returns a non-null value
Example:
if (this.tokenStorage.isLoggedIn()) {
// Show authenticated user features
this.showUserMenu = true;
} else {
// Redirect to login
this.router.navigate(['/login']);
}
Use Case:
- Route guards to protect authenticated pages
- Conditional UI rendering
- Navigation logic
Complete Usage Example
Integration with AuthService
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { TokenStorageService } from '../core/services/token-storage.service';
import { AuthResponse, AuthRequest } from '../core/models/auth.interface';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(
private http: HttpClient,
private tokenStorage: TokenStorageService
) {}
login(credentials: AuthRequest) {
return this.http.post<AuthResponse>('/api/auth/login', credentials)
.subscribe({
next: (response) => {
// Save token using TokenStorageService
this.tokenStorage.saveToken(response.token);
// Save other user data
localStorage.setItem('username', response.username);
localStorage.setItem('role', response.role);
}
});
}
logout() {
// Remove token using TokenStorageService
this.tokenStorage.signOut();
// Remove other user data
localStorage.removeItem('username');
localStorage.removeItem('role');
}
isAuthenticated(): boolean {
return this.tokenStorage.isLoggedIn();
}
}
Use in HTTP Interceptor
import { Injectable, inject } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { TokenStorageService } from '../services/token-storage.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private tokenStorage = inject(TokenStorageService);
intercept(req: HttpRequest<any>, next: HttpHandler) {
// Get token from storage
const token = this.tokenStorage.getToken();
if (token) {
// Clone request and add Authorization header
const authReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
return next.handle(authReq);
}
return next.handle(req);
}
}
Use in Route Guard
import { Injectable, inject } from '@angular/core';
import { Router } from '@angular/router';
import { TokenStorageService } from '../services/token-storage.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard {
private tokenStorage = inject(TokenStorageService);
private router = inject(Router);
canActivate(): boolean {
if (this.tokenStorage.isLoggedIn()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
Security Considerations
localStorage vs sessionStorage
This service uses localStorage which persists across browser sessions. Consider these security implications:
Advantages:
- Token persists across browser restarts
- Better user experience (stay logged in)
Disadvantages:
- Vulnerable to XSS attacks if application has XSS vulnerabilities
- Token persists until explicitly removed
Alternative Implementation with sessionStorage:
// For session-only storage (more secure)
saveToken(token: string): void {
window.sessionStorage.removeItem(TOKEN_KEY);
window.sessionStorage.setItem(TOKEN_KEY, token);
}
getToken(): string | null {
return window.sessionStorage.getItem(TOKEN_KEY);
}
Best Practices
- Token Expiration: Implement token refresh logic before expiration
- XSS Protection: Sanitize all user inputs to prevent XSS attacks
- HTTPS Only: Always use HTTPS in production to prevent token interception
- Automatic Logout: Clear tokens on security-sensitive errors (401, 403)
// Example: Auto-logout on 401 error in HTTP interceptor
if (error.status === 401) {
this.tokenStorage.signOut();
this.router.navigate(['/login']);
}
The service stores JWT tokens in standard format:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
The token consists of three parts separated by dots:
- Header: Algorithm and token type
- Payload: User data and claims
- Signature: Verification signature
Debugging
Inspect tokens in browser developer tools:
// Check current token in console
console.log('Current token:', this.tokenStorage.getToken());
// Check localStorage directly
console.log('localStorage:', window.localStorage.getItem('auth-token'));
// Decode JWT payload (for debugging only - don't trust client-side)
const token = this.tokenStorage.getToken();
if (token) {
const payload = JSON.parse(atob(token.split('.')[1]));
console.log('Token payload:', payload);
}
Common Patterns
Checking Authentication Before Navigation
@Component({
selector: 'app-nav',
template: `...`
})
export class NavComponent {
tokenStorage = inject(TokenStorageService);
navigateToProfile() {
if (!this.tokenStorage.isLoggedIn()) {
alert('Please login first');
return;
}
this.router.navigate(['/profile']);
}
}
Conditional Template Rendering
@Component({
selector: 'app-header',
template: `
<div *ngIf="isLoggedIn()">
<button (click)="logout()">Logout</button>
</div>
<div *ngIf="!isLoggedIn()">
<a routerLink="/login">Login</a>
</div>
`
})
export class HeaderComponent {
tokenStorage = inject(TokenStorageService);
isLoggedIn(): boolean {
return this.tokenStorage.isLoggedIn();
}
logout() {
this.tokenStorage.signOut();
window.location.href = '/login';
}
}
See Also