Implement passkey recovery feature, including email link requests and registration options. Update login and registration pages to support passkey authentication, with UI enhancements for user experience. Add server-side handling for passkey registration and login, including account activation checks. Update environment configuration for passkey recovery TTL settings.
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 48s

This commit is contained in:
Torsten Schulz (local)
2026-01-07 18:37:01 +01:00
parent a8423f9c39
commit fde25d92c5
13 changed files with 843 additions and 5 deletions

View File

@@ -0,0 +1,21 @@
import crypto from 'crypto'
export function hashRecoveryToken(token) {
return crypto.createHash('sha256').update(String(token), 'utf8').digest('hex')
}
export function generateRecoveryToken() {
// URL-safe (hex)
return crypto.randomBytes(32).toString('hex')
}
export function pruneRecoveryTokens(user, maxTokens = 10) {
const list = Array.isArray(user.passkeyRecoveryTokens) ? user.passkeyRecoveryTokens : []
const now = Date.now()
const filtered = list.filter(t => t && t.tokenHash && t.expiresAt && new Date(t.expiresAt).getTime() > now)
// keep newest first
filtered.sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0))
user.passkeyRecoveryTokens = filtered.slice(0, maxTokens)
return user.passkeyRecoveryTokens
}

View File

@@ -1,7 +1,9 @@
const regChallenges = globalThis.__HTC_WEBAUTHN_REG_CHALLENGES__ || new Map()
const authChallenges = globalThis.__HTC_WEBAUTHN_AUTH_CHALLENGES__ || new Map()
const preRegChallenges = globalThis.__HTC_WEBAUTHN_PRE_REG__ || new Map()
globalThis.__HTC_WEBAUTHN_REG_CHALLENGES__ = regChallenges
globalThis.__HTC_WEBAUTHN_AUTH_CHALLENGES__ = authChallenges
globalThis.__HTC_WEBAUTHN_PRE_REG__ = preRegChallenges
function nowMs() {
return Date.now()
@@ -43,4 +45,18 @@ export function consumeAuthChallenge(challenge) {
return true
}
export function setPreRegistration(registrationId, payload, ttlMs = 10 * 60 * 1000) {
cleanup(preRegChallenges)
preRegChallenges.set(String(registrationId), { payload, expiresAt: nowMs() + ttlMs })
}
export function consumePreRegistration(registrationId) {
cleanup(preRegChallenges)
const key = String(registrationId)
const v = preRegChallenges.get(key)
if (!v) return null
preRegChallenges.delete(key)
return v.payload || null
}