SDK reference (JavaScript)¶
Every call in omxus-credential.mjs, the browser SDK for Omxus identities
(passkey, phrase, three inputs). How it remembers people, and what to tell
them, is on Remembering people. This page is the
call-by-call reference.
For ordinary accounts on your own page (email or mobile, username, password,
passkey), use omxus-accounts.mjs, which is live: see
Your own sign-in page. For an iOS app, see
SDK (iOS).
Not reachable from outside yet
omxus-credential.mjs is not served at auth.omxus.com/sdk/ (404,
checked 30 Oct 2026) and @omxus/auth is not on npm. The provider's
/auth/challenge and /auth/verify send no CORS headers, so a page on
another origin cannot finish a sign-in against auth.omxus.com. Today,
sign people in with OIDC or
Signed in everywhere. This SDK works on a page
served by the provider itself, such as
your own provider.
Load it¶
The SDK needs the Omxus engine first. The engine publishes its functions on
globalThis; without it every call throws
engine not loaded: import engine/engine.bundle.mjs first. The engine is live
at https://auth.omxus.com/engine/engine.bundle.mjs (200, CORS open).
import 'https://auth.omxus.com/engine/engine.bundle.mjs';
import { createCredentialStore } from '/sdk/omxus-credential.mjs';
const cred = createCredentialStore({ issuer: location.origin });
const session = await cred.signIn();
createCredentialStore(options)¶
Returns the store: every call below. All options are optional except issuer.
| Option | Default | What it is |
|---|---|---|
issuer |
(none) | The provider's origin. It goes inside the signed bytes, and the provider rejects any other (Origin mismatch: signed for <origin>). |
basePath |
'' |
Prefix for /auth/challenge and /auth/verify. Empty means the page's own origin. |
rpName |
'OMXUS' |
What the passkey prompt calls the site. |
rpId |
location.hostname |
Passkey scope. |
storage |
localStorage |
Where the vault record lives (key omxus.vault.v1). |
passkeyTimeoutMs |
60000 |
How long a biometric prompt may go unanswered before the call gives up. |
fetchImpl |
fetch |
For tests. |
wrapKeyStore |
IndexedDB key | Tier 2's key. For tests. |
webauthn |
the browser's | {available, create, get}. For tests: Node has no authenticator. |
The session¶
Every call that signs in resolves to the provider's answer plus the address:
addressis this origin's key, derived per site. It is not the same ascred.address().tiercomes withsignInWithPasskey(),createPasskeyAndSignIn()andsignIn(), except whensignIn()unseals an existing vault (step 3 below).- The provider sets its session cookie on this answer.
returnTois where the provider was asked to send the person,/if nowhere.
Signing in¶
signIn()¶
One press, nothing typed. The call most sites need.
- Ask for the passkey the vault names, or any passkey this origin has.
- None, or the prompt cancelled or unanswered: make one. This call is the press.
- The passkey has no PRF but a sealed vault exists: unseal it and sign in.
- No PRF and no vault: generate a phrase, sign in with it, seal it. The
person never sees it, and it lives on this device alone (
tier: 'device'or'passkey').
signInWithPasskey()¶
Tier 0 only. Never makes a passkey.
| Resolves to | When |
|---|---|
a session, tier: 'passkey-seed' |
A passkey answered with PRF. The vault is written if it was missing. |
'none' |
No passkey here, the prompt was cancelled, or it went unanswered. Offer "Create a passkey". |
null |
No platform authenticator, or the passkey has no PRF. Fall back to a phrase or the three boxes. |
createPasskeyAndSignIn()¶
Make a passkey and sign in with it. Call it only from a "Create a passkey"
press, after signInWithPasskey() answered 'none'. Resolves to a session
with tier: 'passkey-seed', or null (no authenticator, no PRF, or unanswered).
unlockAndSignIn()¶
The returning-device button. Unseals the vault (biometric for passkey, none
for device), then signs in. For passkey-seed it asks the passkey directly.
Throws nothing is enrolled on this device with no vault.
signInWithPhrase(phrase)¶
Recovery and a new device. Signs in, then seals the phrase here, so it is the
last time it is typed on this device. Throws on a bad checksum (see
validatePhrase).
signInWithIdentity({ name, dob, words })¶
The three-box screen. Checks the words first and throws the engine's message
if they are too weak (for example Too easy to guess. About 14 more characters
would do it...). Signs in, then seals what was typed. The session also carries
phrase (the composed secret) and masterAddress. Same three inputs, same
identity, on any machine. Why the words carry the whole account:
Security model.
addRecovery({ name, dob, words })¶
Attach the three boxes to a passkey identity so it survives losing every
device. The identity changes: it is now the one the three boxes make. The
session carries previousAddress, the old cred.address(). Offer it straight
after the first sign-in, before anything hangs off the old identity.
The vault¶
isEnrolled(), address(), tier()¶
Read the vault. Synchronous, no prompt.
address()is the identity's master address, the same on every site (masterAddressafter the three boxes).nullwith no vault.tier()is'passkey-seed','passkey','device'ornull.
enroll(secret, { address })¶
Seal a secret at the best tier this device has: passkey if a passkey with
PRF can be made in time, else device. Resolves to { tier, address }.
Without address the secret must be a valid BIP-39 phrase, or it throws the
checksum message. Pass address for a three-box secret.
unlock()¶
The sealed secret, without signing in. Throws for passkey-seed
(this identity is the passkey itself — there is nothing to unseal) and with
no vault.
revealPhrase()¶
unlock() under a name a settings page can use: the phrase, for someone who
now wants to write it down, behind the same biometric. Throws for
passkey-seed: there is no phrase.
forget()¶
Remove the vault from this device. The identity still exists; this device stops knowing it.
Phrases¶
createPhrase()¶
A fresh 12-word BIP-39 phrase. Returns a promise.
Await it
signInWithPhrase(cred.createPhrase()) fails with
mnemonic.trim is not a function. Write await cred.createPhrase().
validatePhrase(phrase)¶
Resolves to { valid, error } (plus entropy when valid). Checks the
checksum, not just the word count: one mistyped word derives a different,
empty account. Example error:
A recovery phrase is 12 or 24 words. This one has 3.
Lower-level exports¶
For a page that builds its own flow. Each takes
{ issuer, basePath, fetchImpl } where it signs in.
| Export | Does |
|---|---|
signInWithPhrase(phrase, opts) |
Sign in, no vault. Throws signInWithPhrase needs the issuer origin without issuer. |
signInWithSeed(bytes, opts) |
Sign in from 32 seed bytes (a passkey's PRF output). |
signInWithIdentity(boxes, opts) |
Sign in from the three boxes, no vault. |
createPasskeyIdentity({ rpName, rpId }) |
Make a passkey; { credentialId, seed }, or null without PRF. |
usePasskeyIdentity(credentialId) |
The same seed from a named passkey. |
usePasskeyIdentityAny() |
Any passkey this origin has; { seed, credentialId }, or null without PRF. |
sealWith(secret, key, opts), unsealWith(vault, key) |
AES-GCM seal and unseal of the vault record. |
deviceWrapKey() |
Tier 2's non-extractable key, made once in IndexedDB. |
passkeySupported() |
Whether a platform authenticator is available. |
browserWebAuthn |
The default webauthn option. |
Checked against sdk/omxus-credential.mjs in the auth repo (30 Oct 2026):
every call above run against the provider's router in Node with a stand-in
authenticator; the auth repo's sdk/passkey.test.mjs passes 58/58. Untested
in a browser outside the auth repo.