feat(credentials): organization-level shared credentials - #9
Open
suda wants to merge 11 commits into
Open
Conversation
- Use array index as React key in claude-code-limits.tsx to avoid reconciliation bugs when tokens share the same trailing 4 chars; suppress noArrayIndexKey lint rule with explanation - Fix contradictory JSDoc on cacheByToken — remove false claim that raw tokens are not stored as cache keys - Remove unused createMockUser import in claudeCodeLimits.test.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous implementation called https://api.anthropic.com/api/account which does not exist in Anthropic's API for OAuth tokens. The Claude Code CLI actually uses https://api.anthropic.com/api/oauth/profile to fetch subscription/organization info. Update fetchClaudeSubscriptionLimits to call the correct endpoint and parse the organization.organization_type field for the plan name. Per-token usage stats (messages/tokens used vs. limit) are not available from this endpoint, so those fields return 0 and the UI hides them automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Split long lines in formatResetDate and extraUsage rendering to satisfy biome's line-length formatting rules. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract parseBuckets() and parseExtraUsage() helpers from fetchClaudeSubscriptionLimits() to reduce cognitive complexity from 16 to below the max of 15 - Fix import order in claude-code-limits.tsx (Separator before trpc) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Credentials can now be defined once per organization and are inherited by
every project in it; a project-level credential with the same env var key
overrides the org value for that project.
- New org_credentials table (migration 0062) with AAD = org_id encryption
- Org fallback wired into the two chokepoint resolvers
(resolveProjectCredential / resolveAllProjectCredentials), so worker env
injection, secretBuilder, PM discovery, webhook signature verification,
personas, and hasIntegration checks all inherit org values with zero
worker-side changes
- organization.credentials.{list,set,delete} tRPC procedures gated by
per-org admin role (users.ts refinement pattern)
- projects.credentials.list now merges the org tier and reports
source: 'project' | 'org' plus hasOrgFallback per row
- New /settings/credentials page with a grouped key catalog (SCM, PM,
alerting from the credential-role registry; engines from ENGINE_SECRETS)
plus custom keys
- ProjectSecretField shows 'Inherited from org' state, override placeholder,
and revert-to-org delete semantics
- CLI: cascade org credentials-set / credentials-list / credentials-delete
- Shared maskCredentialValue helper replaces the inline masking expression
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reworks the Claude Code subscription limits feature (previously a superadmin-only sidebar widget) into the credential settings surfaces: - claudeCodeLimits.forOrg (org-admin gated, same audience as the org credentials page): usage for every token source in the org — the shared org credential, each project-level override (with project name), and the server env token — with source attribution instead of value dedup. - claudeCodeLimits.forProject (project access): usage for the credential candidates one project can run on — its override, the inherited org token, and the env token — with the credential-system winner marked active, rendered as a picker preview under CLAUDE_CODE_OAUTH_TOKEN on the project engine tab. - Failed sources now return limits: null instead of being silently dropped, so the UI can distinguish 'no data' from 'not configured'. - Shared ClaudeUsageCard display component; sidebar section deleted. - listAllClaudeCodeCredentials now returns projectName for attribution. - Org-role refinement helpers extracted to _shared/orgRole.ts (reused by organization.credentials.* and claudeCodeLimits.forOrg). - Anthropic client added to the auth-header provenance accept list (LLM subscription API, not a PM/SCM/alerting integration). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses four adversarially-verified review findings: - Drop the server env token from both limits procedures. It was doubly wrong: the tRPC handlers run in the dashboard service while workers get the ROUTER service's env (the dashboard's view can be wrong in both directions), and it exposed a host-level operator secret's usage/billing data to tenant org members. Operators who want env-token usage visible should store it as an org credential — the feature's whole point. - Fix the active flag semantics that followed from env removal: project override wins, else org token is active. - Per-row decrypt resilience: listAllClaudeCodeCredentials skips undecryptable rows with a warning instead of 500ing the endpoint; new getProjectOwnCredential reads exactly one project-tier row (no org fallback, null on decrypt failure) instead of decrypting every project credential just to find the token; forOrg treats an undecryptable org token as absent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Member
Author
|
Scope extended: the Claude Code subscription limits feature (previously PR #3's superadmin sidebar widget) is now folded into the credential views on this branch:
Adversarial review workflow ran over the diff (3 lenses, per-finding verification); all 4 confirmed findings fixed in fb628fa. |
5 tasks
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.
Summary
Credentials (PM keys, GitHub/GitLab tokens, Sentry, engine/LLM keys) can now be defined once per organization and are inherited by every project in it. A project-level credential with the same env var key overrides the org value for that project — so a new project needs zero credential re-entry.
How it works
org_credentialstable (migration 0062), values encrypted with AAD =org_id(project credentials keep AAD =project_id).resolveProjectCredential/resolveAllProjectCredentials). Everything downstream inherits org values automatically: worker env injection,secretBuilder, PM discovery, webhook signature verification, GitHub personas,hasIntegrationchecks. Zero worker-side changes — the router merges tiers before flatteningCASCADE_CREDENTIAL_KEYS.organization.credentials.{list,set,delete}, gated by per-org admin role (same refinement pattern asusers.ts). List returns masked previews only.projects.credentials.listnow merges the org tier: each row carriessource: 'project' | 'org'andhasOrgFallback; existing consumers keep working unchanged.ENGINE_SECRETS) plus free-form custom keys.ProjectSecretFieldshows an "Inherited from org ****xxxx" badge, an "Override organization value..." placeholder, and revert-to-org delete semantics.cascade org credentials-set / credentials-list / credentials-delete.Notes for review
GITHUB_WEBHOOK_SECRETnow inherits the org secret — a previously-unverified webhook path may start enforcing verification. Intended.CODEX_AUTH_JSONwrite-back) intentionally stays project-scoped, creating an override over an org-inherited seed (documented inline).projects.credentials.deleteneeded no changes.Testing
orgCredentialsRepositoryCRUD/AAD tests; fallback matrix incredentialsRepository; org-admin gating + masking inorganizationrouter; DTO merge cases inprojectsrouter).coalesce-bullmq+ 2github-personastests require a local Redis (confirmed failing on clean tree).🤖 Generated with Claude Code