feat(auth): implement Android refresh token handling and session management
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 5m7s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped

- 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:
Torsten Schulz (local)
2026-05-27 19:34:32 +02:00
parent 755442fb70
commit 58fd7fa5c6
32 changed files with 1477 additions and 180 deletions

View File

@@ -1,4 +1,4 @@
import { readUsers, writeUsers, verifyPassword, generateToken, createSession, migrateUserRoles } from '../../utils/auth.js'
import { readUsers, writeUsers, verifyPassword, generateToken, generateAndroidAccessToken, createSession, createRefreshSession, migrateUserRoles } from '../../utils/auth.js'
import { assertRateLimit, getClientIp, registerRateLimitFailure, registerRateLimitSuccess } from '../../utils/rate-limit.js'
import { getAuthCookieOptions } from '../../utils/cookies.js'
import { writeAuditLog } from '../../utils/audit-log.js'
@@ -7,6 +7,7 @@ export default defineEventHandler(async (event) => {
try {
const body = await readBody(event)
const { email, password } = body
const isAndroidClient = body.client === 'android'
if (!email || !password) {
throw createError({
@@ -72,11 +73,15 @@ export default defineEventHandler(async (event) => {
registerRateLimitSuccess(event, { name: 'auth:login:ip', keyParts: [ip] })
registerRateLimitSuccess(event, { name: 'auth:login:account', keyParts: [emailKey] })
// Generate token
const token = generateToken(user)
// Create session
await createSession(user.id, token)
let token
let refreshSession = null
if (isAndroidClient) {
refreshSession = await createRefreshSession(user.id, body.deviceName)
token = generateAndroidAccessToken(user, refreshSession.session.id)
} else {
token = generateToken(user)
await createSession(user.id, token)
}
await writeAuditLog('auth.login.success', { ip, email: emailKey, userId: user.id })
@@ -85,10 +90,13 @@ export default defineEventHandler(async (event) => {
const updatedUsers = users.map(u => u.id === user.id ? user : u)
await writeUsers(updatedUsers)
// Set cookie
setCookie(event, 'auth_token', token, {
...getAuthCookieOptions()
})
if (isAndroidClient) {
deleteCookie(event, 'auth_token')
} else {
setCookie(event, 'auth_token', token, {
...getAuthCookieOptions()
})
}
// Migriere Rollen falls nötig
const migratedUser = migrateUserRoles({ ...user })
@@ -98,6 +106,9 @@ export default defineEventHandler(async (event) => {
return {
success: true,
token: token, // Token auch im Body für externe API-Clients
accessToken: isAndroidClient ? token : undefined,
refreshToken: refreshSession?.refreshToken,
sessionId: refreshSession?.session.id,
user: {
id: user.id,
email: user.email,
@@ -112,4 +123,3 @@ export default defineEventHandler(async (event) => {
throw error
}
})