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

@@ -26,6 +26,36 @@ function deriveFromBaseUrl() {
}
}
function normalizeOrigin(value) {
try {
const u = new URL(value)
if (u.protocol === 'https:') {
return `https://${u.hostname}`
}
if (u.protocol === 'http:' && u.hostname === 'localhost') {
return `${u.protocol}//${u.host}`
}
return u.port === '80' ? `http://${u.hostname}` : `${u.protocol}//${u.host}`
} catch {
return value
}
}
function getAllowedOrigins(origin) {
const configured = String(process.env.WEBAUTHN_ALLOWED_ORIGINS || '')
.split(',')
.map(candidate => normalizeOrigin(candidate.trim()))
.filter(Boolean)
const origins = [origin, ...configured]
// Beide produktiven Hostnamen werden im Browser verwendet und gehoeren zur selben RP-ID.
if (origin === 'https://harheimertc.de' || origin === 'https://www.harheimertc.de') {
origins.push('https://harheimertc.de', 'https://www.harheimertc.de')
}
return [...new Set(origins)]
}
export function getWebAuthnConfig() {
const derived = deriveFromBaseUrl()
@@ -33,23 +63,8 @@ export function getWebAuthnConfig() {
const rpName = process.env.WEBAUTHN_RP_NAME || 'Harheimer TC'
// WEBAUTHN_ORIGIN hat Priorität, sonst von BASE_URL ableiten
let origin = process.env.WEBAUTHN_ORIGIN || derived.origin
// Sicherstellen, dass HTTPS-Origins KEINEN Port haben (auch wenn in ENV gesetzt)
if (origin.startsWith('https://')) {
try {
const u = new URL(origin)
// Port 443 oder kein Port = Standard, also Port weglassen
if (u.port === '443' || !u.port) {
origin = `https://${u.hostname}`
} else {
// Auch andere Ports bei HTTPS entfernen (nicht Standard für WebAuthn)
origin = `https://${u.hostname}`
}
} catch {
// Ignore
}
}
const origin = normalizeOrigin(process.env.WEBAUTHN_ORIGIN || derived.origin)
const origins = getAllowedOrigins(origin)
const requireUV = (process.env.WEBAUTHN_REQUIRE_UV || '').toLowerCase() === 'true'
@@ -57,13 +72,14 @@ export function getWebAuthnConfig() {
rpId,
rpName,
origin,
origins,
requireUV,
webauthnOriginEnv: process.env.WEBAUTHN_ORIGIN,
webauthnAllowedOriginsEnv: process.env.WEBAUTHN_ALLOWED_ORIGINS,
baseUrlEnv: process.env.NUXT_PUBLIC_BASE_URL,
derivedOrigin: derived.origin
})
return { rpId, rpName, origin, requireUV }
return { rpId, rpName, origin, origins, requireUV }
}