Skip to content

Commit be2626b

Browse files
authored
chore: remove unnecessary challenge cookie
* chore: remove unnecessary challenge cookie * docs: highly recommend using challenges
1 parent 2ed5e17 commit be2626b

File tree

4 files changed

+22
-66
lines changed

4 files changed

+22
-66
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ export default defineWebAuthnAuthenticateEventHandler({
423423
```
424424

425425
> [!IMPORTANT]
426-
> 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.
426+
> 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.
427427
428428
> ```ts
429429
> export default defineWebAuthnAuthenticateEventHandler({

src/runtime/server/lib/webauthn/authenticate.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import defu from 'defu'
55
import type { AuthenticationResponseJSON } from '@simplewebauthn/types'
66
import { getRandomValues } from 'uncrypto'
77
import { base64URLStringToBuffer, bufferToBase64URLString } from '@simplewebauthn/browser'
8-
import { storeChallengeAsSession, getChallengeFromSession } from './utils'
98
import { useRuntimeConfig } from '#imports'
109
import type { WebAuthnAuthenticateEventHandlerOptions, WebAuthnCredential } from '#auth-utils'
1110

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

41+
if (!storeChallenge) {
42+
_config.challenge = ''
43+
}
44+
4245
try {
4346
if (!body.verify) {
4447
const options = await generateAuthenticationOptions(_config as GenerateAuthenticationOptionsOpts)
4548
const attemptId = bufferToBase64URLString(getRandomValues(new Uint8Array(32)))
4649

47-
if (storeChallenge)
50+
if (storeChallenge) {
4851
await storeChallenge(event, options.challenge, attemptId)
49-
else
50-
await storeChallengeAsSession(event, options.challenge, attemptId)
52+
}
5153

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

61-
let challenge: string
62-
if (getChallenge)
63-
challenge = await getChallenge(event, body.attemptId)
64-
else
65-
challenge = await getChallengeFromSession(event, body.attemptId)
63+
let expectedChallenge = ''
64+
if (getChallenge) {
65+
expectedChallenge = await getChallenge(event, body.attemptId)
66+
}
6667

6768
const credential = await getCredential(event, body.response.id)
6869
const verification = await verifyAuthenticationResponse({
6970
response: body.response,
70-
expectedChallenge: challenge,
71+
expectedChallenge,
7172
expectedOrigin: url.origin,
7273
expectedRPID: url.hostname,
7374
authenticator: {

src/runtime/server/lib/webauthn/register.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import defu from 'defu'
66
import type { RegistrationResponseJSON } from '@simplewebauthn/types'
77
import { bufferToBase64URLString } from '@simplewebauthn/browser'
88
import { getRandomValues } from 'uncrypto'
9-
import { storeChallengeAsSession, getChallengeFromSession } from './utils'
109
import { useRuntimeConfig } from '#imports'
1110
import type { WebAuthnUser, WebAuthnRegisterEventHandlerOptions } from '#auth-utils'
1211

@@ -52,16 +51,19 @@ export function defineWebAuthnRegisterEventHandler<T extends WebAuthnUser>({
5251
},
5352
} satisfies GenerateRegistrationOptionsOpts)
5453

54+
if (!storeChallenge) {
55+
_config.challenge = ''
56+
}
57+
5558
try {
5659
if (!body.verify) {
5760
const options = await generateRegistrationOptions(_config as GenerateRegistrationOptionsOpts)
5861
const attemptId = bufferToBase64URLString(getRandomValues(new Uint8Array(32)))
5962

6063
// If the developer has stricter storage requirements, they can implement their own storeChallenge function to store the options in a database or KV store
61-
if (storeChallenge)
62-
await storeChallenge?.(event, options.challenge, attemptId)
63-
else
64-
await storeChallengeAsSession(event, options.challenge, attemptId)
64+
if (storeChallenge) {
65+
await storeChallenge(event, options.challenge, attemptId)
66+
}
6567

6668
return {
6769
creationOptions: options,
@@ -76,11 +78,10 @@ export function defineWebAuthnRegisterEventHandler<T extends WebAuthnUser>({
7678
})
7779
}
7880

79-
let expectedChallenge: string
80-
if (getChallenge)
81+
let expectedChallenge = ''
82+
if (getChallenge) {
8183
expectedChallenge = await getChallenge(event, body.attemptId)
82-
else
83-
expectedChallenge = await getChallengeFromSession(event, body.attemptId)
84+
}
8485

8586
const verification = await verifyRegistrationResponse({
8687
response: body.response,

src/runtime/server/lib/webauthn/utils.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)