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:
rawId
response.authenticatorData
response.clientDataJSON
response.signature
response.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:
rawId
response.authenticatorData
response.clientDataJSON
response.signature
response.userHandle
const assertion = assertionFromJSON(JSON.parse(clientResponse))
// assertion.response.signature
Encode ArrayBuffer
or Uint8Array
values as Base64-URL strings and replace
"getter" functions with their result:
rawId
is Base64-URL encodedresponse.attestationObject
is Base64-URL encodedresponse.clientDataJSON
is Base64-URL encodedresponse.authenticatorData
is the Base64-URL encoded result fromresponse.getAuthenticatorData()
response.publicKey
is the Base64-URL encoded result fromresponse.getPublicKey()
response.publicKeyAlgorithm
is the result fromresponse.getPublicKeyAlgorithm()
response.transports
is 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:
rawId
response.attestationObject
response.clientDataJSON
response.authenticatorData
response.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