How OIDC Device Authentication Flow Works for Native Hook Auth in ai-memory

The ai-memory CLI uses the OAuth 2.0 Device Authorization Grant to authenticate native hook commands without requiring a browser on the client machine, polling the token endpoint until the user completes login on a separate device.

The akitaonrails/ai-memory project enables secure, password-less authentication for developer lifecycle hooks by implementing the OIDC device flow. This approach lets command-line tools obtain access tokens by delegating user authentication to a browser on any device, making it ideal for headless environments and native hook invocations.

What Is the OIDC Device Flow?

The Device Authorization Grant (RFC 8628) solves a fundamental CLI problem: how to authenticate a user when the application cannot launch or control a browser. Instead of redirecting within the same process, the flow separates code execution from user interaction.

In ai-memory, this flow authenticates native hook commands—lifecycle events like session-start or project-init that trigger when developers interact with AI-assisted tooling.

Step-by-Step Flow Implementation

1. Initiate Device Authorization Request

When a developer runs ai-memory auth login oidc-device, the CLI constructs a POST request to the provider's device-authorization endpoint. This is implemented in crates/ai-memory-llm/src/oidc.rs (lines 1-30).

The response contains four critical fields:

  • device_code – opaque string for token polling
  • user_code – short, human-readable code for manual entry
  • verification_uri or verification_uri_complete – URL where the user authenticates
  • poll_interval – seconds the CLI must wait between polling attempts

2. Display Verification Instructions to the User

The CLI prints instructions directly to stdout. From the source in oidc.rs:

Visit https://issuer.example.com/realms/team/device and enter code: ABCD-EFGH

The user can complete this step on any device—phone, tablet, or workstation—with no requirement for browser access from the CLI process itself.

3. User Completes Authentication in Browser

This step occurs outside the ai-memory codebase. The user visits the verification URL, signs in with their OIDC provider, and authorizes the "ai-memory-cli" client. The provider then binds the device_code to a pending token grant.

4. CLI Polls the Token Endpoint

While awaiting user approval, the CLI enters a polling loop defined in crates/ai-memory-llm/src/oidc.rs (lines 66-100). It repeatedly POSTs to the token endpoint with:

grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=xxxxxxxx
&client_id=ai-memory-cli

The polling loop handles three server responses:

  • authorization_pending – user has not yet approved; sleep for poll_interval and retry
  • slow_down – increase polling interval to respect rate limits
  • Success – receive access_token and optional refresh_token

5. Persist Token to Local Storage

Upon successful authentication, the CLI writes token metadata to $DATA_DIR/auth.json (default: $HOME/.local/share/ai-memory/). The storage helpers—store_token, load_token, remove_token—are colocated in oidc.rs.

Stored fields include:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2027-08-20T12:34:56Z",
  "issuer": "https://issuer.example.com/realms/team",
  "client_id": "ai-memory-cli"
}

6. Hook Commands Automatically Attach the Token

When executing ai-memory hook post or similar commands, the hook runner reads the stored token and injects it into the Authorization header. As noted in crates/ai-memory-hooks/src/router.rs (lines 543-560), the system recognizes OIDC-authenticated requests and derives an identity key from the token claims.

ai-memory hook post \
  --event session-start \
  --payload '{"session_id":"1234","project":"myproj"}'

This automatically produces:

Authorization: Bearer eyJhbGciOi...

7. Server Validates and Distinguishes Token Types

The server layer in crates/ai-memory-mcp/src/auth.rs (lines 143-156) implements a dual-token strategy:

  • OIDC-derived tokens – authenticate per-developer hook writes
  • Static root bearer token (AI_MEMORY_AUTH_TOKEN) – required for privileged admin routes

This separation ensures that compromise of a developer OIDC token cannot escalate to server administration.

Fallback Behavior and Precedence

According to docs/install.md (lines 986-1002), the authentication system observes strict precedence:

  1. If AI_MEMORY_AUTH_TOKEN environment variable is set, use it (static root token)
  2. Otherwise, load and use the OIDC token from auth.json

This enables operation behind OIDC-aware reverse proxies while maintaining backward compatibility for automated deployments.

Practical Usage Examples

Complete Device Flow Login

ai-memory auth login oidc-device \
  --issuer "https://issuer.example.com/realms/team" \
  --client-id "ai-memory-cli"

Wait for browser approval, then verify stored credentials:

ai-memory auth status

Trigger Hook with Automatic Authentication


# No explicit token needed—CLI reads from auth.json

ai-memory hook post --event project-init --payload '{"lang":"rust"}'

Thin-Client Commands Reuse the Same Token

Even non-hook commands like ai-memory status or ai-memory search automatically pick up the OIDC token when no static bearer is configured, providing unified authentication across all CLI surfaces.

Key Source Files Reference

File Responsibility
crates/ai-memory-llm/src/oidc.rs Full device flow implementation: authorization request, polling loop, token persistence, refresh handling
crates/ai-memory-hooks/src/router.rs (lines 543-560) OIDC request recognition and identity key derivation for incoming hooks
crates/ai-memory-mcp/src/auth.rs (lines 143-156) Server-side token validation and distinction between OIDC and static root tokens
docs/install.md (lines 364-379, 986-1002) CLI usage instructions and fallback behavior documentation
docs/users.md (lines 61-127) Identity model documentation covering issuer, subject, and OIDC pair integration

Summary

  • The OIDC device flow eliminates browser dependencies from the CLI authentication path by separating code generation from user approval
  • crates/ai-memory-llm/src/oidc.rs implements the complete RFC 8628 flow including polling, error handling, and token storage
  • Native hook commands automatically reuse stored OIDC tokens via the Authorization: Bearer header without manual intervention
  • The server maintains defense-in-depth by requiring static root tokens for admin routes while accepting OIDC tokens for per-developer hook operations
  • Environment variable AI_MEMORY_AUTH_TOKEN takes precedence, enabling hybrid deployment scenarios

Frequently Asked Questions

What happens if the user never completes browser authentication?

The CLI continues polling until the device_code expires (typically 15 minutes, per provider policy). The polling loop in oidc.rs handles authorization_pending indefinitely within that window. After expiration, the CLI exits with an error instructing the user to restart ai-memory auth login oidc-device.

Can the CLI automatically refresh expired OIDC tokens?

Yes, if the token response included a refresh_token, the store_token and load_token helpers in oidc.rs persist it alongside the access token. On detection of expiry, the client attempts silent refresh before prompting for re-authentication. If no refresh token exists, the user must repeat the device flow.

Why does ai-memory use device flow instead of the authorization code flow with PKCE?

The authorization code flow requires either localhost redirect handling (fragile with port conflicts and firewalls) or out-of-band URI schemes (OS-dependent registration). The device flow works reliably in containers, SSH sessions, and CI environments where no browser is available. As documented in docs/install.md (lines 364-379), this choice prioritizes portability over the marginally faster single-redirect UX of PKCE.

How does the server distinguish between a valid OIDC token and the static root token?

Per crates/ai-memory-mcp/src/auth.rs (lines 143-156), the server examines token structure and validation path. OIDC tokens validate against the configured issuer's JWKS endpoint, while the static root token matches a constant secret. Route handlers declare required token types: hook endpoints accept either, admin endpoints reject OIDC tokens regardless of scope claims.

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 →