ai: forward X-LC-UID so User API Keys can run org AI sessions#294
Merged
Conversation
User API Keys are scoped to a user, not an org, so jwt.limacharlie.io can only resolve them when the UID accompanies the secret. The AI SDK sent X-LC-OID + Authorization but never X-LC-UID, so a User API Key could not authenticate ai start-session (or any ai session command) and the server rejected it as an invalid org key. ai-sessions' OrgDualAuthMiddleware already reads X-LC-UID for the raw-API-key path (and ignores it for JWT auth), so no server change is needed -- the SDK just has to send it. Add AI._org_auth_headers() which always sets X-LC-OID and adds X-LC-UID when the client has a uid, and route both start_session() and _org_request() through it so the entire org-scoped AI surface (start-session, session list/get/terminate/history, usage) works under a User API Key. Tests: fix the mock_org fixture to set _uid = None (otherwise the MagicMock auto-vivifies a truthy _uid and leaks X-LC-UID into every request) and add TestOrgRequestUidHeader covering both the org-scoped (no header) and user-scoped (header forwarded) cases for start_session and the _org_request path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Running
limacharlie ai start-session(or anyai sessioncommand) under a User API Key failed with401 "Invalid API key for organization". The user reported it as "the backend requires an Org API key to run an Org based session."The cause is purely client-side.
AI.start_session()andAI._org_request()sentX-LC-OID+Authorization: Bearer <key>but neverX-LC-UID. A User API Key is scoped to a user, not an org, so jwt.limacharlie.io can only resolve it when the UID accompanies the secret. Without the UID, the JWT service tries to validate the key as an org key, doesn't find it (user keys live under the user's key store), and rejects the request.The backend already supports this:
ai-sessions'OrgDualAuthMiddlewarereadsX-LC-UIDfor the raw-API-key path (middleware_org_jwt.go) and ignores it for JWT auth, so no server change is required — the SDK just has to send the header.Fix
AI._org_auth_headers(): always setsX-LC-OID, and addsX-LC-UIDwhen the client has a uid. Safe to send unconditionally (ignored for JWT auth).start_session()and_org_request()through it, so the whole org-scoped AI surface works under a User API Key:start-session,session list/get/terminate/history,usage.Tests
mock_orgfixture to set_uid = None— otherwise theMagicMockauto-vivifies a truthy_uidand would leak anX-LC-UIDheader into every request.TestOrgRequestUidHeadercovering org-scoped (no header) and user-scoped (header forwarded) cases for bothstart_sessionand the_org_requestpath.Results: AI SDK suites
84 passed(incl. 4 new tests). Full unit suite change is isolated — the only failures (test_search_checkpoint_cli.py,test_search_output.py) are pre-existing and unrelated, confirmed identical on the baseline with these changes stashed.🤖 Generated with Claude Code