# Security Model for Bearer Tokens and HTTP Basic Auth in ai-memory

> Learn how ai-memory secures its web UI with bearer tokens and HTTP Basic auth. Discover layered authentication for protected access.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: how-to-guide
- Published: 2026-08-20

---

**ai-memory protects its `/web` UI and related endpoints through a layered authentication middleware that prioritizes bearer tokens while offering HTTP Basic authentication as a browser convenience for read-only access.**

The ai-memory repository implements a defense-in-depth authentication system in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) that governs access to the web interface and API endpoints. This security model for bearer tokens and HTTP Basic auth supports everything from single-user deployments to multi-user environments with SSO integration, ensuring that state-changing operations remain strictly token-bound while allowing browser-friendly access for viewing data.

## Six-Layer Authentication Stack

The `require_bearer` middleware evaluates credentials in strict priority order, short-circuiting at the first successful match defined in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs).

### Root Bearer Token

The server’s **root credential** grants full administrative access. When `AuthState` is initialized with `Some(token)`, every request presenting `Authorization: Bearer <token>` receives `AuthLevel::Root` and the configured `root_actor` context. This layer accepts any HTTP method and serves as the primary authentication mechanism for API clients.

### Trusted-Proxy Bearer

For deployments behind a reverse-proxy or SSO gateway, ai-memory supports a dedicated **proxy bearer token** configured via `AuthState::with_trusted_proxy_bearer`. This token is validated only when the request also carries trusted proxy headers (`X-Memory-Actor-*`). Successful authentication yields `AuthLevel::Root` if the proxy asserts the root identity, otherwise `AuthLevel::User`, enabling secure header-based identity injection without exposing internal impersonation capabilities to unauthenticated clients.

### Multi-User Token Lookup

