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

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 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.

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. Matching tokens return AuthLevel::User with a per-user ActorContext defined in 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.

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.

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.

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:

Configuration Examples

Initialize authentication state for different deployment scenarios:

// Single-user deployment with static root token
let auth_state = AuthState::new(Some("secure-root-token".to_string()));
// Enable trusted proxy for SSO termination
let auth_state = AuthState::new(Some("root-token".to_string()))
    .with_trusted_proxy_bearer("proxy-secret-token");
// 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);
// 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:

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

Subsequent requests automatically include the session cookie:

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:

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 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.

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →