Skip to content

Commit

Permalink
chore: remove unnecessary challenge cookie
Browse files Browse the repository at this point in the history
* chore: remove unnecessary challenge cookie

* docs: highly recommend using challenges
  • Loading branch information
Gerbuuun authored Sep 30, 2024
1 parent 2ed5e17 commit be2626b
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 66 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ export default defineWebAuthnAuthenticateEventHandler({
```

> [!IMPORTANT]
> By default, the webauthn event handlers will store the challenge in a short lived, encrypted session cookie. This is not recommended for applications that require strong security guarantees. On a secure connection (https) it is highly unlikely for this to cause problems. However, if the connection is not secure, there is a possibility of a man-in-the-middle attack. To prevent this, you should use a database or KV store to store the challenge instead. For this the `storeChallenge` and `getChallenge` functions are provided.
> Webauthn uses challenges to prevent replay attacks. By default, this module does not make use if this feature. If you want to use challenges (**which is highly recommended**), the `storeChallenge` and `getChallenge` functions are provided. An attempt ID is created and sent with each autentication request. You can use this ID to store the challenge in a database or KV store as shown in the example below.
> ```ts
> export default defineWebAuthnAuthenticateEventHandler({
Expand Down
21 changes: 11 additions & 10 deletions src/runtime/server/lib/webauthn/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import defu from 'defu'
import type { AuthenticationResponseJSON } from '@simplewebauthn/types'
import { getRandomValues } from 'uncrypto'
import { base64URLStringToBuffer, bufferToBase64URLString } from '@simplewebauthn/browser'
import { storeChallengeAsSession, getChallengeFromSession } from './utils'
import { useRuntimeConfig } from '#imports'
import type { WebAuthnAuthenticateEventHandlerOptions, WebAuthnCredential } from '#auth-utils'

Expand Down Expand Up @@ -39,15 +38,18 @@ export function defineWebAuthnAuthenticateEventHandler<T extends WebAuthnCredent
_config.allowCredentials = await allowCredentials(event, body.userName)
}

if (!storeChallenge) {
_config.challenge = ''
}

try {
if (!body.verify) {
const options = await generateAuthenticationOptions(_config as GenerateAuthenticationOptionsOpts)
const attemptId = bufferToBase64URLString(getRandomValues(new Uint8Array(32)))

if (storeChallenge)
if (storeChallenge) {
await storeChallenge(event, options.challenge, attemptId)
else
await storeChallengeAsSession(event, options.challenge, attemptId)
}

return {
requestOptions: options,
Expand All @@ -58,16 +60,15 @@ export function defineWebAuthnAuthenticateEventHandler<T extends WebAuthnCredent
if (!body.attemptId)
throw createError({ statusCode: 400 })

let challenge: string
if (getChallenge)
challenge = await getChallenge(event, body.attemptId)
else
challenge = await getChallengeFromSession(event, body.attemptId)
let expectedChallenge = ''
if (getChallenge) {
expectedChallenge = await getChallenge(event, body.attemptId)
}

const credential = await getCredential(event, body.response.id)
const verification = await verifyAuthenticationResponse({
response: body.response,
expectedChallenge: challenge,
expectedChallenge,
expectedOrigin: url.origin,
expectedRPID: url.hostname,
authenticator: {
Expand Down
19 changes: 10 additions & 9 deletions src/runtime/server/lib/webauthn/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import defu from 'defu'
import type { RegistrationResponseJSON } from '@simplewebauthn/types'
import { bufferToBase64URLString } from '@simplewebauthn/browser'
import { getRandomValues } from 'uncrypto'
import { storeChallengeAsSession, getChallengeFromSession } from './utils'
import { useRuntimeConfig } from '#imports'
import type { WebAuthnUser, WebAuthnRegisterEventHandlerOptions } from '#auth-utils'

Expand Down Expand Up @@ -52,16 +51,19 @@ export function defineWebAuthnRegisterEventHandler<T extends WebAuthnUser>({
},
} satisfies GenerateRegistrationOptionsOpts)

if (!storeChallenge) {
_config.challenge = ''
}

try {
if (!body.verify) {
const options = await generateRegistrationOptions(_config as GenerateRegistrationOptionsOpts)
const attemptId = bufferToBase64URLString(getRandomValues(new Uint8Array(32)))

// If the developer has stricter storage requirements, they can implement their own storeChallenge function to store the options in a database or KV store
if (storeChallenge)
await storeChallenge?.(event, options.challenge, attemptId)
else
await storeChallengeAsSession(event, options.challenge, attemptId)
if (storeChallenge) {
await storeChallenge(event, options.challenge, attemptId)
}

return {
creationOptions: options,
Expand All @@ -76,11 +78,10 @@ export function defineWebAuthnRegisterEventHandler<T extends WebAuthnUser>({
})
}

let expectedChallenge: string
if (getChallenge)
let expectedChallenge = ''
if (getChallenge) {
expectedChallenge = await getChallenge(event, body.attemptId)
else
expectedChallenge = await getChallengeFromSession(event, body.attemptId)
}

const verification = await verifyRegistrationResponse({
response: body.response,
Expand Down
46 changes: 0 additions & 46 deletions src/runtime/server/lib/webauthn/utils.ts

This file was deleted.

0 comments on commit be2626b

Please sign in to comment.