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.ts4
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.ts8
-rw-r--r--packages/server/src/helpers/matchExpectedRPID.ts13
-rw-r--r--packages/server/src/registration/verifyRegistrationResponse.test.ts25
-rw-r--r--packages/server/src/registration/verifyRegistrationResponse.ts10
5 files changed, 54 insertions, 6 deletions
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
index 30eb9d1..5a760e4 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
@@ -44,6 +44,8 @@ test('should return authenticator info after verification', async () => {
expect(verification.authenticationInfo.newCounter).toEqual(144);
expect(verification.authenticationInfo.credentialID).toEqual(authenticator.credentialID);
+ expect(verification.authenticationInfo?.origin).toEqual(assertionOrigin);
+ expect(verification.authenticationInfo?.rpID).toEqual('dev.dontneeda.pw');
});
test('should throw when response challenge is not expected value', async () => {
@@ -224,6 +226,7 @@ test('should support multiple possible origins', async () => {
});
expect(verification.verified).toEqual(true);
+ expect(verification.authenticationInfo?.origin).toEqual(assertionOrigin);
});
test('should throw an error if origin not in list of expected origins', async () => {
@@ -249,6 +252,7 @@ test('should support multiple possible RP IDs', async () => {
});
expect(verification.verified).toEqual(true);
+ expect(verification.authenticationInfo?.rpID).toEqual('dev.dontneeda.pw');
});
test('should throw an error if RP ID not in list of possible RP IDs', async () => {
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts
index d95bca5..c9f23ca 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.ts
@@ -154,7 +154,7 @@ export async function verifyAuthenticationResponse(
expectedRPIDs = expectedRPID;
}
- await matchExpectedRPID(rpIdHash, expectedRPIDs);
+ const matchedRPID = await matchExpectedRPID(rpIdHash, expectedRPIDs);
if (advancedFIDOConfig !== undefined) {
const { userVerification: fidoUserVerification } = advancedFIDOConfig;
@@ -215,6 +215,8 @@ export async function verifyAuthenticationResponse(
credentialDeviceType,
credentialBackedUp,
authenticatorExtensionResults: extensionsData,
+ origin: clientDataJSON.origin,
+ rpID: matchedRPID,
},
};
@@ -236,6 +238,8 @@ export async function verifyAuthenticationResponse(
* @param authenticationInfo.credentialBackedUp Whether or not the multi-device credential has been
* backed up. Always `false` for single-device credentials. **Should be kept in a DB for later
* reference!**
+ * @param authenticationInfo.origin The origin of the website that the authentication occurred on
+ * @param authenticationInfo.rpID The RP ID that the authentication occurred on
* @param authenticationInfo?.authenticatorExtensionResults The authenticator extensions returned
* by the browser
*/
@@ -247,6 +251,8 @@ export type VerifiedAuthenticationResponse = {
userVerified: boolean;
credentialDeviceType: CredentialDeviceType;
credentialBackedUp: boolean;
+ origin: string;
+ rpID: string;
authenticatorExtensionResults?: AuthenticationExtensionsAuthenticatorOutputs;
};
};
diff --git a/packages/server/src/helpers/matchExpectedRPID.ts b/packages/server/src/helpers/matchExpectedRPID.ts
index be49fc2..c08c223 100644
--- a/packages/server/src/helpers/matchExpectedRPID.ts
+++ b/packages/server/src/helpers/matchExpectedRPID.ts
@@ -2,19 +2,22 @@ import { toHash } from './toHash';
import { isoUint8Array } from './iso';
/**
- * Go through each expected RP ID and try to find one that matches. Raises an Error if no
+ * Go through each expected RP ID and try to find one that matches. Returns the unhashed RP ID
+ * that matched the hash in the response.
+ *
+ * Raises an `UnexpectedRPIDHash` error if no match is found
*/
export async function matchExpectedRPID(
rpIDHash: Uint8Array,
expectedRPIDs: string[],
-): Promise<void> {
+): Promise<string> {
try {
- await Promise.any(
+ const matchedRPID = await Promise.any<string>(
expectedRPIDs.map(expected => {
return new Promise((resolve, reject) => {
toHash(isoUint8Array.fromASCIIString(expected)).then(expectedRPIDHash => {
if (isoUint8Array.areEqual(rpIDHash, expectedRPIDHash)) {
- resolve(true);
+ resolve(expected);
} else {
reject();
}
@@ -22,6 +25,8 @@ export async function matchExpectedRPID(
});
}),
);
+
+ return matchedRPID;
} catch (err) {
const _err = err as Error;
diff --git a/packages/server/src/registration/verifyRegistrationResponse.test.ts b/packages/server/src/registration/verifyRegistrationResponse.test.ts
index 9fd8a96..7f89857 100644
--- a/packages/server/src/registration/verifyRegistrationResponse.test.ts
+++ b/packages/server/src/registration/verifyRegistrationResponse.test.ts
@@ -69,6 +69,8 @@ test('should verify FIDO U2F attestation', async () => {
expect(verification.registrationInfo?.attestationObject).toEqual(
isoBase64URL.toBuffer(attestationFIDOU2F.response.attestationObject),
);
+ expect(verification.registrationInfo?.origin).toEqual('https://dev.dontneeda.pw');
+ expect(verification.registrationInfo?.rpID).toEqual('dev.dontneeda.pw');
});
test('should verify Packed (EC2) attestation', async () => {
@@ -140,6 +142,7 @@ test('should verify None attestation', async () => {
'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY',
),
);
+ expect(verification.registrationInfo?.origin).toEqual('https://dev.dontneeda.pw');
});
test('should verify None attestation w/RSA public key', async () => {
@@ -174,6 +177,8 @@ test('should verify None attestation w/RSA public key', async () => {
expect(verification.registrationInfo?.credentialID).toEqual(
isoBase64URL.toBuffer('kGXv4RJWLeXRw8Yf3T22K3Gq_GGeDv9OKYmAHLm0Ylo'),
);
+ expect(verification.registrationInfo?.origin).toEqual('https://dev.dontneeda.pw');
+ expect(verification.registrationInfo?.rpID).toEqual('dev.dontneeda.pw');
});
test('should throw when response challenge is not expected value', async () => {
@@ -415,6 +420,8 @@ test('should validate TPM RSA response (SHA256)', async () => {
expect(verification.registrationInfo?.credentialID).toEqual(
isoBase64URL.toBuffer('lGkWHPe88VpnNYgVBxzon_MRR9-gmgODveQ16uM_bPM'),
);
+ expect(verification.registrationInfo?.origin).toEqual('https://dev.dontneeda.pw');
+ expect(verification.registrationInfo?.rpID).toEqual('dev.dontneeda.pw');
});
test('should validate TPM RSA response (SHA1)', async () => {
@@ -450,6 +457,8 @@ test('should validate TPM RSA response (SHA1)', async () => {
expect(verification.registrationInfo?.credentialID).toEqual(
isoBase64URL.toBuffer('oELnad0f6-g2BtzEn_78iLNoubarlq0xFtOtAMXnflU'),
);
+ expect(verification.registrationInfo?.origin).toEqual('https://dev.dontneeda.pw');
+ expect(verification.registrationInfo?.rpID).toEqual('dev.dontneeda.pw');
});
test('should validate Android-Key response', async () => {
@@ -485,6 +494,8 @@ test('should validate Android-Key response', async () => {
expect(verification.registrationInfo?.credentialID).toEqual(
isoBase64URL.toBuffer('PPa1spYTB680cQq5q6qBtFuPLLdG1FQ73EastkT8n0o'),
);
+ expect(verification.registrationInfo?.origin).toEqual('https://dev.dontneeda.pw');
+ expect(verification.registrationInfo?.rpID).toEqual('dev.dontneeda.pw');
});
test('should support multiple possible origins', async () => {
@@ -496,6 +507,20 @@ test('should support multiple possible origins', async () => {
});
expect(verification.verified).toBe(true);
+ expect(verification.registrationInfo?.origin).toEqual('https://dev.dontneeda.pw');
+ expect(verification.registrationInfo?.rpID).toEqual('dev.dontneeda.pw');
+});
+
+test('should not set RPID in registrationInfo when not expected', async () => {
+ const verification = await verifyRegistrationResponse({
+ response: attestationNone,
+ expectedChallenge: attestationNoneChallenge,
+ expectedOrigin: 'https://dev.dontneeda.pw',
+ expectedRPID: undefined,
+ });
+
+ expect(verification.verified).toBe(true);
+ expect(verification.registrationInfo?.rpID).toBeUndefined();
});
test('should throw an error if origin not in list of expected origins', async () => {
diff --git a/packages/server/src/registration/verifyRegistrationResponse.ts b/packages/server/src/registration/verifyRegistrationResponse.ts
index 2546813..d33cdea 100644
--- a/packages/server/src/registration/verifyRegistrationResponse.ts
+++ b/packages/server/src/registration/verifyRegistrationResponse.ts
@@ -141,6 +141,7 @@ export async function verifyRegistrationResponse(
parsedAuthData;
// Make sure the response's RP ID is ours
+ let matchedRPID: string | undefined;
if (expectedRPID) {
let expectedRPIDs: string[] = [];
if (typeof expectedRPID === 'string') {
@@ -149,7 +150,7 @@ export async function verifyRegistrationResponse(
expectedRPIDs = expectedRPID;
}
- await matchExpectedRPID(rpIdHash, expectedRPIDs);
+ matchedRPID = await matchExpectedRPID(rpIdHash, expectedRPIDs);
}
// Make sure someone was physically present
@@ -246,6 +247,8 @@ export async function verifyRegistrationResponse(
userVerified: flags.uv,
credentialDeviceType,
credentialBackedUp,
+ origin: clientDataJSON.origin,
+ rpID: matchedRPID,
authenticatorExtensionResults: extensionsData,
};
}
@@ -273,6 +276,9 @@ export async function verifyRegistrationResponse(
* @param registrationInfo.credentialBackedUp Whether or not the multi-device credential has been
* backed up. Always `false` for single-device credentials. **Should be kept in a DB for later
* reference!**
+ * @param registrationInfo.origin The origin of the website that the registration occurred on
+ * @param registrationInfo?.rpID The RP ID that the registration occurred on, if one or more were
+ * specified in the registration options
* @param registrationInfo?.authenticatorExtensionResults The authenticator extensions returned
* by the browser
*/
@@ -289,6 +295,8 @@ export type VerifiedRegistrationResponse = {
userVerified: boolean;
credentialDeviceType: CredentialDeviceType;
credentialBackedUp: boolean;
+ origin: string;
+ rpID?: string;
authenticatorExtensionResults?: AuthenticationExtensionsAuthenticatorOutputs;
};
};