Skip to content

Commit

Permalink
feat(WebAuthn): add an option for user verification
Browse files Browse the repository at this point in the history
Allow configuring the user verification requirement. This is implemented
as a boolean that is translated into either UserVerificationRequirement
"required" or "discouraged". Do not default to "preferred" since its
behaviour is confusing at best.

Fixes #21
  • Loading branch information
nsatragno committed Jan 27, 2020
1 parent d8d63d2 commit 99626c6
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 9 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,21 @@ user.
- An object mapping, where the key is the name of a property from the
registration request to be included in the user object and the value is the
name of that property on the user object.
- `[store = MemoryAdapter]` - The storage interface for user objects. Defaults
- `[store = MemoryAdapter]` - the storage interface for user objects. Defaults
to an object in memory (for testing only).
- `[attestation = 'none']` - the [attestation conveyance preference](
https://w3c.github.io/webauthn/#enum-attestation-convey). Setting this to
anything other than `'none'` will require attestation and validate it.
- `[requireUserVerification = false]` - whether to require [user verification](
https://w3c.github.io/webauthn/#user-verification).
- `[credentialEndpoint = '/register']` - the path of the credential attestation
challenge endpoint.
- `[assertionEndpoint = '/login']` - the path of the challenge assertion
endpoint.
- `[challengeEndpoint = '/response']` - the path of the challenge response
endpoint.
- `[logoutEndpoint = '/logout']` - the path of the logout endpoint.
- `[enableLogging = true]` - Enable or disable logging to stdout.
- `[enableLogging = true]` - enable or disable logging to stdout.

**`webauthn.initialize()`**

Expand Down
27 changes: 20 additions & 7 deletions src/Webauthn.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Webauthn {
enableLogging: true,
attestation: Dictionaries.AttestationConveyancePreference.NONE,
authenticator: Dictionaries.AuthenticatorAttachment.CROSS_PLATFORM,
requireUserVerification: false,
}, options)

const attestationOptions = Object.values(Dictionaries.AttestationConveyancePreference)
Expand Down Expand Up @@ -118,6 +119,9 @@ class Webauthn {
if (this.config.enableLogging) console.log('STORED')

const attestation = new AttestationChallengeBuilder(this)
.setUserVerification(this.config.requireUserVerification ?
Dictionaries.UserVerificationRequirement.REQUIRED
: Dictionaries.UserVerificationRequirement.DISCOURAGED)
.setUserInfo(user)
.setAttestationType(this.config.attestation)
.setAuthenticator(this.config.authenticator)
Expand Down Expand Up @@ -261,7 +265,7 @@ class Webauthn {
}

} else if (response.authenticatorData !== undefined) {
result = Webauthn.verifyAuthenticatorAssertionResponse(response, user.authenticator, this.config.enableLogging)
result = this.verifyAuthenticatorAssertionResponse(response, user.authenticator)

if (result.verified) {
if (result.counter <= user.authenticator.counter)
Expand Down Expand Up @@ -359,8 +363,13 @@ class Webauthn {
const authrDataStruct = Webauthn.parseMakeCredAuthData(ctapMakeCredResp.authData);
if (this.config.enableLogging) console.log('AUTHR_DATA_STRUCT', authrDataStruct)

if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENTED
throw new Error('User was NOT presented durring authentication!');
if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENT
throw new Error('User was not present during authentication!');

if (this.config.requireUserVerification && !(authrDataStruct.flags & 0x04)) {// U2F_USER_VERIFIED
throw new Error('User was not verified during authentication!')
}


const publicKey = Webauthn.COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)

Expand Down Expand Up @@ -475,16 +484,20 @@ class Webauthn {
return response
}

static verifyAuthenticatorAssertionResponse (webauthnResponse, authr, enableLogging = false) {
verifyAuthenticatorAssertionResponse (webauthnResponse, authr) {
const authenticatorData = base64url.toBuffer(webauthnResponse.authenticatorData)

const response = { 'verified': false }
if (['fido-u2f'].includes(authr.fmt)) {
const authrDataStruct = Webauthn.parseGetAssertAuthData(authenticatorData)
if (enableLogging) console.log('AUTH_DATA', authrDataStruct)
if (this.config.enableLogging) console.log('AUTH_DATA', authrDataStruct)

if (!(authrDataStruct.flags & 0x01)) {// U2F_USER_PRESENT
throw new Error('User was not present during authentication!')
}

if (!(authrDataStruct.flags & 0x01)) {// U2F_USER_PRESENTED
throw new Error('User was not presented durring authentication!')
if (this.config.requireUserVerification && !(authrDataStruct.flags & 0x04)) {// U2F_USER_VERIFIED
throw new Error('User was not verified during authentication!')
}

const clientDataHash = Webauthn.hash(base64url.toBuffer(webauthnResponse.clientDataJSON))
Expand Down

0 comments on commit 99626c6

Please sign in to comment.