feat(auth): implement Android refresh token handling and session management
- Added support for generating Android access tokens and managing refresh sessions in the auth endpoints. - Implemented new tests for login, logout, and refresh functionalities specific to Android clients. - Enhanced password reset logging with normalization and masking of email addresses. - Created a new diagnostics endpoint for password reset attempts, including filtering and summarizing logs. - Introduced a new utility for managing password reset logs with retention policies. - Added tests for password reset log utilities to ensure proper functionality and privacy compliance. - Updated WebAuthn configuration tests to validate origin handling for production and allowed origins.
This commit is contained in:
47
server/api/auth/refresh.post.js
Normal file
47
server/api/auth/refresh.post.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { generateAndroidAccessToken, getUserById, revokeRefreshSession, rotateRefreshSession } from '../../utils/auth.js'
|
||||
import { assertRateLimit, getClientIp, registerRateLimitFailure, registerRateLimitSuccess } from '../../utils/rate-limit.js'
|
||||
import { writeAuditLog } from '../../utils/audit-log.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const ip = getClientIp(event)
|
||||
const body = await readBody(event)
|
||||
const refreshToken = body?.refreshToken
|
||||
|
||||
if (!refreshToken) {
|
||||
throw createError({ statusCode: 400, message: 'Refresh-Token fehlt' })
|
||||
}
|
||||
|
||||
assertRateLimit(event, {
|
||||
name: 'auth:refresh:ip',
|
||||
keyParts: [ip],
|
||||
windowMs: 10 * 60 * 1000,
|
||||
maxAttempts: 60,
|
||||
lockoutMs: 15 * 60 * 1000
|
||||
})
|
||||
|
||||
const rotated = await rotateRefreshSession(refreshToken)
|
||||
if (rotated.status !== 'rotated') {
|
||||
await registerRateLimitFailure(event, { name: 'auth:refresh:ip', keyParts: [ip], delayBaseMs: 100 })
|
||||
await writeAuditLog('auth.refresh.failed', { ip, reason: rotated.status })
|
||||
throw createError({ statusCode: 401, message: 'Sitzung ist nicht mehr gültig' })
|
||||
}
|
||||
|
||||
const user = await getUserById(rotated.session.userId)
|
||||
if (!user || user.active === false) {
|
||||
await revokeRefreshSession(rotated.refreshToken, 'inactive_or_missing_user')
|
||||
await writeAuditLog('auth.refresh.failed', { ip, userId: rotated.session.userId, reason: 'inactive_or_missing_user' })
|
||||
throw createError({ statusCode: 401, message: 'Sitzung ist nicht mehr gültig' })
|
||||
}
|
||||
|
||||
const accessToken = generateAndroidAccessToken(user, rotated.session.id)
|
||||
registerRateLimitSuccess(event, { name: 'auth:refresh:ip', keyParts: [ip] })
|
||||
await writeAuditLog('auth.refresh.success', { ip, userId: user.id, sessionId: rotated.session.id })
|
||||
|
||||
return {
|
||||
success: true,
|
||||
token: accessToken,
|
||||
accessToken,
|
||||
refreshToken: rotated.refreshToken,
|
||||
sessionId: rotated.session.id
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user