Chapter 9: Authentication and Security
9/1/25About 2 min
Chapter 9: Authentication and Security
Learning Objectives
- Implement Token authentication mechanisms
- Master JWT handling and refreshing
- Learn OAuth2.0 integration
- Understand HTTPS and certificate configuration
- Implement secure cross-domain handling
9.1 Token Authentication
Bearer Token Authentication
class TokenManager {
constructor() {
this.token = localStorage.getItem('access_token');
this.refreshToken = localStorage.getItem('refresh_token');
}
setToken(accessToken, refreshToken = null) {
this.token = accessToken;
localStorage.setItem('access_token', accessToken);
if (refreshToken) {
this.refreshToken = refreshToken;
localStorage.setItem('refresh_token', refreshToken);
}
}
getToken() {
return this.token;
}
clearToken() {
this.token = null;
this.refreshToken = null;
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
}
isTokenValid() {
if (!this.token) return false;
try {
const payload = JSON.parse(atob(this.token.split('.')[1]));
const now = Math.floor(Date.now() / 1000);
return payload.exp > now;
} catch (error) {
return false;
}
}
}
const tokenManager = new TokenManager();
9.2 JWT Handling and Automatic Refresh
JWT Interceptor
class JWTInterceptor {
constructor(axiosInstance, tokenManager) {
this.axios = axiosInstance;
this.tokenManager = tokenManager;
this.isRefreshing = false;
this.failedQueue = [];
this.setupInterceptors();
}
setupInterceptors() {
// Request interceptor - add token
this.axios.interceptors.request.use((config) => {
const token = this.tokenManager.getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor - handle token expiration
this.axios.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
if (this.isRefreshing) {
// If token is being refreshed, add request to queue
return new Promise((resolve, reject) => {
this.failedQueue.push({ resolve, reject });
}).then(token => {
originalRequest.headers.Authorization = `Bearer ${token}`;
return this.axios(originalRequest);
});
}
originalRequest._retry = true;
this.isRefreshing = true;
try {
const newToken = await this.refreshAccessToken();
this.tokenManager.setToken(newToken);
// Process requests in the queue
this.processQueue(null, newToken);
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return this.axios(originalRequest);
} catch (refreshError) {
this.processQueue(refreshError, null);
this.tokenManager.clearToken();
window.location.href = '/login';
return Promise.reject(refreshError);
} finally {
this.isRefreshing = false;
}
}
return Promise.reject(error);
}
);
}
async refreshAccessToken() {
const refreshToken = this.tokenManager.refreshToken;
if (!refreshToken) {
throw new Error('No refresh token available');
}
const response = await axios.post('/api/auth/refresh', {
refresh_token: refreshToken
});
return response.data.access_token;
}
processQueue(error, token = null) {
this.failedQueue.forEach(({ resolve, reject }) => {
if (error) {
reject(error);
} else {
resolve(token);
}
});
this.failedQueue = [];
}
}
9.3 OAuth2.0 Integration
OAuth2 Flow Implementation
class OAuth2Handler {
constructor(clientId, redirectUri, authUrl) {
this.clientId = clientId;
this.redirectUri = redirectUri;
this.authUrl = authUrl;
}
// Authorization Code Flow
initiateAuthorizationCode(scope = 'read write') {
const state = this.generateState();
sessionStorage.setItem('oauth_state', state);
const params = new URLSearchParams({
response_type: 'code',
client_id: this.clientId,
redirect_uri: this.redirectUri,
scope: scope,
state: state
});
window.location.href = `${this.authUrl}?${params.toString()}`;
}
// Handle authorization callback
async handleAuthorizationCallback(code, state) {
// Verify state
const storedState = sessionStorage.getItem('oauth_state');
if (state !== storedState) {
throw new Error('Invalid state parameter');
}
// Exchange for access token
const response = await axios.post('/api/oauth/token', {
grant_type: 'authorization_code',
client_id: this.clientId,
code: code,
redirect_uri: this.redirectUri
});
return response.data;
}
generateState() {
return Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
}
}
9.4 HTTPS and Security Configuration
Secure Request Configuration
// HTTPS and security configuration
const secureAxios = axios.create({
// Force HTTPS
baseURL: 'https://api.example.com',
// Security header settings
headers: {
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block'
},
// Request validation
validateStatus: (status) => {
return status >= 200 && status < 300;
}
});
// Certificate configuration in Node.js environment
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') {
const https = require('https');
const fs = require('fs');
const httpsAgent = new https.Agent({
cert: fs.readFileSync('path/to/client-cert.pem'),
key: fs.readFileSync('path/to/client-key.pem'),
ca: fs.readFileSync('path/to/ca-cert.pem'),
rejectUnauthorized: true
});
secureAxios.defaults.httpsAgent = httpsAgent;
}
9.5 CSRF Protection
CSRF Token Handling
class CSRFHandler {
constructor() {
this.token = null;
this.init();
}
async init() {
try {
// Get CSRF token
const response = await axios.get('/api/csrf-token');
this.token = response.data.token;
// Set default header
axios.defaults.headers.common['X-CSRF-TOKEN'] = this.token;
} catch (error) {
console.error('Failed to get CSRF token:', error);
}
}
async refreshToken() {
await this.init();
}
getToken() {
return this.token;
}
}
const csrfHandler = new CSRFHandler();
9.6 API Key Management
Secure API Key Handling
class APIKeyManager {
constructor() {
this.apiKey = process.env.API_KEY || '';
this.keyRotationInterval = 24 * 60 * 60 * 1000; // 24 hours
this.setupKeyRotation();
}
setupKeyRotation() {
setInterval(() => {
this.rotateApiKey();
}, this.keyRotationInterval);
}
async rotateApiKey() {
try {
const response = await axios.post('/api/rotate-key', {
current_key: this.apiKey
});
this.apiKey = response.data.new_key;
process.env.API_KEY = this.apiKey;
console.log('API key rotated successfully');
} catch (error) {
console.error('Failed to rotate API key:', error);
}
}
getApiKey() {
return this.apiKey;
}
setApiKeyHeader(config) {
config.headers = config.headers || {};
config.headers['X-API-Key'] = this.getApiKey();
return config;
}
}
Chapter Summary
- Token authentication is the standard authentication method for modern web applications
- Automatic JWT refresh ensures a continuous user experience
- OAuth2.0 provides secure third-party authorization
- HTTPS and security configurations protect data transmission
- CSRF protection prevents cross-site request forgery attacks
Key Takeaways
- Never store sensitive information on the client side
- Use HTTPS to protect all authentication-related communications
- Implement automatic token refresh to avoid repeated user logins
- Proper security header configuration enhances application security