Skip to content

Commit f25661c

Browse files
committed
Write some words for reg options generation
1 parent 128a8ab commit f25661c

File tree

1 file changed

+128
-7
lines changed

1 file changed

+128
-7
lines changed
Lines changed: 128 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,148 @@
11
---
2-
title: "Back End Requirements"
2+
title: "Backend Requirements"
33
description: "Guidance on server-side handling of passkeys registration and authentication"
44
date: 2024-08-13T12:00:00.000Z
55
---
66

7-
The back end drives WebAuthn ceremonies through four primary responsibilities:
7+
The backend drives WebAuthn ceremonies through four primary responsibilities:
88

99
1. Generate registration options
1010
2. Verify registration responses
1111
3. Generate authentication options
1212
4. Verify authentication responses
1313

14-
The guidance below is intended to identify best practices to fulfill these responsibilities and
15-
securely incorporate passkeys-based authentication into your website.
14+
The guidance below identifies best practices to fulfill these responsibilities and securely incorporate passkeys-based authentication into your website.
15+
16+
{{< alert type="info" >}}
17+
**Please note that this is general guidance; it does not account for any one specific server implementation.**
18+
It is intended to be a launching point. Care should be taken as you consider how best to adapt this guidance for your particular site.
19+
{{< /alert >}}
20+
21+
## Data Structures
22+
23+
A site that uses passkeys should be prepared to store the following "`PasskeyModel`" database model:
24+
25+
```ts
26+
type PasskeyModel = {
27+
// SQL: Store as `TEXT`. Index this column
28+
id: Base64URLString;
29+
// SQL: Store raw bytes as `BYTEA`/`BLOB`/etc...
30+
publicKey: Uint8Array;
31+
// SQL: Foreign Key to an instance of your internal user model
32+
user: UserModel;
33+
// SQL: Store as `TEXT`. Index this column. A UNIQUE constraint on
34+
// (webAuthnUserID + user) also achieves maximum user privacy
35+
webauthnUserID: Base64URLString;
36+
// SQL: `INT` or whatever similar type is supported
37+
counter: number;
38+
// SQL: `BOOL` or whatever similar type is supported
39+
backupEligible: boolean;
40+
// SQL: `BOOL` or whatever similar type is supported
41+
backupStatus: boolean;
42+
// SQL: `VARCHAR(255)` and store string array as a CSV string
43+
// Ex: ble,cable,hybrid,internal,nfc,smart-card,usb
44+
transports?: AuthenticatorTransport[];
45+
};
46+
```
47+
48+
The association between an instance of this `PasskeyModel` and an instance of
49+
your site's "`UserModel`" should be established in a way that makes sense
50+
for your site's database architecture.
51+
For the purposes of the documentation below, the following `UserModel` is assumed:
52+
53+
```ts
54+
type UserModel = {
55+
// No assumption is made of how users are assigned IDs within the database
56+
id: any;
57+
// An email address, username, or other identifiable information
58+
username: string;
59+
};
60+
```
1661

17-
**Please note that this guidance is not applicable to any specific server implementation.**
18-
It is intended to be a launching point; care should be taken as you consider how best to adapt this
19-
guidance for your particular site.
2062

2163
## 1. Generate registration options
2264

65+
Registration options define several values that authenticators need to help associate a passkey with your site. Some of the most important values include the following:
66+
67+
- **RP ID:** A scoped domain name of the site on which the passkey should be available. The browser will require this to be part of the effective domain of the site hosting the registration ceremony.
68+
- **User ID:** A unique, random identifier for the user account that the passkey will be registered to.
69+
This **should not be personally-identifying information** like an email address/username/`UserModel.username`/etc...!
70+
71+
Some of the options that get passed in to WebAuthn's `navigator.credentials.create()` call need to be of type `Uint8Array`. Unfortunately byte arrays do not serialize well into a JSON representation, making it tricky to get these values from the backend to the frontend.
72+
73+
It is suggested, then, that the "`generateRegistrationOptions()`" method returns a value of type [`PublicKeyCredentialCreationOptionsJSON`](https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson) that uses **Base64URL** encoding to encode such byte arrays to a value that is easier to transmit to the frontend as JSON:
74+
75+
```ts
76+
/**
77+
* Generate passkey registration options
78+
*/
79+
async function generateRegistrationOptions(
80+
currentUser: UserModel,
81+
): PublicKeyCredentialCreationOptionsJSON {
82+
// A domain name for your site (e.g. "passkeys.dev")
83+
const rpID: string = process.env.RP_ID;
84+
// A human-readable name for your website (e.g. "Passkeys Developer Resources")
85+
const rpName: string = process.env.RP_NAME;
86+
87+
// Generate one-time-use random bytes for the authenticator to sign
88+
const challenge: Uint8Array = await pseudocodeGenerateChallenge(currentUser);
89+
// Generate or retrieve a pseudonymous, WebAuthn-specific user identifier as random bytes
90+
const userID: Uint8Array = await pseudocodeGetWebAuthnUserID(currentUser);
91+
// Get a list of the user's currently registered passkeys to prevent re-registration
92+
const userCurrentPasskeys: PasskeyModel[] = await pseudocodeGetCurrentPasskeys(currentUser);
93+
94+
return {
95+
rp: {
96+
id: rpID,
97+
name: rpName,
98+
},
99+
user: {
100+
id: pseudocodeBytesToBase64URLString(userID),
101+
name: currentUser.username,
102+
displayName: '',
103+
},
104+
challenge: pseudocodeBytesToBase64URLString(challenge),
105+
pubKeyCredParams: [
106+
{ alg: -8, type: 'public-key' },
107+
{ alg: -7, type: 'public-key' },
108+
{ alg: -257, type: 'public-key' },
109+
],
110+
attestation: 'direct',
111+
excludeCredentials: userCurrentPasskeys.map((passkey) => ({
112+
id: passkey.id,
113+
transports: passkey.transports,
114+
type: 'public-key',
115+
})),
116+
authenticatorSelection: {
117+
residentKey: 'preferred',
118+
userVerification: 'preferred',
119+
},
120+
};
121+
}
122+
```
123+
124+
These options should then be remembered since some values will be needed to verify the registration
125+
response generated for these options:
126+
127+
```ts
128+
const regOptions = await generateRegistrationOptions(currentUser);
129+
130+
await pseudocodeSaveCurrentRegistrationOptions(currentUser, regOptions);
131+
```
132+
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()`.
135+
23136
## 2. Verify registration responses
24137

25138
## 3. Generate authentication options
26139

27140
## 4. Verify authentication responses
141+
142+
## Third-Party Libraries
143+
144+
Many third-party libraries exist to simplify the job of generating options and verifying responses.
145+
These libraries can help reduce your maintenance burden of supporting passkeys by virtue of
146+
their ability to keep up with WebAuthn API changes. Head over to
147+
{{< link "../tools-libraries/libraries" >}}Tools &amp; Libraries > Libraries{{< /link >}}
148+
for a list of maintained libraries in your backend's language.

0 commit comments

Comments
 (0)