# Authentication Service Internals

The Authentication Service handles user registration and login via the OPAQUE asymmetric password-authenticated key exchange (PAKE) protocol. It also manages MLS KeyPackages, hybrid post-quantum keys, and session token issuance.

This page covers the server-side OPAQUE flow, session token lifecycle, KeyPackage storage, and hybrid key endpoints.

**Sources:**
- `crates/quicprochat-server/src/domain/` (OPAQUE handlers, session management)
- `crates/quicprochat-server/src/sql_store.rs` (SqlStore persistence)
- `proto/qpc/v1/auth.proto` (wire schema)

---

## OPAQUE Protocol

quicprochat uses the OPAQUE asymmetric PAKE (RFC 9497) for user authentication. The password never leaves the client and is never known to the server. The server stores an OPAQUE registration record derived from the password, but this record cannot be used to recover the password even if the server is fully compromised.

### Registration (IDs 100-101)

Registration takes two round trips.

```text
Client                                    Server
  |                                          |
  |  [1] OpaqueRegisterStartRequest          |
  |      username: "alice"                   |
  |      request: <OPAQUE RegistrationReq>   |
  | ---------------------------------------->|
  |                                          |
  |  [2] OpaqueRegisterStartResponse         |
  |      response: <OPAQUE RegistrationResp> |
  | <----------------------------------------|
  |                                          |
  |  [3] OpaqueRegisterFinishRequest         |
  |      username: "alice"                   |
  |      upload: <OPAQUE RegistrationUpload> |
  |      identity_key: <Ed25519 pubkey>      |
  | ---------------------------------------->|
  |                                          |
  |  [4] OpaqueRegisterFinishResponse        |
  |      success: true                       |
  | <----------------------------------------|
```

**Step [1]:** The client generates a `RegistrationRequest` blob using the `opaque-ke` crate. This contains a masked version of the password; the server cannot extract the raw password.

**Step [2]:** The server generates a `RegistrationResponse` using its OPAQUE server keypair and the client's request. The server does not yet persist anything.

