summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/server/src')
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.test.ts109
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.ts45
2 files changed, 147 insertions, 7 deletions
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
index ecd3c24..8a5b2fa 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
@@ -372,6 +372,115 @@ test('should return credential backup info', async () => {
expect(verification.authenticationInfo?.credentialBackedUp).toEqual(false);
});
+test('[FIDO Conformance] should verify if user verification is required and user was verified but not present', () => {
+ const actualData = esmParseAuthenticatorData.parseAuthenticatorData(
+ base64url.toBuffer(assertionResponse.response.authenticatorData),
+ );
+
+ mockParseAuthData.mockReturnValue({
+ ...actualData,
+ flags: {
+ up: false,
+ uv: true,
+ },
+ });
+
+ const verification = verifyAuthenticationResponse({
+ credential: assertionResponse,
+ expectedChallenge: assertionChallenge,
+ expectedOrigin: assertionOrigin,
+ expectedRPID: 'dev.dontneeda.pw',
+ authenticator: authenticator,
+ advancedFIDOConfig: {
+ userVerification: 'required',
+ }
+ });
+
+ expect(verification.verified).toEqual(true);
+});
+
+test('[FIDO Conformance] should verify if user verification is preferred and user was not verified or present', () => {
+ const actualData = esmParseAuthenticatorData.parseAuthenticatorData(
+ base64url.toBuffer(assertionResponse.response.authenticatorData),
+ );
+
+ mockParseAuthData.mockReturnValue({
+ ...actualData,
+ flags: {
+ up: false,
+ uv: false,
+ },
+ });
+
+ const verification = verifyAuthenticationResponse({
+ credential: assertionResponse,
+ expectedChallenge: assertionChallenge,
+ expectedOrigin: assertionOrigin,
+ expectedRPID: 'dev.dontneeda.pw',
+ authenticator: authenticator,
+ requireUserVerification: false,
+ advancedFIDOConfig: {
+ userVerification: 'preferred',
+ },
+ });
+
+ expect(verification.verified).toEqual(true);
+});
+
+test('[FIDO Conformance] should verify if user verification is discouraged and user was verified but not present', () => {
+ const actualData = esmParseAuthenticatorData.parseAuthenticatorData(
+ base64url.toBuffer(assertionResponse.response.authenticatorData),
+ );
+
+ mockParseAuthData.mockReturnValue({
+ ...actualData,
+ flags: {
+ up: false,
+ uv: true,
+ },
+ });
+
+ const verification = verifyAuthenticationResponse({
+ credential: assertionResponse,
+ expectedChallenge: assertionChallenge,
+ expectedOrigin: assertionOrigin,
+ expectedRPID: 'dev.dontneeda.pw',
+ authenticator: authenticator,
+ advancedFIDOConfig: {
+ userVerification: 'discouraged',
+ },
+ });
+
+ expect(verification.verified).toEqual(true);
+});
+
+test('[FIDO Conformance] should verify if user verification is discouraged and user was not verified or present', () => {
+ const actualData = esmParseAuthenticatorData.parseAuthenticatorData(
+ base64url.toBuffer(assertionResponse.response.authenticatorData),
+ );
+
+ mockParseAuthData.mockReturnValue({
+ ...actualData,
+ flags: {
+ up: false,
+ uv: false,
+ },
+ });
+
+ const verification = verifyAuthenticationResponse({
+ credential: assertionResponse,
+ expectedChallenge: assertionChallenge,
+ expectedOrigin: assertionOrigin,
+ expectedRPID: 'dev.dontneeda.pw',
+ authenticator: authenticator,
+ advancedFIDOConfig: {
+ userVerification: 'discouraged',
+ },
+ });
+
+ expect(verification.verified).toEqual(true);
+});
+
/**
* Assertion examples below
*/
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts
index e3dc770..f6a437e 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.ts
@@ -3,6 +3,7 @@ import {
AuthenticationCredentialJSON,
AuthenticatorDevice,
CredentialDeviceType,
+ UserVerificationRequirement,
} from '@simplewebauthn/typescript-types';
import { decodeClientDataJSON } from '../helpers/decodeClientDataJSON';
@@ -21,6 +22,9 @@ export type VerifyAuthenticationResponseOpts = {
expectedRPID: string | string[];
authenticator: AuthenticatorDevice;
requireUserVerification?: boolean;
+ advancedFIDOConfig?: {
+ userVerification?: UserVerificationRequirement,
+ },
};
/**
@@ -36,6 +40,11 @@ export type VerifyAuthenticationResponseOpts = {
* @param authenticator An internal {@link AuthenticatorDevice} matching the credential's ID
* @param requireUserVerification (Optional) Enforce user verification by the authenticator
* (via PIN, fingerprint, etc...)
+ * @param advancedFIDOConfig (Optional) Options for satisfying more stringent FIDO RP feature
+ * requirements
+ * @param advancedFIDOConfig.userVerification (Optional) Enable alternative rules for evaluating the
+ * User Presence and User Verified flags in authenticator data: UV (and UP) flags are optional
+ * unless this value is `"required"`
*/
export function verifyAuthenticationResponse(
options: VerifyAuthenticationResponseOpts,
@@ -47,6 +56,7 @@ export function verifyAuthenticationResponse(
expectedRPID,
authenticator,
requireUserVerification,
+ advancedFIDOConfig,
} = options;
const { id, rawId, type: credentialType, response } = credential;
@@ -155,14 +165,35 @@ export function verifyAuthenticationResponse(
}
}
- // WebAuthn only requires the user presence flag be true
- if (!flags.up) {
- throw new Error('User not present during authentication');
- }
+ if (advancedFIDOConfig !== undefined) {
+ const {
+ userVerification: fidoUserVerification,
+ } = advancedFIDOConfig;
+
+ /**
+ * Use FIDO Conformance-defined rules for verifying UP and UV flags
+ */
+ if (fidoUserVerification === 'required') {
+ // Require `flags.uv` be true (implies `flags.up` is true)
+ if (!flags.uv) {
+ throw new Error('User verification required, but user could not be verified');
+ }
+ } else if (fidoUserVerification === 'preferred' || fidoUserVerification === 'discouraged') {
+ // Ignore `flags.uv`
+ }
+ } else {
+ /**
+ * Use WebAuthn spec-defined rules for verifying UP and UV flags
+ */
+ // WebAuthn only requires the user presence flag be true
+ if (!flags.up) {
+ throw new Error('User not present during authentication');
+ }
- // Enforce user verification if required
- if (requireUserVerification && !flags.uv) {
- throw new Error('User verification required, but user could not be verified');
+ // Enforce user verification if required
+ if (requireUserVerification && !flags.uv) {
+ throw new Error('User verification required, but user could not be verified');
+ }
}
const clientDataHash = toHash(base64url.toBuffer(response.clientDataJSON));