Skip to content

Commit cf30a19

Browse files
committed
Define reg response verification
1 parent 97d8b5e commit cf30a19

File tree

1 file changed

+93
-3
lines changed

1 file changed

+93
-3
lines changed

content/en/docs/implementation/backend.md

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,106 @@ These options should then be remembered since some values will be needed to veri
125125
response generated for these options:
126126

127127
```ts
128+
// The ID of the user's auth session that was created after the user logged in
129+
const sessionID = request.session.id;
130+
131+
// User data associated with the current auth session
132+
const currentUser: UserModel = await getUserData(sessionID);
133+
128134
const regOptions = await generateRegistrationOptions(currentUser);
129135

130-
await pseudocodeSaveCurrentRegistrationOptions(currentUser, regOptions);
136+
// Persist the options so we can reference values in them during verification
137+
await pseudocodeSaveRegistrationOptions(sessionID, regOptions);
131138
```
132139

133-
`regOptions` can then be transmitted to the frontend (docs coming soon) as JSON for the frontend
134-
to eventually pass them in to `navigator.credentials.create()`.
140+
`regOptions` can then be transmitted to the frontend as JSON for
141+
{{< link "./frontend.md" >}}the frontend to eventually pass in to
142+
`navigator.credentials.create()`.{{< /link >}}
135143

136144
## 2. Verify registration responses
137145

146+
Once the frontend has taken the registration options above and fed them into WebAuthn,
147+
and after the user succeeds in creating a passkey with their chosen passkey provider,
148+
then the subsequent registration response returned from the WebAuthn call
149+
will need to be sent to the backend for verification.
150+
151+
As JSON is a popular way to send the response back,
152+
the backend method "`verifyRegistrationResponse()`" should prepare to accept a value
153+
in the shape of [`RegistrationResponseJSON`](https://w3c.github.io/webauthn/#dictdef-registrationresponsejson).
154+
155+
```ts
156+
/**
157+
* Check that the WebAuthn registration data represents a well-formed passkey
158+
*/
159+
async function verifyRegistrationResponse(
160+
currentUser: UserModel,
161+
registrationOptions: PublicKeyCredentialCreationOptionsJSON,
162+
registrationResponse: RegistrationResponseJSON,
163+
): VerifiedRegistration {
164+
try {
165+
// TODO: Write basic attestation-less response verification here?
166+
} catch (error) {
167+
throw new Error(`Couldn't verify registration response`, { cause: error });
168+
}
169+
170+
return {
171+
passkey: {
172+
id: registrationResponse.id,
173+
publicKey: new Uint8Array([...]),
174+
counter: 0,
175+
backupEligible: true,
176+
backupStatus: true,
177+
transports: ['internal', 'hybrid'],
178+
},
179+
}
180+
}
181+
182+
type VerifiedRegistration = {
183+
passkey: {
184+
id: Base64URLString;
185+
publicKey: Uint8Array;
186+
counter: number;
187+
backupEligible: boolean;
188+
backupStatus: boolean;
189+
transports?: AuthenticatorTransport[];
190+
}
191+
};
192+
```
193+
194+
```ts
195+
// The ID of the user's auth session that was created after the user logged in
196+
const sessionID = request.session.id;
197+
198+
// User data associated with the current auth session
199+
const currentUser: UserModel = await getUserData(sessionID);
200+
201+
// Retrieve registration options for the current attempt to check for expected values
202+
const regOptions: PublicKeyCredentialCreationOptionsJSON =
203+
await pseudocodeRetrieveAndDeleteRegistrationOptions(sessionID);
204+
205+
let passkey;
206+
try {
207+
const verification = await verifyRegistrationResponse(currentUser, regOptions, regResponse);
208+
passkey = verification.passkey;
209+
// User successfully registered a passkey, continue
210+
} catch (err) {
211+
console.error(err);
212+
// Something went wrong, notify the user accordingly
213+
}
214+
```
215+
216+
Assuming successful creation, information about the newly-created passkey
217+
should then get stored as a `PasskeyModel` record in the database:
218+
219+
```ts
220+
/**
221+
* - Use `currentUser` for the foreign key needed for `PasskeyModel.user`
222+
* - Use `regOptions.user.id` for `PasskeyModel.webauthnUserID`
223+
* - Use the values in `passkey` to populate the remaining `PasskeyModel` fields
224+
*/
225+
await pseudocodeSaveNewPasskey(currentUser, regOptions, passkey);
226+
```
227+
138228
## 3. Generate authentication options
139229

140230
## 4. Verify authentication responses

0 commit comments

Comments
 (0)