# Register & Authenticate Credentials

## Overview

A WebAuthn credential is created once (registration) and asserted many times (authentication).
The [`ox/webauthn` entrypoint](/webauthn) mirrors this split:
[`Registration`](/webauthn/webauthn/Registration) creates and verifies credentials,
[`Authentication`](/webauthn/webauthn/Authentication) requests and verifies assertions, and
[`Credential`](/webauthn/webauthn/Credential) serializes credentials for transport and storage.
[`Authenticator`](/webauthn/webauthn/Authenticator) constructs raw authenticator payloads (mostly for
testing), and [`CoseKey`](/api/CoseKey) converts the COSE-encoded public keys found in attestation
objects.

## Recipes

### Register a Credential

Create a credential with a server-issued challenge, then serialize it so it can be sent to the
server for verification and storage.

```ts twoslash
import { Credential, Registration } from 'ox/webauthn'

// Challenge generated and stored by the server for this registration.
const challenge = '0x69abb4b5a0de4bc62a2a201f8d25bae9'

const credential = await Registration.create({
  challenge,
  name: 'Example', // [!code hl]
})

// Serialize for transmission — `Credential.serialize` converts binary
// fields to base64url/hex strings.
const serialized = Credential.serialize(credential)
const body = JSON.stringify(serialized)
```

The returned `credential.id` and `credential.publicKey` identify the credential in later
ceremonies — persist them client-side if you plan to verify locally.

### Authenticate an Existing Credential

Request an assertion for a known credential ID with a fresh server-issued challenge, then check
it against the public key stored at registration.

```ts twoslash
import { PublicKey } from 'ox'
import { Authentication } from 'ox/webauthn'

// Public key stored by the server at registration.
declare const publicKey: PublicKey.PublicKey

// Challenge issued by the server for this login attempt.
const challenge = '0x1e0c4b8b5f14ec863da1e7f0d3a58b8a'

const response = await Authentication.sign({
  challenge,
  credentialId: 'oZ48...', // [!code hl]
})

const valid = Authentication.verify({
  challenge,
  metadata: response.metadata,
  origin: 'https://example.com',
  publicKey,
  rpId: 'example.com',
  signature: response.signature,
})
// @log: true
```

To verify on a server, send the assertion with
[`Authentication.serializeResponse`](/webauthn/webauthn/Authentication) and rehydrate it with
`Authentication.deserializeResponse` before calling `verify`.

### Verify Registration on the Server

Deserialize the credential received from the client and validate the whole registration ceremony —
challenge, origin, RP ID, authenticator flags, and attestation — with
[`Registration.verify`](/webauthn/webauthn/Registration).

```ts twoslash
import { Credential, Registration } from 'ox/webauthn'

// Request body received from the client.
declare const body: string

const credential = Credential.deserialize(JSON.parse(body))

const result = Registration.verify({
  credential,
  challenge: '0x69abb4b5a0de4bc62a2a201f8d25bae9', // [!code hl]
  origin: 'https://example.com',
  rpId: 'example.com',
})
// @log: {
// @log:   credential: {
// @log:     id: 'oZ48...',
// @log:     publicKey: { prefix: 4, x: 51421...5123n, y: 12345...6789n },
// @log:   },
// @log:   counter: 0,
// @log:   userVerified: true,
// @log: }
```

Store `result.credential.id`, `result.credential.publicKey`, and `result.counter` — they are the
inputs for verifying future authentication ceremonies.

## Best Practices

### Issue Challenges Server-Side

Generate a random, single-use challenge on the server for every ceremony and reject responses
whose challenge does not match. A client-chosen challenge defeats replay protection.

### Pin Origin and RP ID

Always pass `origin` and `rpId` when verifying on a server. They bind the ceremony to your site
and stop credentials phished on look-alike origins from validating.

### Never Ship the Raw Credential

Serialize with `Credential.serialize` rather than the native `toJSON()` — native output can
include client extension results (such as PRF outputs), which are secrets.

## See More

<Cards>
  <Card icon="lucide:signature" title="Sign & Verify with Passkeys" description="Sign payloads with a passkey and verify WebAuthn P256 signatures." to="/guides/webauthn/signing" />

  <Card icon="lucide:key-round" title="Derive Secrets with PRF" description="Turn PRF outputs into signing and encryption keys." to="/guides/webauthn/prf" />

  <Card icon="lucide:box" title="Build ERC-4337 User Operations" description="Use passkey-backed smart accounts with bundlers and EntryPoints." to="/guides/account-abstraction/user-operations" />
</Cards>
