How to Set Up Multi-User Authentication with OIDC Device Tokens in ai-memory

To enable multi-user authentication in ai-memory, configure root_issuer and root_subject in config.toml, then run ai-memory auth login oidc-device to generate a device token that automatically isolates each user's data in separate operator namespaces.

The akitaonrails/ai-memory repository implements a true multi-user model using OIDC device-authorization tokens stored in a shared auth.json file. Each user's OIDC pair (issuer, subject) generates a deterministic namespace, ensuring complete isolation between operators while allowing headless CI authentication through the device flow.

Configure the Root OIDC Identity

Start by defining the root operator in your config.toml. This replaces the legacy single-user bearer token model with a cryptographically verified OIDC identity.

Add the following under the [auth] section:

[auth]
root_issuer = "https://login.example.com/realms/ai-memory"
root_subject = "admin@example.com"
root_username = "alice"
root_email    = "alice@example.com"
root_name     = "Alice Admin"

The root_issuer and root_subject pair must match the OIDC token used for administrative operations. According to the source code in crates/ai-memory-cli/src/config.rs (lines 62–68), these fields define which identity receives root capability and can perform privileged actions like ai-memory admin purge-project.

Authenticate Users via Device Flow

The device flow allows headless authentication—ideal for CI runners and containerized environments.

Run the login command:

$ ai-memory auth login oidc-device
> Open https://login.example.com/realms/ai-memory/device in a browser
> Enter code: ABCD-EFGH

The CLI executes the full device-authorization grant as implemented in crates/ai-memory-llm/src/oidc.rs (lines 74–85):

  1. Discovers the OIDC provider endpoints
  2. Requests a device code via request_device_code
  3. Polls the token endpoint until authorization completes
  4. Stores the token via OidcToken::save (lines 92–98)

The token persists to $XDG_CONFIG_HOME/ai-memory/auth.json under the oidc key, preserving existing entries for other providers like openai or copilot.

Automatic Token Management

Once stored, the CLI automatically loads the OIDC token for every subcommand unless overridden with --auth-token.

The lookup logic in crates/ai-memory-cli/src/commands/hook_spool.rs follows this priority:

let token = if let Some(t) = cli_opt.auth_token {
    Some(t)
} else {
    OidcToken::load(&auth_path)?.map(|t| t.access.expose_secret().to_string())
};

Token refresh happens automatically when token.needs_refresh() returns true. The CLI calls refresh_access_token (defined in crates/ai-memory-llm/src/oidc.rs, lines 64–84) to obtain a fresh access token using the stored refresh grant, then updates auth.json via OidcToken::save without user intervention.

Server-Side Validation and Namespacing

Incoming HTTP requests to the ai-memory server must include the JWT in the Authorization: Bearer header.

The server validates tokens in crates/ai-memory-mcp/src/auth.rs:

let jwt = extract_bearer(jwt_header)?;
let claims = validate_oidc(&jwt, config.oidc_jwks_url)?;
let actor = ActorContext {
    user: Some(User {
        oidc_issuer: claims.iss.clone(),
        oidc_subject: claims.sub.clone(),
    }),
};

After validation, the server constructs the operator namespace using the logic in crates/ai-memory-core/src/actor.rs (lines 320–340). The namespace format is:


o-<len(issuer)>-<base64(issuer)>-<subject>

This identifier isolates all slots, handoffs, and wiki pages to the specific (issuer, subject) pair, guaranteeing that users cannot access each other's data.

Root-Only Operations

Only the OIDC identity matching the configured root_issuer and root_subject receives AuthLevel::Root. All other authenticated users operate within their individual namespaces with standard privileges.

To perform administrative tasks, ensure your active OIDC token (loaded from auth.json or passed via --auth-token) corresponds to the root pair defined in step one. The server validates this match in the AuthSettings logic referenced in crates/ai-memory-cli/src/config.rs.

Summary

  • Configure root identity: Set root_issuer and root_subject in config.toml to establish administrative privileges.
  • Device flow login: Run ai-memory auth login oidc-device to authenticate without browser integrations.
  • Automatic persistence: Tokens store securely in auth.json and load automatically via OidcToken::load.
  • Per-user isolation: The server maps each (issuer, subject) pair to a unique operator namespace using deterministic hashing in ai_memory_core::actor.
  • Seamless refresh: Expired tokens update automatically using refresh_access_token before API requests.

Frequently Asked Questions

How does ai-memory store multiple user tokens without conflicts?

Each user runs ai-memory auth login oidc-device independently, which calls OidcToken::save to write the token to auth.json. Because the CLI loads the token from this shared file on every invocation via OidcToken::load, different users simply maintain separate auth.json files in their respective $XDG_CONFIG_HOME directories, or the system uses distinct --auth-dir paths for service accounts.

Can I use OIDC device tokens in CI/CD pipelines?

Yes. The device flow is designed for headless environments. Run ai-memory auth login oidc-device once interactively to generate the initial token, commit the resulting auth.json (or mount it as a secret), and the CLI will automatically handle token refresh during subsequent CI runs. This eliminates the need to embed long-lived bearer tokens in environment variables.

What happens if my OIDC token expires during a long-running operation?

The CLI checks token expiry before each request using token.needs_refresh(). If the token nears expiration, the refresh_access_token function (in crates/ai-memory-llm/src/oidc.rs) executes a background refresh, saves the new credentials via OidcToken::save, and continues the operation without interruption. No manual intervention is required.

How do I revoke or invalidate a user's access?

Delete the auth.json file from the user's configuration directory (or remove the specific oidc key from it). On the server side, the validation logic in crates/ai-memory-mcp/src/auth.rs always checks the OIDC provider's JWKS endpoint, so revoking the token at the identity provider level will immediately invalidate it for future requests, regardless of the local auth.json state.

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 →