Skip to content

Commit 70e1434

Browse files
committed
Use webauthn-json in getting started guide
1 parent 6508e29 commit 70e1434

File tree

1 file changed

+27
-100
lines changed

1 file changed

+27
-100
lines changed

README

Lines changed: 27 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,7 @@ The client side involves:
110110
passing the result from `RelyingParty.startRegistration(...)` or `.startAssertion(...)` as the argument.
111111
2. Encode the result of the successfully resolved promise and return it to the server.
112112
For this you need some way to encode `Uint8Array` values;
113-
this guide will assume use of link:https://github.com/beatgammit/base64-js[base64-js].
114-
However the built-in parser methods for
115-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/data/PublicKeyCredential.html[`PublicKeyCredential`]
116-
expect URL-safe Base64 encoding, so the below examples will include this as an additional step.
113+
this guide will use GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library.
117114

118115
Example code is given below.
119116
For more detailed example usage, see
@@ -163,6 +160,8 @@ A registration ceremony consists of 5 main steps:
163160
4. Validate the response using `RelyingParty.finishRegistration(...)`.
164161
5. Update your database using the `finishRegistration` output.
165162

163+
This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call.
164+
166165
First, generate registration parameters and send them to the client:
167166

168167
[source,java]
@@ -196,62 +195,23 @@ Now call the WebAuthn API on the client side:
196195