When `AuthState::with_multiuser` is enabled with a **TokenPepper** and **ReaderPool**, the middleware hashes incoming bearer tokens and queries the `users` table via `find_active_user_by_token_hash` defined in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs). Matching tokens return `AuthLevel::User` with a per-user **ActorContext** defined in [`crates/ai-memory-core/src/user.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/user.rs), allowing granular access control for individual accounts without shared root credentials.

### HTTP Basic Authentication (GET-Only)

To simplify browser access, ai-memory accepts **HTTP Basic authentication** strictly for GET requests. The client must send `Authorization: Basic <base64(any:<token>)>` where the username portion is ignored and the password must match the root bearer token. Upon successful validation, the middleware issues a session cookie and returns `AuthLevel::Root` (or user level after multi-user lookup). This convenience layer is explicitly disabled for POST, PUT, and DELETE methods to limit CSRF attack surface.

### Session Cookie (GET-Only)

Following successful Basic authentication, browsers receive an `ai_memory_auth` cookie containing the authenticated token. Subsequent GET requests presenting this cookie bypass the Basic auth prompt, maintaining `AuthLevel::Root` or user-level access. Like Basic auth, cookie authentication is restricted to read-only operations; state-changing requests require explicit bearer tokens in the `Authorization` header.

### Anonymous Access (Development Mode)

When `AuthState.expected` is `None` (no token configured), all requests pass through with `AuthLevel::Anonymous` and an anonymous `ActorContext`. This mode is intended strictly for development environments and provides no security guarantees.

## Security Hardening Mechanisms

The ai-memory security model implements several hardening measures to prevent common web vulnerabilities and side-channel attacks.

### Constant-Time Token Comparison

To prevent timing analysis attacks, all token comparisons use `subtle::ConstantTimeEq` rather than standard string equality. This ensures that attacker-controlled input cannot be measured against valid tokens to leak byte-by-byte information, as implemented in lines 20-24 of [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs).

### Method-Based Restriction for Browser Auth

The middleware enforces a strict separation between read and write operations. Lines 14-15 of the authentication logic reject Basic authentication and session cookies for POST, PUT, and DELETE requests, as noted in the comment at line 16. This design confines CSRF exposure to read-only endpoints while requiring cryptographically secure bearer tokens for any state modification.

### Dual WWW-Authenticate Challenges

When authentication fails on GET requests, the `unauthorized` function (lines 105-124) responds with both `WWW-Authenticate: Basic` and `WWW-Authenticate: Bearer` headers. This dual challenge ensures that browsers trigger a native credential dialog for manual entry while API clients recognize the Bearer token requirement, accommodating both human users and automated MCP clients seamlessly.

### Secure Session Cookie Attributes

Session cookies are configured with **HttpOnly**, **SameSite=Strict**, and a 30-day max-age. Operators can enable the `Secure` flag via `AuthState::with_secure_cookie` to ensure cookies transmit only over HTTPS connections. These attributes are set in lines 77-84 of the authentication middleware, preventing XSS theft and CSRF attacks across domains.

### Trusted-Proxy Isolation

Proxy-asserted identity headers (`X-Memory-Actor-*`) are only trusted when the request authenticates with the dedicated proxy bearer token. The `trusted_proxy_actor` logic (lines 58-75) strictly validates this pairing, ensuring that unauthenticated clients cannot spoof identity headers to escalate privileges.

## Source Code Architecture

The authentication flow spans multiple crates within the ai-memory repository:

- **[`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs)**: Contains the core `require_bearer` middleware, `AuthState` struct, and token validation logic with constant-time comparison.
- **[`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs)**: Registers the authentication layer for MCP, hook, handoff, and `/web/*` routes.
- **[`crates/ai-memory-web/src/lib.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-web/src/lib.rs)**: Mounts the `/web` UI and applies the same middleware stack to browser-facing endpoints.
- **[`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs)**: Provides `find_active_user_by_token_hash` for multi-user token resolution against the database.
- **[`crates/ai-memory-core/src/user.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/user.rs)**: Defines the `User` struct and `ActorContext` used throughout the authorization flow.

## Configuration Examples

Initialize authentication state for different deployment scenarios:

```rust
// Single-user deployment with static root token
let auth_state = AuthState::new(Some("secure-root-token".to_string()));

```

```rust
// Enable trusted proxy for SSO termination
let auth_state = AuthState::new(Some("root-token".to_string()))
    .with_trusted_proxy_bearer("proxy-secret-token");

```

```rust
// Activate multi-user mode with database-backed token storage
let auth_state = AuthState::new(Some("root-token".to_string()))
    .with_multiuser(pepper, reader_pool, writer_handle);

```

```rust
// Enforce secure cookie attributes for HTTPS deployments
let auth_state = AuthState::new(Some("root-token".to_string()))
    .with_secure_cookie(true);

```

Browser clients authenticate using HTTP Basic for the initial request:

```bash
curl -u "any:secure-root-token" https://ai-memory.example.com/web/

```

Subsequent requests automatically include the session cookie:

```bash
curl -H "Cookie: ai_memory_auth=secure-root-token" \
     https://ai-memory.example.com/web/dashboard

```

API clients should always use bearer tokens for full method access:

```bash
curl -X POST \
     -H "Authorization: Bearer secure-root-token" \
     -H "Content-Type: application/json" \
     -d '{"query":"example"}' \
     https://ai-memory.example.com/mcp/query

```

## Summary

- **Layered authentication** in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) evaluates root bearer, proxy bearer, multi-user, Basic auth, cookies, and anonymous access in strict priority.
- **Bearer tokens** are required for POST/PUT/DELETE operations; Basic auth and cookies are restricted to GET requests to mitigate CSRF risks.
- **Constant-time comparison** via `subtle::ConstantTimeEq` prevents timing attacks during token validation.
- **Trusted-proxy mode** isolates header-based identity injection behind a dedicated bearer token, preventing privilege escalation.
- **Secure cookie attributes** (HttpOnly, SameSite-Strict, optional Secure flag) protect session persistence in browser environments.

## Frequently Asked Questions

### How does ai-memory prevent timing attacks against bearer tokens?

The middleware uses `subtle::ConstantTimeEq` to compare submitted tokens against stored credentials in constant time, ensuring that the comparison duration reveals no information about the correct token value. This implementation appears in lines 20-24 of [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs).

### Why does HTTP Basic authentication only work for GET requests?

The security model explicitly restricts Basic auth and session cookies to GET requests (lines 14-16) to limit CSRF attack vectors. State-changing operations require explicit `Authorization: Bearer` headers that cannot be automatically sent by browsers via CSRF mechanisms, ensuring that write operations always require intentional, scriptable authentication.

### What is the trusted-proxy bearer used for?

The trusted-proxy bearer allows terminating reverse-proxies or SSO gateways to authenticate with ai-memory while injecting user identity via `X-Memory-Actor-*` headers. This token is validated separately from the root token (lines 58-75), ensuring that proxy-asserted identities are only trusted when the request presents the pre-shared proxy secret, enabling secure header-based authentication in load-balanced environments.

### How do I enable multi-user authentication instead of a single shared token?

Call `AuthState::with_multiuser(pepper, reader_pool, writer_handle)` when building the authentication state. This configures the middleware to hash incoming tokens with the server-wide `TokenPepper` and query the `users` table via `find_active_user_by_token_hash`, allowing individual user accounts with distinct tokens rather than a single root credential.