**Step [3]:** The client completes the OPAQUE registration and sends a `RegistrationUpload` blob. This blob contains the password-derived key material (specifically the client's OPAQUE export key envelope and public key). The client also sends its Ed25519 identity public key.

**Step [4]:** The server stores the `RegistrationUpload` blob as the user's OPAQUE record, indexed by `username`. The Ed25519 identity key is stored alongside the record. Registration fails with `success: false` if the username is already taken.

### Login (IDs 102-103)

Login also takes two round trips and produces a session token.

```text
Client                                    Server
  |                                          |
  |  [1] OpaqueLoginStartRequest             |
  |      username: "alice"                   |
  |      request: <OPAQUE CredentialReq>     |
  | ---------------------------------------->|
  |                                          |
  |  [2] OpaqueLoginStartResponse            |
  |      response: <OPAQUE CredentialResp>   |
  | <----------------------------------------|
  |                                          |
  |  [3] OpaqueLoginFinishRequest            |
  |      username: "alice"                   |
  |      finalization: <OPAQUE Finalization> |
  |      identity_key: <Ed25519 pubkey>      |
  | ---------------------------------------->|
  |                                          |
  |  [4] OpaqueLoginFinishResponse           |
  |      session_token: <32 bytes>           |
  | <----------------------------------------|
```

**Step [1]:** The client generates a `CredentialRequest` using the `opaque-ke` crate.

**Step [2]:** The server looks up the user's OPAQUE record by `username` and generates a `CredentialResponse`. If the username is unknown, the server generates a fake response using a blinded dummy record to prevent username enumeration.

**Step [3]:** The client verifies the server's `CredentialResponse` against the stored password, derives the shared export key, and sends a `CredentialFinalization` blob that proves knowledge of the password. The client also sends its Ed25519 identity key.

**Step [4]:** The server verifies the `CredentialFinalization`. If verification succeeds and the identity key matches the registered key, the server generates a `session_token` (32 random bytes), stores it in the session table, and returns it to the client. If verification fails, the server returns an error status with an empty `session_token`.

### Session token lifecycle

The `session_token` is a 32-byte random bearer credential issued at login. It is:

- Stored in the SQLCipher `sessions` table (see [Storage Backend](storage-backend.md)).
- Included by the client in subsequent QUIC connections for authentication.
- Validated by the server on connection establishment; the server rejects connections with unknown or expired tokens.
- Invalidated on `DeleteAccount` or explicit logout.

The `Auth` message in `common.proto` carries the token for federation contexts:

```protobuf
message Auth {
  bytes access_token = 1;
  bytes device_id = 2;
}
```

---

## KeyPackage Storage

MLS KeyPackages are single-use by RFC 9420 requirement. The server stores a FIFO queue of KeyPackages per identity key.

### Data model

```text
identity_key (32-byte Ed25519 pubkey)
    -> VecDeque<KeyPackage bytes>
```

Each identity can have multiple KeyPackages queued. Clients should upload several packages after registration so that concurrent group invitations can each consume one without exhausting the supply.

### UploadKeyPackage (ID 300)

**Handler logic:**

1. Validate `identity_key` (exactly 32 bytes) and `package` (non-empty, <= 1 MiB).
2. Compute `SHA-256(package)` as the fingerprint.
3. Push the package to the back of the identity's queue in the SQL store.
4. Return the fingerprint.

The fingerprint allows the uploading client to detect server-side tampering. A peer that fetches a KeyPackage can compare its SHA-256 hash against the fingerprint communicated out-of-band.

### FetchKeyPackage (ID 301)

**Handler logic:**

1. Validate `identity_key` (exactly 32 bytes).
2. Pop from the front of the identity's queue (atomic operation).
3. Return the package bytes, or empty bytes if the queue is empty.

The pop is atomic with respect to the store lock, so concurrent fetch requests will not receive the same package. An empty response is not an error -- it means the target has exhausted its KeyPackage supply.

---

## Hybrid Key Endpoints

Hybrid (X25519 + ML-KEM-768) public keys are used for post-quantum sealed envelope encryption. Unlike KeyPackages, hybrid keys are not single-use. Each identity stores exactly one hybrid key; uploading a new key overwrites the previous one.

### UploadHybridKey (ID 302)

**Handler logic:**

1. Validate `identity_key` (32 bytes) and `hybrid_public_key` (non-empty).
2. Store the hybrid key, overwriting any previous value for this identity.
3. Return empty response.

### FetchHybridKey (ID 303)

Non-destructive lookup. Returns the stored hybrid public key, or empty bytes if none is stored. The key persists across fetches.

### FetchHybridKeys (ID 304)

Batch variant. Returns one key per input identity key in the same order. Missing keys are returned as empty bytes at the corresponding index position.

---

## Key Transparency Integration

The key transparency log (a Merkle append-only log) records key revocations and allows clients to audit the integrity of the key directory.

### RevokeKey (ID 510)

Appends a revocation entry to the KT Merkle log. Returns the leaf index of the revocation entry. Reasons: `"compromised"`, `"superseded"`, `"user_revoked"`.

### CheckRevocation (ID 511)

Returns the revocation status of an identity key: whether revoked, the reason, and the timestamp in milliseconds.

### AuditKeyTransparency (ID 520)

Returns a range of entries from the append-only log for client-side Merkle verification. Clients can verify the returned `root` hash against the Merkle tree built from the entries.

---

## Server implementation structure

```rust
// Domain handler (quicprochat-server/src/domain/)
struct AuthHandler {
    store: Arc<SqlStore>,         // SQLCipher persistence
    opaque_server: OpaqueServer,  // opaque-ke server state
}
```

All connections share the same `SqlStore` via `Arc`. The OPAQUE server state contains the server's long-term OPAQUE keypair, which is generated on first start and persisted to the database.

---

## Related pages

- [Storage Backend](storage-backend.md) -- SqlStore and FileBackedStore persistence
- [Auth Schema](../wire-format/auth-schema.md) -- Protobuf wire definitions
- [Method ID Reference](../wire-format/envelope-schema.md) -- all 44 method IDs
