Skip to content

Commit

Permalink
Fix WebAuthn when event body is base64 encoded (like when deploying t…
Browse files Browse the repository at this point in the history
…o Vercel) (#6757)

* Use existing this.params instead of parsing this.event.body again

* Adds test covering base64 encoded body for WebAuthn requests
  • Loading branch information
cannikin authored and jtoar committed Nov 8, 2022
1 parent e2f84fa commit 67f0038
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 14 deletions.
33 changes: 19 additions & 14 deletions packages/api/src/functions/dbAuth/DbAuthHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import type {
VerifiedRegistrationResponse,
VerifiedAuthenticationResponse,
} from '@simplewebauthn/server'
import type {
AuthenticationCredentialJSON,
RegistrationCredentialJSON,
} from '@simplewebauthn/typescript-types'
import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda'
import base64url from 'base64url'
import CryptoJS from 'crypto-js'
Expand Down Expand Up @@ -229,12 +233,13 @@ export type AuthMethodNames =
| 'webAuthnAuthOptions'
| 'webAuthnAuthenticate'

type Params = {
username?: string
password?: string
method: AuthMethodNames
[key: string]: any
}
type Params = AuthenticationCredentialJSON &
RegistrationCredentialJSON & {
username?: string
password?: string
method: AuthMethodNames
[key: string]: any
}

/**
* To use in src/lib/auth#getCurrentUser
Expand Down Expand Up @@ -705,9 +710,8 @@ export class DbAuthHandler<TUser extends Record<string | number, any>> {
throw new DbAuthError.WebAuthnError('WebAuthn is not enabled')
}

const jsonBody = JSON.parse(this.event.body as string)
const credential = await this.dbCredentialAccessor.findFirst({
where: { id: jsonBody.rawId },
where: { id: this.params.rawId },
})

if (!credential) {
Expand All @@ -724,7 +728,7 @@ export class DbAuthHandler<TUser extends Record<string | number, any>> {
let verification: VerifiedAuthenticationResponse
try {
const opts: VerifyAuthenticationResponseOpts = {
credential: jsonBody,
credential: this.params,
expectedChallenge: user[this.options.authFields.challenge as string],
expectedOrigin: webAuthnOptions.origin,
expectedRPID: webAuthnOptions.domain,
Expand Down Expand Up @@ -772,7 +776,7 @@ export class DbAuthHandler<TUser extends Record<string | number, any>> {
// get the regular `login` cookies
const [, loginHeaders] = this._loginResponse(user)
const cookies = [
this._webAuthnCookie(jsonBody.rawId, this.webAuthnExpiresDate),
this._webAuthnCookie(this.params.rawId, this.webAuthnExpiresDate),
loginHeaders['set-cookie'],
].flat()

Expand Down Expand Up @@ -897,12 +901,11 @@ export class DbAuthHandler<TUser extends Record<string | number, any>> {
}

const user = await this._getCurrentUser()
const jsonBody = JSON.parse(this.event.body as string)

let verification: VerifiedRegistrationResponse
try {
const options: VerifyRegistrationResponseOpts = {
credential: jsonBody,
credential: this.params,
expectedChallenge: user[this.options.authFields.challenge as string],
expectedOrigin: this.options.webAuthn.origin,
expectedRPID: this.options.webAuthn.domain,
Expand Down Expand Up @@ -935,8 +938,10 @@ export class DbAuthHandler<TUser extends Record<string | number, any>> {
user[this.options.authFields.id],
[this.options.webAuthn.credentialFields.publicKey]:
credentialPublicKey,
[this.options.webAuthn.credentialFields.transports]:
jsonBody.transports ? JSON.stringify(jsonBody.transports) : null,
[this.options.webAuthn.credentialFields.transports]: this.params
.transports
? JSON.stringify(this.params.transports)
: null,
[this.options.webAuthn.credentialFields.counter]: counter,
},
})
Expand Down
29 changes: 29 additions & 0 deletions packages/api/src/functions/dbAuth/__tests__/DbAuthHandler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,35 @@ describe('dbAuth', () => {
expect(credential.transports).toEqual('["internal"]')
expect(credential.counter).toEqual(0)
})

it('works if event body is base64 encoded', async () => {
const user = await createDbUser({
webAuthnChallenge: 'HuGPrQqK7f53NLwMZMst_DL9Dig2BBivDYWWpawIPVM',
})
event = {
headers: {
'Content-Type': 'application/json',
cookie: encryptToCookie(
JSON.stringify({ id: user.id }) + ';' + 'token'
),
},
body: Buffer.from(
`{"method":"webAuthnRegister","id":"GqjZOuYYppObBDeVknbrcBLkaa9imS5EJJwtCV740asUz24sdAmGFg","rawId":"GqjZOuYYppObBDeVknbrcBLkaa9imS5EJJwtCV740asUz24sdAmGFg","response":{"attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVisSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAK3OAAI1vMYKZIsLJfHwVQMAKBqo2TrmGKaTmwQ3lZJ263AS5GmvYpkuRCScLQle-NGrFM9uLHQJhhalAQIDJiABIVggGIipTQt-gcoDPOpW6Zje_Av9C0-jWb2R2PBmXJJL-c8iWCC76wxo3uzG8cPqb0A8Vij-dqMbrEytEHjuFOtiQ2dt8A","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSHVHUHJRcUs3ZjUzTkx3TVpNc3RfREw5RGlnMkJCaXZEWVdXcGF3SVBWTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODkxMCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9"},"type":"public-key","clientExtensionResults":{},"transports":["internal"]}`,
'utf8'
),
}
const dbAuth = new DbAuthHandler(event, context, options)

await dbAuth.webAuthnRegister()

const credential = db.userCredential.findFirst({
where: { userId: user.id },
})

expect(credential.id).toEqual(
'GqjZOuYYppObBDeVknbrcBLkaa9imS5EJJwtCV740asUz24sdAmGFg'
)
})
})

describe('_validateOptions', () => {
Expand Down

0 comments on commit 67f0038

Please sign in to comment.