# How Passkeys Work

This page explains the WebAuthn protocol ceremonies and how Barycenter's architecture implements them using a Rust WASM client and server-side endpoints.

## WebAuthn Ceremonies

The WebAuthn specification defines two core ceremonies:

### Registration (Attestation)

Registration creates a new credential and associates it with a user account. The WebAuthn specification calls this the **attestation** ceremony because the authenticator attests to the properties of the newly created key pair.

The registration ceremony involves three parties:

1. **Relying Party (Barycenter server)** -- generates a challenge and specifies credential creation parameters.
2. **Browser** -- mediates between the server and authenticator, enforcing origin checks.
3. **Authenticator** -- generates a new key pair, stores the private key, and returns the public key with an attestation statement.

During registration, the server receives:

- The **public key** of the newly created credential.
- The **credential ID** that uniquely identifies this key pair.
- An **attestation statement** proving the key was generated by a legitimate authenticator.
- Authenticator metadata including **backup eligibility** (used for [hwk vs swk classification](./passkeys.md#passkey-classification-hwk-vs-swk)) and the initial **signature counter**.

### Authentication (Assertion)

Authentication proves possession of a previously registered credential. The WebAuthn specification calls this the **assertion** ceremony because the authenticator asserts the user's identity by signing a challenge.

During authentication, the server:

1. Sends a random challenge and a list of acceptable credential IDs.
2. The authenticator signs the challenge with the private key.
3. The server verifies the signature against the stored public key.
4. The server checks the signature counter to detect cloned authenticators.

## WASM Client Architecture

Barycenter uses a Rust crate (`client-wasm/`) compiled to WebAssembly to handle the browser-side WebAuthn API calls. This design provides several benefits:

- **Type safety**: WebAuthn options are parsed and validated with Rust's type system before being passed to the browser API.
- **Consistent behavior**: the same Rust data structures are used on both client and server, reducing serialization mismatches.
- **Small footprint**: the compiled WASM module is loaded once by the login page.

### Client-Server Communication Flow

```
Browser (WASM)                    Barycenter Server
     |                                   |
     |  1. POST /webauthn/*/start        |
     |  -------------------------------->|
     |                                   |  Generate challenge,
     |                                   |  store in DB (5 min TTL)
     |  2. PublicKeyCredentialOptions     |
     |  <--------------------------------|
     |                                   |
     |  3. navigator.credentials.*()     |
     |  (user interaction: biometric/PIN)|
     |                                   |
     |  4. POST /webauthn/*/finish       |
     |  -------------------------------->|
     |                                   |  Verify response,
     |                                   |  consume challenge
     |  5. Success / session             |
     |  <--------------------------------|
     |                                   |
```

The WASM module calls `navigator.credentials.create()` for registration and `navigator.credentials.get()` for authentication, converting the server's JSON options into the `PublicKeyCredentialCreationOptions` or `PublicKeyCredentialRequestOptions` that the browser expects.

### WASM Module Functions

The compiled WASM module exposes four functions:

```javascript
import init, {
  supports_webauthn,
  supports_conditional_ui,
  register_passkey,
  authenticate_passkey
} from '/static/wasm/barycenter_webauthn_client.js';

// Initialize the WASM module
await init();

// Check browser capabilities
const hasWebAuthn = supports_webauthn();
const hasAutofill = await supports_conditional_ui();

// Register a new passkey (options from /webauthn/register/start)
const credential = await register_passkey(registrationOptions);

// Authenticate (options from /webauthn/authenticate/start)
// mediation: "conditional" for autofill, "optional" for explicit
const assertion = await authenticate_passkey(authOptions, "conditional");
```

## Relying Party ID

The WebAuthn Relying Party (RP) ID is a domain string that scopes credentials to a specific origin. Barycenter derives the RP ID from the **host component** of the configured issuer URL.

| Issuer URL                          | RP ID               |
|-------------------------------------|----------------------|
| `https://auth.example.com`          | `auth.example.com`   |
| `https://example.com:8443/`         | `example.com`        |
| `http://localhost:9090`             | `localhost`           |

The RP ID determines which credentials are available during authentication:

- Credentials registered with RP ID `auth.example.com` are only usable on `auth.example.com` and its subdomains.
- The RP ID must be a registrable domain suffix of the origin. For example, `example.com` is valid for `auth.example.com`, but `other.com` is not.

> **Important**: Changing the issuer URL after passkeys have been registered will invalidate all existing passkey credentials because the RP ID will no longer match. Plan your domain structure before deploying passkey authentication in production.

## Challenge Lifecycle

Every WebAuthn ceremony requires a server-generated challenge to prevent replay attacks:

1. The server generates a random challenge and stores it in the `webauthn_challenges` table with a **5-minute TTL**.
2. The challenge is sent to the browser as part of the credential options.
3. The authenticator includes the challenge in its signed response.
4. The server retrieves and validates the challenge, then deletes it (single-use).

A background job runs every 5 minutes to clean up expired challenges that were never consumed (for example, if the user abandoned the ceremony).

## Further Reading

- [Registering a Passkey](./passkey-registration.md) -- the attestation ceremony in detail
- [Authenticating with a Passkey](./passkey-authentication.md) -- the assertion ceremony in detail
- [Building the WASM Client](../development/wasm-client.md) -- compilation instructions
