1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
import type Authenticator from '../types/Authenticator';
import type User from '../types/User';
import { type RegistrationResponseJSON } from '@simplewebauthn/server/script/deps';
import { verifyRegistrationResponse as innerVerifyRegistrationResponse } from '@simplewebauthn/server';
import RELYING_PARTY from './_relyingParty';
import addAuthenticator from '../db/_addAuthenticator';
import addUser from '../db/_addUser';
import env from '../_env';
/**
* Verifies the registration response returned by @simplewebauthn/browser's
* startAuthentication() method. Includes checking the provided challenge
* matches the expected challenge, which should be the most recent challenge
* to be associated with the given user.
*
* If verification is successful, saves the new user and their new
* authenticator to the database.
*/
export default async function verifyRegistrationResponse(
newUser: User,
expectedChallenge: string,
registrationResponse: RegistrationResponseJSON,
): Promise<boolean> {
const verification = await innerVerifyRegistrationResponse({
response: registrationResponse,
expectedChallenge,
expectedOrigin: env.ENVIRONMENT === 'prod'
? 'https://scotsleidassocie.org'
: 'https://webauthn.scotsleidassocie.org',
expectedRPID: RELYING_PARTY.id,
}).catch((err) => {
throw new Error(`Registration response verification failed. ${err}`);
});
if (!verification.verified || !verification.registrationInfo) {
return false;
}
const authenticator: Authenticator = {
id: verification.registrationInfo.credentialID,
backedUp: verification.registrationInfo.credentialBackedUp,
counter: verification.registrationInfo.counter,
deviceType: verification.registrationInfo.credentialDeviceType,
publicKey: verification.registrationInfo.credentialPublicKey,
type: verification.registrationInfo.credentialType,
userId: newUser.id,
// transports doesn't seem to be implemented on the RegistrationInfo object
// provided by @simplewebauthn/server. We'll default to an empty array for
// now, and if the property is ever implemented, if we're lucky, we might
// get it for free. It's not clear to me how this property affects
// behaviour, but it's not required to be populated by the WebAuthn
// standard.
transports: verification.registrationInfo['transports'] ?? [],
};
await addUser(newUser);
await addAuthenticator(authenticator);
return true;
}
|