A set of functions for converting to and from JSON compatible structures, useful for passing these values between clients and servers as JSON strings.
import {
parseCreationOptionsFromJSON,
attestationToJSON,
} from 'https://esm.sh/gh/tjconcept/[email protected]'
function signUp() {
const credential = navigator.credentials.create({
publicKey: parseCreationOptionsFromJSON(JSON.parse(serverWebauthnCreate())),
})
join(credential, (credential) =>
serverWebauthnStore(JSON.stringify(attestationToJSON(credential))),
)
}creationOptionsToJSON(options) → json
parseCreationOptionsFromJSON(json) → options
requestOptionsToJSON(options) → json
parseRequestOptionsFromJSON(json) → options
assertionToJSON(credential) → json
assertionFromJSON(json) → credential
attestationToJSON(credential) → json
attestationFromJSON(json) → credential
pubKeyCredParams = [
{alg: -8, type: 'public-key'}, // EdDSA
{alg: -7, type: 'public-key'}, // ES256
{alg: -257, type: 'public-key'}, // RS256
]
A convenient list of common algorithms that can be filtered or sorted to specific needs.
const supported = [-8, -257]
navigator.credentials.create({
publicKey: {
authenticatorSelection: {residentKey: true},
challenge: Uint8Array.from([1, 2, 3, 4]),
pubKeyCredParams: pubKeyCredParams.filter((p) => supported.includes(p.alg)),
rp: 'example.com',
user: {
name: 'Foo',
displayName: 'Bar',
id: Uint8Array.from([5, 6, 7, 8]),
},
},
})Ponyfill of
PublicKeyCredential.parseCreationOptionsFromJSON().
Decode Base64-URL strings in challenge and user.id to Uint8Array values.
navigator.credentials.create({
publicKey: parseCreationOptionsFromJSON(JSON.parse(serverResponse)),
})Partner (reverse) of
PublicKeyCredential.parseCreationOptionsFromJSON().
Encode ArrayBuffer or Uint8Array values in challenge and user.id as
Base64-URL strings.
const serverResponse = creationOptionsToJSON({
authenticatorSelection: {residentKey: true},
challenge: Uint8Array.from([1, 2, 3, 4]),
pubKeyCredParams: [{alg: -8, type: 'public-key'}],
rp: 'example.com',
user: {
name: 'Foo',
displayName: 'Bar',
id: Uint8Array.from([5, 6, 7, 8]),
},
})
sendToClient(JSON.stringify(serverResponse))Ponyfill of
PublicKeyCredential.parseRequestOptionsFromJSON().
Decode Base64-URL strings in challenge and allowCredentials[].id to
Uint8Array values.
navigator.credentials.get({
publicKey: parseRequestOptionsFromJSON(JSON.parse(serverResponse)),
})Partner (reverse) of
PublicKeyCredential.parseRequestOptionsFromJSON().
Encode ArrayBuffer or Uint8Array values in challenge and
allowCredentials[].id as Base64-URL strings.
const serverResponse = creationOptionsToJSON({
allowCredentials: [
{
id: Uint8Array.from([5, 6, 7, 8]),
transports: ['ble', 'nfc'],
type: 'public-key',
},
],
challenge: Uint8Array.from([1, 2, 3, 4]),
extensions: {
foo: 'bar',
},
hints: ['hybrid'],
rpId: 'example.com',
timeout: 12000,
userVerification: 'preferred',
})
sendToClient(JSON.stringify(serverResponse))Encode ArrayBuffer or Uint8Array values in the following paths as Base64-URL
strings:
rawIdresponse.authenticatorDataresponse.clientDataJSONresponse.signatureresponse.userHandle
navigator.credentials
.get({
publicKey: parseRequestOptionsFromJSON(JSON.parse(serverResponse)),
})
.then((credential) =>
sendToServer(JSON.stringify(assertionToJSON(credential))),
)Decode Base64-URL strings in the following paths to Uint8Array values:
rawIdresponse.authenticatorDataresponse.clientDataJSONresponse.signatureresponse.userHandle
const assertion = assertionFromJSON(JSON.parse(clientResponse))
// assertion.response.signatureEncode ArrayBuffer or Uint8Array values as Base64-URL strings and replace
"getter" functions with their result:
rawIdis Base64-URL encodedresponse.attestationObjectis Base64-URL encodedresponse.clientDataJSONis Base64-URL encodedresponse.authenticatorDatais the Base64-URL encoded result fromresponse.getAuthenticatorData()response.publicKeyis the Base64-URL encoded result fromresponse.getPublicKey()response.publicKeyAlgorithmis the result fromresponse.getPublicKeyAlgorithm()response.transportsis the result fromresponse.getTransports()
navigator.credentials
.create({
publicKey: parseCreationOptionsFromJSON(JSON.parse(serverResponse)),
})
.then((credential) => sendToServer(attestationToJSON(credential)))Decode Base64-URL strings in the following paths to Uint8Array values:
rawIdresponse.attestationObjectresponse.clientDataJSONresponse.authenticatorDataresponse.publicKey
Note that this function is not an exact mirroring of
attestationToJSON(PublicKeyCredential) as it will not reproduce the "getter"
functions but replace them with a corresponding key-value pair, e.g.
getAuthenticatorData: () → String is replaced by authenticatorData: String.
However, it is a mirroring of attestationToJSON(json), such that
attestationToJSON(attestationFromJSON(json)) will work. This simply means it
works as expected and can be used on both ends to transfer or store attestations
(e.g. using JSON.stringify/parse).
const attestation = attestationFromJSON(JSON.parse(clientResponse))
if (attestation.response.publicKeyAlgorithm !== -8) {
throw new Error('Algorithm not supported')
}
const key = crypto.subtle.importKey(
'spki',
attestation.response.publicKey,
{name: 'Ed25519'},
extractable,
usages,
)deno --allow-env --allow-read test.js