curl -X GET "https://auth-api.yourdomain.com/reauthenticate" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json"
const response = await fetch('/reauthenticate', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (response.ok) {
const result = await response.json();
console.log('Reauthentication successful:', result);
} else {
console.error('Reauthentication failed');
}
import requests
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
response = requests.get(
'https://auth-api.yourdomain.com/reauthenticate',
headers=headers
)
if response.status_code == 200:
result = response.json()
print(f"Reauthentication successful: {result}")
else:
print("Reauthentication failed")
package main
import (
"fmt"
"net/http"
)
func reauthenticate(accessToken string) error {
req, err := http.NewRequest("GET", "https://auth-api.yourdomain.com/reauthenticate", nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
fmt.Println("Reauthentication successful")
return nil
}
return fmt.Errorf("reauthentication failed with status: %d", resp.StatusCode)
}
{
"message": "Reauthentication successful",
"timestamp": "2023-12-01T10:30:00Z",
"valid_until": "2023-12-01T10:45:00Z"
}
{
"code": 401,
"msg": "Invalid or expired token",
"details": "JWT token is invalid or has expired"
}
{
"code": 403,
"msg": "Reauthentication required",
"details": "Token is too old for sensitive operations"
}
User Management
Reauthenticate User
Verify user identity for sensitive operations requiring additional security
GET
/
reauthenticate
curl -X GET "https://auth-api.yourdomain.com/reauthenticate" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json"
const response = await fetch('/reauthenticate', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (response.ok) {
const result = await response.json();
console.log('Reauthentication successful:', result);
} else {
console.error('Reauthentication failed');
}
import requests
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
response = requests.get(
'https://auth-api.yourdomain.com/reauthenticate',
headers=headers
)
if response.status_code == 200:
result = response.json()
print(f"Reauthentication successful: {result}")
else:
print("Reauthentication failed")
package main
import (
"fmt"
"net/http"
)
func reauthenticate(accessToken string) error {
req, err := http.NewRequest("GET", "https://auth-api.yourdomain.com/reauthenticate", nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
fmt.Println("Reauthentication successful")
return nil
}
return fmt.Errorf("reauthentication failed with status: %d", resp.StatusCode)
}
{
"message": "Reauthentication successful",
"timestamp": "2023-12-01T10:30:00Z",
"valid_until": "2023-12-01T10:45:00Z"
}
{
"code": 401,
"msg": "Invalid or expired token",
"details": "JWT token is invalid or has expired"
}
{
"code": 403,
"msg": "Reauthentication required",
"details": "Token is too old for sensitive operations"
}
Overview
The/reauthenticate endpoint verifies the user’s identity for sensitive operations that require additional security confirmation. This endpoint is typically used before allowing users to perform critical actions like changing passwords, updating payment methods, or accessing sensitive data.
This endpoint requires a valid JWT token in the Authorization header.
Request
curl -X GET "https://auth-api.yourdomain.com/reauthenticate" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json"
const response = await fetch('/reauthenticate', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (response.ok) {
const result = await response.json();
console.log('Reauthentication successful:', result);
} else {
console.error('Reauthentication failed');
}
import requests
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
response = requests.get(
'https://auth-api.yourdomain.com/reauthenticate',
headers=headers
)
if response.status_code == 200:
result = response.json()
print(f"Reauthentication successful: {result}")
else:
print("Reauthentication failed")
package main
import (
"fmt"
"net/http"
)
func reauthenticate(accessToken string) error {
req, err := http.NewRequest("GET", "https://auth-api.yourdomain.com/reauthenticate", nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
fmt.Println("Reauthentication successful")
return nil
}
return fmt.Errorf("reauthentication failed with status: %d", resp.StatusCode)
}
Response
{
"message": "Reauthentication successful",
"timestamp": "2023-12-01T10:30:00Z",
"valid_until": "2023-12-01T10:45:00Z"
}
{
"code": 401,
"msg": "Invalid or expired token",
"details": "JWT token is invalid or has expired"
}
{
"code": 403,
"msg": "Reauthentication required",
"details": "Token is too old for sensitive operations"
}
Response Fields
| Field | Type | Description |
|---|---|---|
message | string | Success message |
timestamp | string | Current timestamp |
valid_until | string | When the reauthentication expires (typically 15 minutes) |
Implementation Examples
React Hook for Reauthentication
import { useState } from 'react';
import { useAuth } from './useAuth';
export function useReauthentication() {
const [isReauthenticated, setIsReauthenticated] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const { accessToken } = useAuth();
const reauthenticate = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('/api/auth/reauthenticate', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Reauthentication failed');
}
const result = await response.json();
setIsReauthenticated(true);
// Auto-expire reauthentication after 15 minutes
setTimeout(() => {
setIsReauthenticated(false);
}, 15 * 60 * 1000);
return result;
} catch (err) {
setError(err.message);
setIsReauthenticated(false);
throw err;
} finally {
setLoading(false);
}
};
const clearReauthentication = () => {
setIsReauthenticated(false);
setError(null);
};
return {
isReauthenticated,
reauthenticate,
clearReauthentication,
loading,
error
};
}
// Usage in component
function SensitiveOperation() {
const { isReauthenticated, reauthenticate, loading } = useReauthentication();
const [showReauthPrompt, setShowReauthPrompt] = useState(false);
const handleSensitiveAction = async () => {
if (!isReauthenticated) {
setShowReauthPrompt(true);
return;
}
// Proceed with sensitive operation
await performSensitiveOperation();
};
const handleReauthenticate = async () => {
try {
await reauthenticate();
setShowReauthPrompt(false);
// Now proceed with the sensitive operation
await performSensitiveOperation();
} catch (error) {
console.error('Reauthentication failed:', error);
}
};
if (showReauthPrompt) {
return (
<div className="reauthentication-prompt">
<h3>Security Verification Required</h3>
<p>This action requires additional verification for your security.</p>
<button
onClick={handleReauthenticate}
disabled={loading}
>
{loading ? 'Verifying...' : 'Verify Identity'}
</button>
<button onClick={() => setShowReauthPrompt(false)}>
Cancel
</button>
</div>
);
}
return (
<button onClick={handleSensitiveAction}>
Perform Sensitive Action
</button>
);
}
Protected Route Component
import { useEffect, useState } from 'react';
import { useReauthentication } from './useReauthentication';
function ProtectedSensitiveRoute({ children }) {
const { isReauthenticated, reauthenticate, loading } = useReauthentication();
const [showReauthForm, setShowReauthForm] = useState(false);
useEffect(() => {
// Check if reauthentication is needed when component mounts
if (!isReauthenticated) {
setShowReauthForm(true);
}
}, [isReauthenticated]);
const handleReauthenticate = async () => {
try {
await reauthenticate();
setShowReauthForm(false);
} catch (error) {
console.error('Reauthentication failed:', error);
}
};
if (showReauthForm) {
return (
<div className="reauthentication-gate">
<div className="reauthentication-card">
<h2>Security Verification</h2>
<p>
For your security, please verify your identity to access this section.
</p>
<button
onClick={handleReauthenticate}
disabled={loading}
className="btn-primary"
>
{loading ? 'Verifying...' : 'Verify Identity'}
</button>
</div>
</div>
);
}
return children;
}
// Usage
function App() {
return (
<Routes>
<Route path="/settings" element={
<ProtectedSensitiveRoute>
<UserSettings />
</ProtectedSensitiveRoute>
} />
<Route path="/billing" element={
<ProtectedSensitiveRoute>
<BillingSettings />
</ProtectedSensitiveRoute>
} />
</Routes>
);
}
Node.js Middleware
const jwt = require('jsonwebtoken');
// Middleware to check if reauthentication is required
function requireReauthentication(maxAge = 15 * 60 * 1000) { // 15 minutes default
return async (req, res, next) => {
try {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({
code: 401,
msg: 'No token provided'
});
}
// Decode token without verification to check timestamp
const decoded = jwt.decode(token);
if (!decoded) {
return res.status(401).json({
code: 401,
msg: 'Invalid token format'
});
}
const tokenAge = Date.now() - (decoded.iat * 1000);
if (tokenAge > maxAge) {
return res.status(403).json({
code: 403,
msg: 'Reauthentication required',
details: 'Token is too old for sensitive operations'
});
}
// Check if user has recently reauthenticated
const reauthKey = `reauth:${decoded.sub}`;
const reauthTimestamp = await redis.get(reauthKey);
if (!reauthTimestamp || (Date.now() - parseInt(reauthTimestamp)) > maxAge) {
return res.status(403).json({
code: 403,
msg: 'Reauthentication required',
details: 'Recent identity verification required'
});
}
next();
} catch (error) {
res.status(500).json({
code: 500,
msg: 'Internal server error'
});
}
};
}
// Reauthentication endpoint handler
app.get('/reauthenticate', authenticateToken, async (req, res) => {
try {
const userId = req.user.sub;
const now = Date.now();
// Store reauthentication timestamp
const reauthKey = `reauth:${userId}`;
await redis.setex(reauthKey, 15 * 60, now.toString()); // 15 minutes TTL
res.json({
message: 'Reauthentication successful',
timestamp: new Date(now).toISOString(),
valid_until: new Date(now + 15 * 60 * 1000).toISOString()
});
} catch (error) {
res.status(500).json({
code: 500,
msg: 'Internal server error'
});
}
});
// Usage on sensitive routes
app.put('/user/password',
authenticateToken,
requireReauthentication(10 * 60 * 1000), // 10 minutes for password change
async (req, res) => {
// Handle password change
res.json({ message: 'Password updated successfully' });
}
);
app.delete('/user/account',
authenticateToken,
requireReauthentication(5 * 60 * 1000), // 5 minutes for account deletion
async (req, res) => {
// Handle account deletion
res.json({ message: 'Account deleted successfully' });
}
);
Use Cases
Sensitive Operations
Require reauthentication before:- Changing passwords or email addresses
- Updating payment methods
- Deleting accounts or data
- Accessing financial information
- Modifying security settings
Time-Based Security
- Short-lived verification: 5-15 minutes for critical operations
- Session-based: Require reauthentication once per session for sensitive areas
- Operation-specific: Different timeouts for different sensitivity levels
Progressive Security
- Low sensitivity: No reauthentication required
- Medium sensitivity: Reauthentication within last 15 minutes
- High sensitivity: Reauthentication within last 5 minutes
- Critical operations: Fresh reauthentication required
Security Considerations
- Token Age: Check both JWT
iat(issued at) and recent reauthentication timestamp - Secure Storage: Store reauthentication state securely (Redis, encrypted cookies)
- Time Limits: Use appropriate time limits based on operation sensitivity
- User Experience: Balance security with usability
- Audit Logging: Log reauthentication attempts and sensitive operations
Rate Limiting
- Endpoint: 10 requests per 5 minutes per user
- Purpose: Prevent abuse while allowing legitimate use
- Headers: Standard rate limiting headers included in response
Related Endpoints
- User Profile - May require reauthentication for sensitive data
- Update Profile - Requires reauthentication for security fields
- Logout - Alternative to reauthentication for security
Testing
// Jest test example
describe('Reauthentication', () => {
test('should succeed with valid token', async () => {
const response = await request(app)
.get('/reauthenticate')
.set('Authorization', `Bearer ${validToken}`);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
expect(response.body).toHaveProperty('valid_until');
});
test('should fail without token', async () => {
const response = await request(app)
.get('/reauthenticate');
expect(response.status).toBe(401);
expect(response.body.msg).toContain('token');
});
test('should require reauthentication for old tokens', async () => {
const oldToken = generateToken({ iat: Math.floor(Date.now() / 1000) - 3600 }); // 1 hour old
const response = await request(app)
.put('/user/password')
.set('Authorization', `Bearer ${oldToken}`)
.send({ password: 'newpassword123' });
expect(response.status).toBe(403);
expect(response.body.msg).toContain('Reauthentication required');
});
});
⌘I