Add CORS support for Cross-Device Authentication in passkey handling
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 45s
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 45s
Enhance authentication options in the server API by adding CORS headers to support cross-device authentication. Implement handling for preflight OPTIONS requests and increase timeout for registration and authentication processes to 5 minutes, improving user experience and compatibility across devices.
This commit is contained in:
@@ -2,13 +2,28 @@ import { generateAuthenticationOptions } from '@simplewebauthn/server'
|
|||||||
import { getWebAuthnConfig } from '../../../utils/webauthn-config.js'
|
import { getWebAuthnConfig } from '../../../utils/webauthn-config.js'
|
||||||
import { setAuthChallenge } from '../../../utils/webauthn-challenges.js'
|
import { setAuthChallenge } from '../../../utils/webauthn-challenges.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (_event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
// CORS-Header für Cross-Device Authentication
|
||||||
|
const origin = getHeader(event, 'origin')
|
||||||
|
if (origin) {
|
||||||
|
setHeader(event, 'Access-Control-Allow-Origin', origin)
|
||||||
|
setHeader(event, 'Access-Control-Allow-Credentials', 'true')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getMethod(event) === 'OPTIONS') {
|
||||||
|
return { success: true }
|
||||||
|
}
|
||||||
|
|
||||||
const { rpId } = getWebAuthnConfig()
|
const { rpId } = getWebAuthnConfig()
|
||||||
|
|
||||||
// Username-less / discoverable credentials: allowCredentials absichtlich leer
|
// Username-less / discoverable credentials: allowCredentials absichtlich leer
|
||||||
const options = await generateAuthenticationOptions({
|
const options = await generateAuthenticationOptions({
|
||||||
rpID: rpId,
|
rpID: rpId,
|
||||||
userVerification: 'preferred'
|
userVerification: 'preferred',
|
||||||
|
// Timeout erhöhen für Cross-Device (Standard: 60s, hier: 5 Minuten)
|
||||||
|
timeout: 300000
|
||||||
})
|
})
|
||||||
|
|
||||||
setAuthChallenge(options.challenge)
|
setAuthChallenge(options.challenge)
|
||||||
|
|||||||
@@ -18,6 +18,19 @@ function findUserByCredentialId(users, credentialId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
// CORS-Header für Cross-Device Authentication
|
||||||
|
const origin = getHeader(event, 'origin')
|
||||||
|
if (origin) {
|
||||||
|
setHeader(event, 'Access-Control-Allow-Origin', origin)
|
||||||
|
setHeader(event, 'Access-Control-Allow-Credentials', 'true')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getMethod(event) === 'OPTIONS') {
|
||||||
|
return { success: true }
|
||||||
|
}
|
||||||
|
|
||||||
const ip = getClientIp(event)
|
const ip = getClientIp(event)
|
||||||
const body = await readBody(event)
|
const body = await readBody(event)
|
||||||
const response = body?.credential
|
const response = body?.credential
|
||||||
|
|||||||
@@ -6,6 +6,19 @@ import { toBase64Url } from '../../../utils/webauthn-encoding.js'
|
|||||||
import { writeAuditLog } from '../../../utils/audit-log.js'
|
import { writeAuditLog } from '../../../utils/audit-log.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
// CORS-Header für Cross-Device Authentication
|
||||||
|
const origin = getHeader(event, 'origin')
|
||||||
|
if (origin) {
|
||||||
|
setHeader(event, 'Access-Control-Allow-Origin', origin)
|
||||||
|
setHeader(event, 'Access-Control-Allow-Credentials', 'true')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getMethod(event) === 'OPTIONS') {
|
||||||
|
return { success: true }
|
||||||
|
}
|
||||||
|
|
||||||
const token = getCookie(event, 'auth_token')
|
const token = getCookie(event, 'auth_token')
|
||||||
const user = token ? await getUserFromToken(token) : null
|
const user = token ? await getUserFromToken(token) : null
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,19 @@ import { setRegistrationChallenge } from '../../../utils/webauthn-challenges.js'
|
|||||||
import { writeAuditLog } from '../../../utils/audit-log.js'
|
import { writeAuditLog } from '../../../utils/audit-log.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
// CORS-Header für Cross-Device Authentication
|
||||||
|
const origin = getHeader(event, 'origin')
|
||||||
|
if (origin) {
|
||||||
|
setHeader(event, 'Access-Control-Allow-Origin', origin)
|
||||||
|
setHeader(event, 'Access-Control-Allow-Credentials', 'true')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||||||
|
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getMethod(event) === 'OPTIONS') {
|
||||||
|
return { success: true }
|
||||||
|
}
|
||||||
|
|
||||||
const token = getCookie(event, 'auth_token')
|
const token = getCookie(event, 'auth_token')
|
||||||
const user = token ? await getUserFromToken(token) : null
|
const user = token ? await getUserFromToken(token) : null
|
||||||
|
|
||||||
@@ -17,7 +30,16 @@ export default defineEventHandler(async (event) => {
|
|||||||
throw createError({ statusCode: 403, statusMessage: 'Keine Berechtigung' })
|
throw createError({ statusCode: 403, statusMessage: 'Keine Berechtigung' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { rpId, rpName } = getWebAuthnConfig()
|
const { rpId, rpName, origin: webauthnOrigin } = getWebAuthnConfig()
|
||||||
|
|
||||||
|
// Debug: Log für Cross-Device Troubleshooting
|
||||||
|
const requestOrigin = getHeader(event, 'origin')
|
||||||
|
console.log('[WebAuthn Registration]', {
|
||||||
|
rpId,
|
||||||
|
webauthnOrigin,
|
||||||
|
requestOrigin,
|
||||||
|
userAgent: getHeader(event, 'user-agent')
|
||||||
|
})
|
||||||
|
|
||||||
const existing = Array.isArray(user.passkeys) ? user.passkeys : []
|
const existing = Array.isArray(user.passkeys) ? user.passkeys : []
|
||||||
const excludeCredentials = existing
|
const excludeCredentials = existing
|
||||||
@@ -39,8 +61,11 @@ export default defineEventHandler(async (event) => {
|
|||||||
authenticatorSelection: {
|
authenticatorSelection: {
|
||||||
residentKey: 'preferred',
|
residentKey: 'preferred',
|
||||||
userVerification: 'preferred'
|
userVerification: 'preferred'
|
||||||
|
// authenticatorAttachment weglassen = beide Typen erlauben (platform + cross-platform)
|
||||||
},
|
},
|
||||||
excludeCredentials
|
excludeCredentials,
|
||||||
|
// Timeout erhöhen für Cross-Device (Standard: 60s, hier: 5 Minuten)
|
||||||
|
timeout: 300000
|
||||||
})
|
})
|
||||||
|
|
||||||
setRegistrationChallenge(user.id, options.challenge)
|
setRegistrationChallenge(user.id, options.challenge)
|
||||||
|
|||||||
Reference in New Issue
Block a user