Google OAuth Connect UI Plan
Goal
Provide a simple first-run UI that helps users connect ai-memory-hub to MCP
clients with Google sign-in. Google proves user identity; ai-memory-hub issues
the MCP bearer token that controls memory:read and memory:write access.
User-facing setup should use api.auth: oauth_resource_server. Keep
api.auth: none reserved for CI/test fixtures and maintainer-only smoke tests.
Product Shape
Add a small server-rendered Connect UI, not a marketing site:
GET /redirects to or renders the Connect UI.GET /connectshows service status, MCP URL, auth status, and client setup snippets.GET /auth/googlestarts Google OAuth/OIDC login.GET /auth/google/callbackvalidates Google login, creates or finds the local user, creates a web session, and issues or displays the hub token workflow.POST /auth/logoutclears the web session.
The UI should show:
- MCP URL, for example
https://memory.example.com/mcp. - Signed-in identity, for example
alice@example.com. - Copy buttons for client setup snippets.
- Auth support status per client: verified OAuth, bearer fallback, or unverified.
- Account-switching guidance: sign out or clear the client MCP auth token, then authenticate again.
Storage Model
Use the same configured metadata database for durable auth state. SQLite remains the default local store; Postgres and other metadata providers should implement the same contract when they support auth.
Store:
oauth_identities- provider, initially
google - provider subject, from Google
sub - local
user_id - normalized email and display name
- created and last-login timestamps
web_sessions- hashed session id
user_id- CSRF token hash
- expiry, created, last-seen, revoked timestamps
auth_tokensor OAuth access-token records- token id and hash or JWT id
user_id- scopes
- expiry and revoked timestamps
Do not store Google access tokens by default. Store Google refresh tokens only if a later feature truly needs Google APIs; MCP authorization should use hub-issued tokens.
Phase 1: Connect UI Skeleton
- [x] Add config for enabling the Connect UI, default enabled for user-facing Docker/runtime setups.
- [x] Add public Connect UI routes that do not require MCP bearer auth:
/,/connect,/auth/google,/auth/google/callback, and/auth/logout. - [x] Render a Jinja-backed HTML page with service status, MCP URL, and auth
status. Templates and static assets live under
memory/ui/connect. - [x] Derive the MCP URL from
api.public_base_urlplus/mcpunlessapi.oauth.resourceis explicitly configured. - [x] Add copyable setup snippets with placeholders for Codex, Copilot CLI, Pi, OpenCode, Claude, Hermes, OpenClaw, and Gemini CLI.
- [x] Mark every unverified client snippet as unverified until tested against current official docs or local client behavior.
- [x] Add tests for route availability, secret-free rendering, and correct MCP URL derivation.
Phase 2: Google OAuth/OIDC Login
- [x] Add config for Google OAuth: client id env var, client secret env var, callback URL, allowed hosted domains, and allowed email list if configured.
- [x] Use maintained Python OAuth/OIDC libraries rather than hand-rolling protocol handling.
- [x] Start Google login from
/auth/googleusing OIDC scopes:openid email profile. - [x] Validate the Google callback, ID token signature, issuer, audience, nonce,
state, and expiry. Provider PKCE remains tracked in
oidc_pkce_hardening_plan.md. - [x] Reject users outside configured hosted-domain or email allowlists.
- [x] Never log Google tokens, ID-token claims beyond safe identifiers, or raw callback query strings.
- [x] Add unit/integration tests for callback success, invalid state, wrong audience, expired token, denied domain, and secret redaction.
Phase 3: Durable Identity And Web Sessions
- [x] Add
oauth_identitiesmetadata-store contract methods. - [x] Implement Google subject to local user lookup and creation.
- [x] Add
web_sessionsmetadata-store contract methods. - [x] Store only hashed session ids and hashed CSRF tokens.
- [x] Set web-session cookies as
HttpOnly,Securewhen not on loopback, andSameSite=Lax. - [x] Make sessions survive server restart when the metadata DB and session signing secret are unchanged.
- [x] Add logout and session revocation.
- [ ] Add migration tests for SQLite and Postgres metadata stores.
- [x] Add restart tests proving the same Google subject maps to the same
owner_id.
Phase 4: Hub-Issued MCP Tokens
- [x] Add a token issuer that creates hub-owned access tokens after Google login.
- [x] Include
sub,iss,audorresource,scope,iat,exp, andjticlaims. - [x] Use
api.oauth.resourceorapi.public_base_url + /mcpas the MCP audience/resource. - [x] Scope default tokens to
memory:read memory:write. - [x] Keep token expiry short by default.
- [x] Store revocation state or token hashes where needed to support logout and emergency revocation.
- [x] Reuse existing OAuth resource-server validation for MCP/API requests.
- [x] Add tests for valid token use, expired token rejection, wrong resource rejection, insufficient scope, revocation, and account isolation.
Phase 5: MCP Client Setup Matrix
- [x] Create a client setup matrix for: Codex, Copilot CLI, Pi, OpenCode, Claude, Hermes, OpenClaw, and Gemini CLI.
- [ ] For each client, document: config file path or command, exact MCP URL snippet, OAuth support status, token storage behavior, reauth/account-switch behavior, and known limits.
- [ ] Verify exact syntax against official docs or local client behavior before marking a client as verified.
- [x] Keep unverified clients visible but labeled
Unverified. - [x] Include copy buttons in the Connect UI.
- [ ] Include copy buttons in docs.
- [x] Add tests that generated snippets include the configured MCP URL and never include raw tokens.
Phase 6: Account Switching And Reauth
- [x] Document that account switching is client-driven.
- [x] Add UI guidance for clearing the old MCP auth token before signing in with another Google account.
- [x] Make the server treat each Google
subas a distinct identity unless an admin explicitly links accounts. - [x] Add a safe account-linking plan or explicitly defer account linking.
- [x] Add tests proving User A cannot access User B memory after reauth with a different Google account.
Phase 7: Docker And Release Docs
- [x] Add a user-facing OAuth-enabled Docker Compose example.
- [x] Keep test-only unauthenticated Compose examples labeled as maintainer smoke tests.
- [x] Document required environment variables:
Google client id, Google client secret, hub JWT/session secret,
api.public_base_url, and allowed domains/emails. - [x] Document restart behavior: identities and sessions persist with the same metadata DB and secrets; changing the JWT/session secret invalidates existing tokens/sessions.
- [x] Update README quickstart to send users to
/connect. - [x] Update MCP docs to make
/connectthe source of exact client setup snippets.
Phase 8: Security, Privacy, And Operations
- [x] Add CSRF protection for state-changing UI routes.
- [ ] Rate-limit login and callback attempts when a rate-limit mechanism exists.
- [ ] Add audit events for login success, login denial, logout, token issuance, token revocation, and account switch.
- [ ] Redact session ids, authorization headers, Google tokens, and hub tokens from logs, traces, metrics labels, and error responses.
- [x] Add readiness/observability status for Connect UI and Google OAuth config without exposing secrets.
- [x] Add a safe diagnostics panel to
/connectthat renders auth mode, endpoint mode, provider health, and OpenTelemetry status without memory counts, identities, bearer tokens, API keys, DSNs, raw queries, or embeddings. - [x] Keep
/connectavailable whenapi.connect.enabledis true even whenapi.auth: none; render it as a local/trusted no-auth setup page instead of hiding the page. - [x] Render
api.auth: bearer_tokenas a protected-header mode with client setup guidance but no secret display. - [x] Render
api.auth: oauth_resource_serveras the OAuth client-owned authorization mode, showing configured providers as non-clickable readiness chips. - [ ] Add negative tests for token leakage in logs and rendered pages.
Implementation Notes
Current implementation:
- The Connect UI is server-rendered with Jinja templates under
memory/ui/connectand enabled throughapi.connect. - Passport sign-in providers are configured through
api.connect.passport. This is a hub config namespace, not Node Passport middleware. Google, Meta, and X are accepted provider names; live OAuth authorization and callback handling uses HTTPX plus JOSE/OIDC token verification through theoauthextra. Test hooks can still inject provider claims without calling external identity providers. - Google
subvalues map to deterministic local users. Account linking is explicitly deferred; different Google subjects stay distinct unless a future admin workflow links them. - Hub-issued MCP tokens are HS256 JWTs with
sub,iss,aud,resource,scope,iat,exp, andjti; their hashes are also stored inauth_tokensso logout can revoke the current token. - Client snippets are visible but remain
Unverifieduntil tested against current client releases.
Remaining follow-ups:
- Complete Postgres migration tests for
oauth_identitiesandweb_sessions. - Verify exact client setup syntax and token-storage behavior per client.
- Add rate limiting once a shared rate-limit mechanism exists.
- Add explicit audit-event coverage and log/rendered-page token-leakage tests.
- Complete provider PKCE support in
oidc_pkce_hardening_plan.md.
Done When
- [ ] A new user can start the OAuth-enabled Docker setup, open
/connect, sign in with Google, copy an MCP URL/snippet, and connect at least one verified MCP client. - [ ] The MCP client sends
Authorization: Bearer <hub-token>and memory reads and writes are scoped to the signed-in user. - [ ] Server restart preserves identities and active sessions when the metadata DB and secrets persist.
- [ ] Reauth with another Google account maps to another local user and cannot read the previous user's memory.
- [ ] User-facing docs do not present
api.auth: noneas a setup option.