197196
[source,javascript]
198197
----------
199-
function base64urlToUint8array(base64Bytes) {
200-
const padding = '===='.substring(0, (4 - (base64Bytes.length % 4)) % 4);
201-
return base64js.toByteArray((base64Bytes + padding).replace(/\//g, "_").replace(/\+/g, "-"));
202-
}
203-
function uint8arrayToBase64url(bytes) {
204-
if (bytes instanceof Uint8Array) {
205-
return base64js.fromByteArray(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
206-
} else {
207-
return uint8arrayToBase64url(new Uint8Array(bytes));
208-
}
209-
}
210-
211-
fetch(/* ... */) // Make the call that returns the credentialCreateJson above
212-
.then(credentialCreateJson => ({ // Decode byte arrays from base64url
213-
publicKey: {
214-
...credentialCreateJson.publicKey,
215-
216-
challenge: base64urlToUint8array(credentialCreateJson.publicKey.challenge),
217-
user: {
218-
...credentialCreateJson.publicKey.user,
219-
id: base64urlToUint8array(credentialCreateJson.publicKey.user.id),
220-
},
221-
excludeCredentials: credentialCreateJson.publicKey.excludeCredentials.map(credential => ({
222-
...credential,
223-
id: base64urlToUint8array(credential.id),
224-
})),
225-
226-
// Warning: Extension inputs could also contain binary data that needs encoding
227-
extensions: credentialCreateJson.publicKey.extensions,
228-
},
229-
}))
230-
.then(credentialCreateOptions => // Call WebAuthn ceremony
231-
navigator.credentials.create(credentialCreateOptions))
232-
.then(publicKeyCredential => ({ // Encode PublicKeyCredential for transport to server (example)
233-
type: publicKeyCredential.type,
234-
id: publicKeyCredential.id,
235-
response: {
236-
attestationObject: uint8arrayToBase64url(publicKeyCredential.response.attestationObject),
237-
clientDataJSON: uint8arrayToBase64url(publicKeyCredential.response.clientDataJSON),
238-
239-
// Attempt to read transports, but recover gracefully if not supported by the browser
240-
transports: publicKeyCredential.response.getTransports && publicKeyCredential.response.getTransports() || [],
241-
},
242-
243-
// Warning: Client extension results could also contain binary data that needs encoding
244-
clientExtensionResults: publicKeyCredential.getClientExtensionResults(),
245-
}))
246-
.then(encodedResult =>
247-
fetch(/* ... */)); // Return encoded PublicKeyCredential to server
198+
import * as webauthnJson from "@github/webauthn-json";
199+
200+
// Make the call that returns the credentialCreateJson above
201+
const credentialCreateOptions = await fetch(/* ... */).then(resp => resp.json());
202+
203+
// Call WebAuthn ceremony using webauthn-json wrapper
204+
const publicKeyCredential = await webauthnJson.create(credentialCreateOptions);
205+
206+
// Return encoded PublicKeyCredential to server
207+
fetch(/* ... */, { body: JSON.stringify(publicKeyCredential) });
248208
----------
249209

250210
Validate the response on the server side:
251211

252212
[source,java]
253213
----------
254-
String publicKeyCredentialJson = /* ... */; // encodedResult from client
214+
String publicKeyCredentialJson = /* ... */; // publicKeyCredential from client
255215
PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> pkc =
256216
PublicKeyCredential.parseRegistrationResponseJson(publicKeyCredentialJson);
257217

@@ -295,6 +255,8 @@ Like registration ceremonies, an authentication ceremony consists of 5 main step
295255
4. Validate the response using `RelyingParty.finishAssertion(...)`.
296256
5. Update your database using the `finishAssertion` output, and act upon the result (for example, grant login access).
297257

258+
This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call.
259+
298260
First, generate authentication parameters and send them to the client:
299261

300262
[source,java]
@@ -313,58 +275,23 @@ Now call the WebAuthn API on the client side:
313275

314276
[source,javascript]
315277
----------
316-
function base64urlToUint8array(base64Bytes) {
317-
const padding = '===='.substring(0, (4 - (base64Bytes.length % 4)) % 4);
318-
return base64js.toByteArray((base64Bytes + padding).replace(/\//g, "_").replace(/\+/g, "-"));
319-
}
320-
function uint8arrayToBase64url(bytes) {
321-
if (bytes instanceof Uint8Array) {
322-
return base64js.fromByteArray(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
323-
} else {
324-
return uint8arrayToBase64url(new Uint8Array(bytes));
325-
}
326-
}
327-
328-
fetch(/* ... */) // Make the call that returns the credentialGetJson above
329-
.then(credentialGetJson => ({ // Decode byte arrays from base64url
330-
publicKey: {
331-
...credentialGetJson.publicKey,
332-
allowCredentials: credentialGetJson.publicKey.allowCredentials
333-
&& credentialGetJson.publicKey.allowCredentials.map(credential => ({
334-
...credential,
335-
id: base64urlToUint8array(credential.id),
336-
})),
337-
338-
challenge: base64urlToUint8array(credentialGetJson.publicKey.challenge),
339-
340-
// Warning: Extension inputs could also contain binary data that needs encoding
341-
extensions: credentialGetJson.publicKey.extensions,
342-
},
343-
}))
344-
.then(credentialGetOptions => // Call WebAuthn ceremony
345-
navigator.credentials.get(credentialGetOptions))
346-
.then(publicKeyCredential => ({ // Encode PublicKeyCredential for transport to server (example)
347-
type: publicKeyCredential.type,
348-
id: publicKeyCredential.id,
349-
response: {
350-
authenticatorData: uint8arrayToBase64url(publicKeyCredential.response.authenticatorData),
351-
clientDataJSON: uint8arrayToBase64url(publicKeyCredential.response.clientDataJSON),
352-
signature: uint8arrayToBase64url(publicKeyCredential.response.signature),
353-
userHandle: publicKeyCredential.response.userHandle && uint8arrayToBase64url(publicKeyCredential.response.userHandle),
354-
},
355-
356-
// Warning: Client extension results could also contain binary data that needs encoding
357-
clientExtensionResults: publicKeyCredential.getClientExtensionResults(),
358-
}))
359-
.then(encodedResult =>
360-
fetch(/* ... */)); // Return encoded PublicKeyCredential to server
278+
import * as webauthnJson from "@github/webauthn-json";
279+
280+
// Make the call that returns the credentialGetJson above
281+
const credentialGetOptions = await fetch(/* ... */).then(resp => resp.json());
282+
283+
// Call WebAuthn ceremony using webauthn-json wrapper
284+
const publicKeyCredential = await webauthnJson.get(credentialGetOptions);
285+
286+
// Return encoded PublicKeyCredential to server
287+
fetch(/* ... */, { body: JSON.stringify(publicKeyCredential) });
361288
----------
362289

363290
Validate the response on the server side:
364291

365292
[source,java]
366293
----------
367-
String publicKeyCredentialJson = /* ... */; // encodedResult from client
294+
String publicKeyCredentialJson = /* ... */; // publicKeyCredential from client
368295
PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> pkc =
369296
PublicKeyCredential.parseAssertionResponseJson(publicKeyCredentialJson);
370297

0 commit comments

Comments
 (0)