48hr.email/infrastructure/web/api/middleware/authenticator.js

97 lines
3.1 KiB
JavaScript

/**
* API Authentication Middleware
* Supports both session-based auth and Bearer token auth
*/
function createAuthenticator(apiTokenRepository) {
/**
* Require authentication - returns 401 if not authenticated
*/
function requireAuth(req, res, next) {
// Check session first (existing web auth)
if (req.session && req.session.isAuthenticated && req.session.userId) {
req.user = {
id: req.session.userId,
username: req.session.username
}
req.authMethod = 'session'
return next()
}
// Check Bearer token
const authHeader = req.headers.authorization
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7) // Remove 'Bearer ' prefix
const tokenData = apiTokenRepository.getByToken(token)
if (tokenData) {
req.user = {
id: tokenData.user_id,
username: tokenData.username
}
req.authMethod = 'token'
// Update last_used timestamp asynchronously
setImmediate(() => {
try {
apiTokenRepository.updateLastUsed(token)
} catch (err) {
// Log but don't fail the request
console.error('Failed to update token last_used:', err)
}
})
return next()
}
}
// No valid authentication found
return res.apiError('Authentication required', 'UNAUTHORIZED', 401)
}
/**
* Optional authentication - sets req.user if authenticated, but doesn't require it
*/
function optionalAuth(req, res, next) {
// Check session first
if (req.session && req.session.isAuthenticated && req.session.userId) {
req.user = {
id: req.session.userId,
username: req.session.username
}
req.authMethod = 'session'
return next()
}
// Check Bearer token
const authHeader = req.headers.authorization
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7)
const tokenData = apiTokenRepository.getByToken(token)
if (tokenData) {
req.user = {
id: tokenData.user_id,
username: tokenData.username
}
req.authMethod = 'token'
// Update last_used timestamp asynchronously
setImmediate(() => {
try {
apiTokenRepository.updateLastUsed(token)
} catch (err) {
console.error('Failed to update token last_used:', err)
}
})
}
}
// Continue regardless of auth status
next()
}
return { requireAuth, optionalAuth }
}
module.exports = createAuthenticator