WebAuthn ceremony helpers for passkeys.
Three layers, ported from auth-js (lib/base64url.ts, lib/webauthn.ts,
lib/webauthn.dom.ts):
bytes->base64url /
base64url->bytes over vectors of bytes.deserialize-credential-creation-options /
deserialize-credential-request-options turn the server's W3C JSON
options (base64url strings) into the JS objects
navigator.credentials.create/get expect;
serialize-credential-creation-response /
serialize-credential-request-response turn a credential back into
the JSON-ready map the server verifies.register-passkey and sign-in-with-passkey run the full
start -> prompt -> verify round-trip against
supabase.auth.passkey.Unlike auth-js this port always uses the manual JSON marshalling (the
W3C parseCreationOptionsFromJSON / toJSON fast paths are not taken),
so behaviour is identical across browsers. Detailed WebAuthn error
identification (auth-js webauthn.errors.ts) is also out of scope:
ceremony failures become :cognitect.anomalies/incorrect anomalies
carrying the browser error name under :webauthn/error-name.
Every ClojureScript-only function returns an anomaly on the JVM instead: passkeys require ClojureScript and a WebAuthn-capable browser.
TESTING GAP: the WebAuthn ceremony itself (navigator.credentials.create
/ .get) cannot run under node or jsdom. The codec and marshalling are
unit-tested on both platforms (the orchestration wiring against a
hand-rolled fake navigator.credentials); a real WebAuthn ceremony needs
manual verification in a browser against a Supabase project with
passkeys enabled.
WebAuthn ceremony helpers for passkeys.
Three layers, ported from auth-js (`lib/base64url.ts`, `lib/webauthn.ts`,
`lib/webauthn.dom.ts`):
* Base64-URL codec — pure, cross-platform: `bytes->base64url` /
`base64url->bytes` over vectors of bytes.
* DOM <-> JSON marshalling — ClojureScript-only:
`deserialize-credential-creation-options` /
`deserialize-credential-request-options` turn the server's W3C JSON
options (base64url strings) into the JS objects
`navigator.credentials.create`/`get` expect;
`serialize-credential-creation-response` /
`serialize-credential-request-response` turn a credential back into
the JSON-ready map the server verifies.
* High-level ceremony orchestration — ClojureScript-only:
`register-passkey` and `sign-in-with-passkey` run the full
start -> prompt -> verify round-trip against
`supabase.auth.passkey`.
Unlike auth-js this port always uses the manual JSON marshalling (the
W3C `parseCreationOptionsFromJSON` / `toJSON` fast paths are not taken),
so behaviour is identical across browsers. Detailed WebAuthn error
identification (auth-js `webauthn.errors.ts`) is also out of scope:
ceremony failures become `:cognitect.anomalies/incorrect` anomalies
carrying the browser error name under `:webauthn/error-name`.
Every ClojureScript-only function returns an anomaly on the JVM instead:
passkeys require ClojureScript and a WebAuthn-capable browser.
TESTING GAP: the WebAuthn ceremony itself (`navigator.credentials.create`
/ `.get`) cannot run under node or jsdom. The codec and marshalling are
unit-tested on both platforms (the orchestration wiring against a
hand-rolled fake `navigator.credentials`); a real WebAuthn ceremony needs
manual verification in a browser against a Supabase project with
passkeys enabled.(base64url->bytes s)Decodes a Base64-URL string into a vector of bytes (ints 0-255).
Whitespace and = padding are ignored; any other invalid character
throws. Port of auth-js base64UrlToUint8Array.
(base64url->bytes "aGVsbG8") ; => [104 101 108 108 111]
Decodes a Base64-URL string into a vector of bytes (ints 0-255).
Whitespace and `=` padding are ignored; any other invalid character
throws. Port of auth-js `base64UrlToUint8Array`.
(base64url->bytes "aGVsbG8") ; => [104 101 108 108 111](browser-supports-webauthn?)True when the runtime exposes the WebAuthn API: PublicKeyCredential
and navigator.credentials with callable create/get. Always false on
the JVM.
True when the runtime exposes the WebAuthn API: `PublicKeyCredential` and `navigator.credentials` with callable `create`/`get`. Always false on the JVM.
(bytes->base64url bytes)Encodes a sequence of bytes (ints 0-255) as a Base64-URL string, without
padding. Port of auth-js bytesToBase64URL.
(bytes->base64url [104 101 108 108 111]) ; => "aGVsbG8"
Encodes a sequence of bytes (ints 0-255) as a Base64-URL string, without
padding. Port of auth-js `bytesToBase64URL`.
(bytes->base64url [104 101 108 108 111]) ; => "aGVsbG8"(cancel-ceremony)Manually cancels the current WebAuthn ceremony, if any. Useful when the user dismisses the prompt.
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Manually cancels the current WebAuthn ceremony, if any. Useful when the user dismisses the prompt. JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Manually cancels the current WebAuthn ceremony, if any. Useful when the user dismisses the prompt.
Manually cancels the current WebAuthn ceremony, if any. Useful when the user dismisses the prompt.
(create-abort-signal)Cancels any in-flight WebAuthn ceremony and returns a fresh
AbortSignal to pass to register-passkey / sign-in-with-passkey
(or to navigator.credentials directly).
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Cancels any in-flight WebAuthn ceremony and returns a fresh `AbortSignal` to pass to `register-passkey` / `sign-in-with-passkey` (or to `navigator.credentials` directly). JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Cancels any in-flight WebAuthn ceremony and returns a fresh
AbortSignal to pass to register-passkey / sign-in-with-passkey
(or to navigator.credentials directly).
Cancels any in-flight WebAuthn ceremony and returns a fresh `AbortSignal` to pass to `register-passkey` / `sign-in-with-passkey` (or to `navigator.credentials` directly).
(create-credential _opts)Runs navigator.credentials.create with opts (:public-key from
deserialize-credential-creation-options, optional :signal from
create-abort-signal).
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Runs `navigator.credentials.create` with `opts` (`:public-key` from `deserialize-credential-creation-options`, optional `:signal` from `create-abort-signal`). JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(create-credential opts)Runs navigator.credentials.create with opts (:public-key from
deserialize-credential-creation-options, optional :signal from
create-abort-signal). Returns a js/Promise of the registration
credential, or of an anomaly.
Runs `navigator.credentials.create` with `opts` (`:public-key` from `deserialize-credential-creation-options`, optional `:signal` from `create-abort-signal`). Returns a `js/Promise` of the registration credential, or of an anomaly.
(deserialize-credential-creation-options _options)Converts the server's credential creation options (the :options map
from passkey/start-registration, a W3C
PublicKeyCredentialCreationOptionsJSON with base64url-encoded binary
fields) into the JS publicKey object for
navigator.credentials.create.
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Converts the server's credential creation options (the `:options` map from `passkey/start-registration`, a W3C `PublicKeyCredentialCreationOptionsJSON` with base64url-encoded binary fields) into the JS `publicKey` object for `navigator.credentials.create`. JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(deserialize-credential-creation-options options)Converts the server's credential creation options (the :options map
from passkey/start-registration, a W3C
PublicKeyCredentialCreationOptionsJSON with base64url-encoded binary
fields) into the JS publicKey object for
navigator.credentials.create.
The challenge, user.id and each excludeCredentials[].id are
decoded to ArrayBuffers; everything else passes through unchanged.
Browser options are otherwise accepted as-is.
Throws on a malformed input (missing options map or invalid Base64-URL).
Converts the server's credential creation options (the `:options` map from `passkey/start-registration`, a W3C `PublicKeyCredentialCreationOptionsJSON` with base64url-encoded binary fields) into the JS `publicKey` object for `navigator.credentials.create`. The `challenge`, `user.id` and each `excludeCredentials[].id` are decoded to ArrayBuffers; everything else passes through unchanged. Browser options are otherwise accepted as-is. Throws on a malformed input (missing options map or invalid Base64-URL).
(deserialize-credential-request-options _options)Converts the server's credential request options (the :options map
from passkey/start-authentication, a W3C
PublicKeyCredentialRequestOptionsJSON) into the JS publicKey
object for navigator.credentials.get.
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Converts the server's credential request options (the `:options` map from `passkey/start-authentication`, a W3C `PublicKeyCredentialRequestOptionsJSON`) into the JS `publicKey` object for `navigator.credentials.get`. JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(deserialize-credential-request-options options)Converts the server's credential request options (the :options map
from passkey/start-authentication, a W3C
PublicKeyCredentialRequestOptionsJSON) into the JS publicKey
object for navigator.credentials.get.
The challenge and each allowCredentials[].id are decoded to
ArrayBuffers; everything else passes through unchanged. Throws on a
malformed input.
Converts the server's credential request options (the `:options` map from `passkey/start-authentication`, a W3C `PublicKeyCredentialRequestOptionsJSON`) into the JS `publicKey` object for `navigator.credentials.get`. The `challenge` and each `allowCredentials[].id` are decoded to ArrayBuffers; everything else passes through unchanged. Throws on a malformed input.
(get-credential _opts)Runs navigator.credentials.get with opts (:public-key from
deserialize-credential-request-options, optional :signal and
:mediation — e.g. "conditional" for passkey autofill).
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Runs `navigator.credentials.get` with `opts` (`:public-key` from `deserialize-credential-request-options`, optional `:signal` and `:mediation` — e.g. `"conditional"` for passkey autofill). JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(get-credential opts)Runs navigator.credentials.get with opts (:public-key from
deserialize-credential-request-options, optional :signal and
:mediation — e.g. "conditional" for passkey autofill). Returns a
js/Promise of the authentication credential, or of an anomaly.
Runs `navigator.credentials.get` with `opts` (`:public-key` from `deserialize-credential-request-options`, optional `:signal` and `:mediation` — e.g. `"conditional"` for passkey autofill). Returns a `js/Promise` of the authentication credential, or of an anomaly.
(register-passkey _client _access-token)(register-passkey _client _access-token _opts)Registers a passkey for the signed-in user, running the full WebAuthn
ceremony: passkey/start-registration, then
navigator.credentials.create, then passkey/verify-registration.
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Registers a passkey for the signed-in user, running the full WebAuthn ceremony: `passkey/start-registration`, then `navigator.credentials.create`, then `passkey/verify-registration`. JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(register-passkey client access-token)(register-passkey client access-token {:keys [signal]})Registers a passkey for the signed-in user identified by
access-token, running the full WebAuthn ceremony:
passkey/start-registration — challenge + creation optionsnavigator.credentials.create — the user picks/verifies their
authenticatorpasskey/verify-registration — server verifies the credentialopts (optional): :signal — an AbortSignal; when omitted a signal
from create-abort-signal is used so concurrent ceremonies cancel
each other.
Returns a js/Promise of the verify response (the registered passkey
metadata) or of an anomaly: invalid client, unsupported browser,
ceremony failure or server rejection.
(register-passkey client "<access-token>")
NOTE: the ceremony itself cannot be exercised under node/jsdom; verify manually in a browser (see the namespace docstring).
Registers a passkey for the signed-in user identified by
`access-token`, running the full WebAuthn ceremony:
1. `passkey/start-registration` — challenge + creation options
2. `navigator.credentials.create` — the user picks/verifies their
authenticator
3. `passkey/verify-registration` — server verifies the credential
`opts` (optional): `:signal` — an `AbortSignal`; when omitted a signal
from `create-abort-signal` is used so concurrent ceremonies cancel
each other.
Returns a `js/Promise` of the verify response (the registered passkey
metadata) or of an anomaly: invalid client, unsupported browser,
ceremony failure or server rejection.
## Example
(register-passkey client "<access-token>")
NOTE: the ceremony itself cannot be exercised under node/jsdom; verify
manually in a browser (see the namespace docstring).(serialize-credential-creation-response _credential)Serializes the registration credential from navigator.credentials.create
into the JSON-ready map (RegistrationResponseJSON) the server
expects.
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Serializes the registration credential from `navigator.credentials.create` into the JSON-ready map (`RegistrationResponseJSON`) the server expects. JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(serialize-credential-creation-response credential)Serializes the registration credential from navigator.credentials.create
into the JSON-ready map (RegistrationResponseJSON) the server
expects: binary response fields are Base64-URL encoded, rawId mirrors
id, extension outputs are converted to a plain map. Pass the result
to passkey/verify-registration.
Serializes the registration credential from `navigator.credentials.create` into the JSON-ready map (`RegistrationResponseJSON`) the server expects: binary response fields are Base64-URL encoded, `rawId` mirrors `id`, extension outputs are converted to a plain map. Pass the result to `passkey/verify-registration`.
(serialize-credential-request-response _credential)Serializes the authentication credential from navigator.credentials.get
into the JSON-ready map (AuthenticationResponseJSON) the server
expects.
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Serializes the authentication credential from `navigator.credentials.get` into the JSON-ready map (`AuthenticationResponseJSON`) the server expects. JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(serialize-credential-request-response credential)Serializes the authentication credential from navigator.credentials.get
into the JSON-ready map (AuthenticationResponseJSON) the server
expects: binary response fields are Base64-URL encoded (:userHandle
omitted when absent), rawId mirrors id, extension outputs are
converted to a plain map. Pass the result to
passkey/verify-authentication.
Serializes the authentication credential from `navigator.credentials.get` into the JSON-ready map (`AuthenticationResponseJSON`) the server expects: binary response fields are Base64-URL encoded (`:userHandle` omitted when absent), `rawId` mirrors `id`, extension outputs are converted to a plain map. Pass the result to `passkey/verify-authentication`.
(sign-in-with-passkey _client)(sign-in-with-passkey _client _opts)Signs in with a passkey, running the full WebAuthn ceremony:
passkey/start-authentication, then navigator.credentials.get,
then passkey/verify-authentication (which returns a session).
JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
Signs in with a passkey, running the full WebAuthn ceremony: `passkey/start-authentication`, then `navigator.credentials.get`, then `passkey/verify-authentication` (which returns a session). JVM: returns an anomaly — passkeys require ClojureScript and a WebAuthn-capable browser.
(sign-in-with-passkey client)(sign-in-with-passkey client {:keys [captcha-token signal mediation]})Signs in with a passkey, running the full WebAuthn ceremony:
passkey/start-authentication — challenge + request optionsnavigator.credentials.get — the user picks/verifies their
passkeypasskey/verify-authentication — server verifies the assertion
and returns a sessionopts (optional):
:captcha-token — CAPTCHA verification token:signal — an AbortSignal; when omitted a signal from
create-abort-signal is used:mediation — forwarded to navigator.credentials.get; use
"conditional" for passkey autofill (the page needs an input
with autocomplete="username webauthn")Returns a js/Promise of the verify response (whose body carries the
fresh session) or of an anomaly.
(sign-in-with-passkey client)
(sign-in-with-passkey client {:mediation "conditional"})
NOTE: the ceremony itself cannot be exercised under node/jsdom; verify manually in a browser (see the namespace docstring).
Signs in with a passkey, running the full WebAuthn ceremony:
1. `passkey/start-authentication` — challenge + request options
2. `navigator.credentials.get` — the user picks/verifies their
passkey
3. `passkey/verify-authentication` — server verifies the assertion
and returns a session
`opts` (optional):
* `:captcha-token` — CAPTCHA verification token
* `:signal` — an `AbortSignal`; when omitted a signal from
`create-abort-signal` is used
* `:mediation` — forwarded to `navigator.credentials.get`; use
`"conditional"` for passkey autofill (the page needs an input
with `autocomplete="username webauthn"`)
Returns a `js/Promise` of the verify response (whose body carries the
fresh session) or of an anomaly.
## Example
(sign-in-with-passkey client)
(sign-in-with-passkey client {:mediation "conditional"})
NOTE: the ceremony itself cannot be exercised under node/jsdom; verify
manually in a browser (see the namespace docstring).cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |