Token-Based Authentication Examples
This section provides practical examples demonstrating how to implement and utilize token-based authentication in your applications. Token-based authentication is a stateless method for securing APIs and web services, offering scalability and flexibility.
What is Token-Based Authentication?
In token-based authentication, after a user successfully logs in with their credentials, the server issues a unique token to the client. This token is then included in subsequent requests to authenticate the user. The server can validate this token without needing to store session state, making it highly efficient.
Common Token Types
- JWT (JSON Web Tokens): A popular open standard (RFC 7519) for securely transmitting information between parties as a JSON object.
- Opaque Tokens: Random strings that don't contain information themselves but serve as identifiers that the server can look up.
Example 1: Generating and Validating a JWT
Server-Side: Generating a JWT
This example uses Node.js with the 'jsonwebtoken' library to create a JWT upon successful login.
Node.js (Server-Side)
const jwt = require('jsonwebtoken');
const secretKey = 'your_super_secret_key'; // Should be kept secure and not hardcoded in production
function generateAuthToken(userId, username) {
const payload = {
userId: userId,
username: username,
iss: 'your_api_issuer', // Issuer
aud: 'your_api_audience', // Audience
exp: Math.floor(Date.now() / 1000) + (60 * 60) // Token expires in 1 hour
};
return jwt.sign(payload, secretKey);
}
// Example usage after successful authentication
const userId = 123;
const username = 'testuser';
const token = generateAuthToken(userId, username);
console.log('Generated Token:', token);
// The token would then be sent back to the client in the response body or header.
Client-Side: Sending a JWT with Requests
The client receives the token and includes it in the 'Authorization' header for protected API calls.
JavaScript (Client-Side Fetch API)
async function fetchDataWithToken(token) {
const response = await fetch('/api/protected-resource', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Example usage:
const userToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywidXNlcm5hbWUiOiJ0ZXN0dXNlciIsImlzcyI6InlvdXJfYXBpX2lzc3VlciIsImF1ZCI6InlvdXJfYXBpX2F1ZGllbmNlIiwiZXhwIjoxNjI3ODY4ODAwfQ.SflKxwRJSMe...'; // Replace with actual token
fetchDataWithToken(userToken)
.then(data => console.log('Data from protected resource:', data))
.catch(error => console.error('Error fetching data:', error));
Server-Side: Verifying a JWT
The server verifies the token's signature and checks for expiration and other claims.
Node.js (Server-Side Middleware)
const jwt = require('jsonwebtoken');
const secretKey = 'your_super_secret_key';
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (token == null) return res.sendStatus(401); // If there's no token
jwt.verify(token, secretKey, (err, user) => {
if (err) {
console.error('JWT Verification Error:', err.message);
return res.sendStatus(403); // If token is invalid or expired
}
req.user = user; // Attach user info to the request
next(); // Proceed to the next handler
});
}
// Apply this middleware to protected routes
// app.get('/api/protected-resource', authenticateToken, (req, res) => {
// res.json({ message: 'Access granted to protected resource!', user: req.user });
// });
Example 2: Using an API Key (Simple Token)
For simpler scenarios or internal services, a static API key can act as a token.
Client-Side: Sending an API Key
JavaScript (Client-Side Fetch API)
async function callApiWithApiKey(apiKey) {
const response = await fetch('/api/public-service', {
method: 'GET',
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Example usage:
const myApiKey = 'your_generated_api_key_here';
callApiWithApiKey(myApiKey)
.then(data => console.log('API Key Service Response:', data))
.catch(error => console.error('Error calling API:', error));
Server-Side: Validating an API Key
The server checks if the provided API key is valid and authorized.
Node.js (Server-Side Middleware)
const validApiKeys = ['your_generated_api_key_here', 'another_valid_key']; // In real app, use a secure store
function validateApiKey(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (!apiKey || !validApiKeys.includes(apiKey)) {
return res.status(401).json({ message: 'Invalid or missing API Key.' });
}
next();
}
// Apply this middleware to routes requiring API key authentication
// app.get('/api/public-service', validateApiKey, (req, res) => {
// res.json({ message: 'API Key accepted! Access granted to public service.' });
// });
Best Practices
- Secure Your Secret Key: If using JWTs, keep your secret key confidential and use strong, unique keys. Do not hardcode them in client-side code.
- Token Expiration: Set reasonable expiration times for your tokens to limit the window of exposure if a token is compromised.
- HTTPS: Always transmit tokens over HTTPS to prevent eavesdropping.
- Token Storage: On the client-side, store tokens securely. Consider HttpOnly cookies for web applications or secure storage mechanisms for mobile apps.
- Refresh Tokens: For longer-lived sessions, implement refresh tokens to obtain new access tokens without requiring the user to re-login frequently.
- Revocation: For critical applications, consider implementing token revocation mechanisms, though this can add complexity to stateless systems.