feat(config): per-upstream auth_broker block + teams credential keys (spec 074, MCP-1035)#588
Merged
Merged
Conversation
…(spec 074)
Add server-edition configuration surface for per-user upstream token
brokering (spec 074, T2 / MCP-1035):
- ServerConfig.AuthBroker *AuthBrokerConfig with mode
(token_exchange|entra_obo|oauth_connect), token_endpoint, resource
(RFC 8707 audience), scopes, client_id/secret, and configurable header
+ header_format (defaults "Authorization" / "Bearer {token}", FR-016).
- TeamsConfig.CredentialEncryptionKey (env fallback MCPPROXY_CRED_KEY,
explicit config wins) and StoreIDPTokens bool, default false (FR-006).
- Validation rejects auth_broker on stdio/non-HTTP-family upstreams with
an "unsupported in this phase" message (FR-002); HTTP-family upstreams
pass and have header defaults applied. Opt-in per server; upstreams
without a broker behave exactly as today (FR-003).
The AuthBrokerConfig type and validation are behind //go:build server with
a personal-edition stub (empty struct + no-op validator), so the personal
edition is unaffected. AuthBroker carries swaggerignore (mirrors Teams) —
swagger-verify confirms no OpenAPI drift.
Related #587
Deploying mcpproxy-docs with
|
| Latest commit: |
c786d39
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://a8cf30c4.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://074-t2-config-auth-broker.mcpproxy-docs.pages.dev |
# Conflicts: # internal/config/config.go
# Conflicts: # internal/config/teams_config.go
…canary AuthBroker (spec 074) is server-edition per-upstream broker config carried in the JSON config (like Shared), not persisted to the BBolt UpstreamRecord. Mark it intentionally excluded in TestSaveServerSyncFieldCoverage so the field-coverage canary passes. Related #588
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 26966810712 --repo smart-mcp-proxy/mcpproxy-go
|
This was referenced Jun 4, 2026
Dumbris
added a commit
that referenced
this pull request
Jun 10, 2026
…MCP-1036) (#601) * feat(teams): persist + refresh IdP subject token at login (spec 074, MCP-1036) Today HandleCallback fetched userinfo then discarded the provider token. When teams.store_idp_tokens is enabled, capture the IdP access + refresh token at login and persist it encrypted (AES-256-GCM) via the credential store as an idp_subject_token record keyed by userID (empty serverKey). Adds GetValidIDPSubjectToken: returns a non-expired token, refreshing via the provider refresh_token grant when expired/near-expiry; when no valid token can be produced (absent, store disabled, expired-and-not-refreshable, or refresh failed) it returns ErrReauthRequired — never a stale token. This is the prerequisite seam consumed by the credential resolver (Path A token exchange, MCP-1039). Default-off: with store_idp_tokens false (default) or no encryption key, login behaves exactly as before — no storage. Persist is best-effort: a write failure never breaks login. - OAuthProvider.RefreshAccessToken: refresh_token grant (RFC 6749 §6) - OAuthHandler.credStore + SetCredentialStore; wired in teams/setup.go - TDD: capture-when-enabled, no-storage-when-disabled, refresh-near-expiry, expired-not-refreshable -> re-auth, absent -> re-auth, store-disabled -> re-auth FR-004, FR-005, FR-006. Related #588 (T2 config), #587 (T1 store). Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix(teams/auth): request offline access when store_idp_tokens enabled When teams.store_idp_tokens is on, the login authorization request must ask providers for a durable refresh token so GetValidIDPSubjectToken can actually refresh instead of returning ErrReauthRequired after expiry (Codex finding on PR #601). Provider-specific contracts (FR-006 default-off — login unchanged otherwise): - Google: access_type=offline + prompt=consent (OfflineAuthParams) - Microsoft: offline_access scope (OfflineAccessScopes) - GitHub: unchanged (no refresh-token mechanism in scope) BuildAuthURL gains an offlineAccess bool; caller (HandleLogin) sets it from config.StoreIDPTokens. New tests: - TestBuildAuthURL_OfflineAccess_Google / _Microsoft - TestHandleLogin_RequestsOfflineAccess (default-off assertion added to TestHandleLogin_Redirects) Co-Authored-By: Paperclip <noreply@paperclip.ing> * docs: add idp-token-storage operator guide (store_idp_tokens + credential_encryption_key) Satisfies ENG-9 requirement from Codex R2 review on PR #601: document the operator/security model introduced by idp_subject_token.go and setup.go: - store_idp_tokens config field (default off) - credential_encryption_key / MCPPROXY_CRED_KEY env override - AES-256-GCM at-rest encryption model, key generation, key rotation caveat - Token lifecycle (login → use → refresh → re-auth) Co-Authored-By: Paperclip <noreply@paperclip.ing> * docs: fix IdP token-storage bucket name (user_upstream_credentials) CodexReviewer caught that idp-token-storage.md named the key-rotation cleanup bucket as 'upstream_credentials' but the actual store is 'user_upstream_credentials' (internal/teams/broker/bbolt_aes.go:28) — following the doc would clear the wrong bucket and leave IdP tokens behind. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dumbris
added a commit
that referenced
this pull request
Jun 12, 2026
…ition; teams→server_edition key (alias)
Renames the server-edition surface for clarity and to disambiguate from any
"teams" collaboration concept (MCP-1086).
Go:
- config.TeamsConfig→ServerEditionConfig, TeamsOAuthConfig→ServerEditionOAuthConfig,
DefaultTeamsConfig→DefaultServerEditionConfig; Config.Teams→Config.ServerEdition.
- Config key/tag teams→server_edition. Legacy "teams" key still loads onto
ServerEdition via normalize-on-load in loadConfigFile (new key wins). Compiles
in both editions (ServerEditionConfig is a struct{} stub in personal builds).
- Move internal/teams/→internal/serveredition/ (package teams→serveredition,
incl. broker/ from #588); update all imports and selectors.
- Rename exported symbols: TeamsAuthMiddleware→ServerEditionAuthMiddleware,
TeamsStatusInfo→ServerEditionStatusInfo, TeamsInfo→ServerEditionInfo,
wireTeamsOAuth→wireServerEditionOAuth.
- `mcpproxy status -o json` server-edition block key teams→server_edition (aligns
output with the rename).
Edition strings ("personal"/"server"), the `server` build tag, and the
edition_teams.go build-tag filename are unchanged per scope.
Tests:
- New file-load back-compat tests: new server_edition key, legacy teams key,
and both-keys-new-wins (server-tagged).
Docs:
- CLAUDE.md path/config-key references (kept under the 40k char gate); active
feature docs; specs config-key + code-path references. Spec directory slugs
(029-mcpproxy-teams, 024-teams-multiuser-oauth) and historical narrative are
preserved intact.
swagger: teams config is swaggerignore — no REST surface change (verified).
Related MCP-1086
Dumbris
added a commit
that referenced
this pull request
Jun 12, 2026
…ition, teams→server_edition key + alias) (MCP-1086) (#603) * refactor(config): rename Teams→ServerEdition; internal/teams→serveredition; teams→server_edition key (alias) Renames the server-edition surface for clarity and to disambiguate from any "teams" collaboration concept (MCP-1086). Go: - config.TeamsConfig→ServerEditionConfig, TeamsOAuthConfig→ServerEditionOAuthConfig, DefaultTeamsConfig→DefaultServerEditionConfig; Config.Teams→Config.ServerEdition. - Config key/tag teams→server_edition. Legacy "teams" key still loads onto ServerEdition via normalize-on-load in loadConfigFile (new key wins). Compiles in both editions (ServerEditionConfig is a struct{} stub in personal builds). - Move internal/teams/→internal/serveredition/ (package teams→serveredition, incl. broker/ from #588); update all imports and selectors. - Rename exported symbols: TeamsAuthMiddleware→ServerEditionAuthMiddleware, TeamsStatusInfo→ServerEditionStatusInfo, TeamsInfo→ServerEditionInfo, wireTeamsOAuth→wireServerEditionOAuth. - `mcpproxy status -o json` server-edition block key teams→server_edition (aligns output with the rename). Edition strings ("personal"/"server"), the `server` build tag, and the edition_teams.go build-tag filename are unchanged per scope. Tests: - New file-load back-compat tests: new server_edition key, legacy teams key, and both-keys-new-wins (server-tagged). Docs: - CLAUDE.md path/config-key references (kept under the 40k char gate); active feature docs; specs config-key + code-path references. Spec directory slugs (029-mcpproxy-teams, 024-teams-multiuser-oauth) and historical narrative are preserved intact. swagger: teams config is swaggerignore — no REST surface change (verified). Related MCP-1086 * docs: finish Teams→ServerEdition rename leftovers (MCP-1795) Codex review follow-up for PR #603 / MCP-1086: - generate-release-notes.sh: server-edition code path internal/teams/ → internal/serveredition/ - specs/029-mcpproxy-teams/tasks.md: build-tag docs //go:build teams / -tags teams → server, matching the accepted scope (build tag remains "server", current code uses //go:build server). Package paths were already renamed by PR #603; only the build-tag references were stale. Edition-name/wording strings left untouched (owned by MCP-1087). Related #603 * docs: fix stale build-tag references (teams -> server) in specs/029 and related docs All //go:build teams, -tags teams, internal/teams/ references updated to //go:build server, -tags server, internal/serveredition/ to match the actual build tags used throughout the codebase. Related #603 * fix(frontend): rebind server-edition settings + docs to server_edition key (MCP-1086) Address CodexReviewer REQUEST_CHANGES on PR #603. The backend renamed the canonical config key teams -> server_edition; the settings UI was still bound to the legacy teams object, so after the rename the Server Edition tab would disappear and edits would reintroduce the legacy key. - Settings.vue: gate the Server Edition tab on server_edition (fallback to the legacy teams key), and alias a legacy teams-keyed config onto server_edition at load so old configs still hydrate the form. - settings/fields.ts: write/read the canonical server_edition.* dot-paths; the shared SettingsSection read (getPath) and PATCH partial (buildPartial) follow the field keys, so both now target server_edition. - idp-token-storage.md + idp_subject_token.go: rename operator-facing refs to server_edition, noting teams is still accepted as a back-compat alias. Verified: vitest (server-edition wording spec), frontend build (vue-tsc), go build -tags server ./cmd/mcpproxy, go build ./cmd/mcpproxy. Related #603
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
Spec 074 T2 (MCP-1035) — the config-schema foundation for per-user upstream token brokering (server edition). No blockers; parallel with T1.
Per-upstream
auth_broker(server edition)ServerConfig.AuthBroker *AuthBrokerConfig:mode:token_exchange|entra_obo|oauth_connecttoken_endpoint,resource(RFC 8707 audience),scopes[],client_id,client_secretheader(defaultAuthorization) +header_format(defaultBearer {token}) — FR-016Teams-level keys (
teams_config.go)credential_encryption_key— env fallbackMCPPROXY_CRED_KEY(explicit config wins)store_idp_tokens bool— default false, privacy-preserving (FR-006)Validation
auth_brokeron stdio / non-HTTP-family upstreams with a clear "unsupported in this phase" message (FR-002).http/sse/streamable-http, plus inferred URL-only) pass; header defaults applied in place.Edition isolation
AuthBrokerConfig+ its validator live behind//go:build server; the personal edition gets an empty-struct stub + no-op validator (mirrors the existingTeamsConfigpattern). Personal edition unaffected.AuthBrokercarriesswaggerignorelikeTeams—make swagger-verifyconfirms no OpenAPI drift.Testing (TDD, test-first)
internal/config/auth_broker_test.go: defaults, valid HTTP broker, stdio + implied-stdio rejection, invalid/missing mode, missing token_endpoint, all valid modes, no-broker-unaffected, JSON round-trip.internal/config/teams_credential_test.go:store_idp_tokensdefault false + parse, cred-key parse, env fallback, config-wins-over-env.Verification (all green):
go test ./internal/config/... -race(personal) ✅go test -tags server ./internal/config/... -race✅go build ./cmd/mcpproxy+go build -tags server ./cmd/mcpproxy✅./scripts/run-linter.sh→ 0 issues ✅make swagger-verify→ up to date ✅FR-002, FR-003, FR-006, FR-016 (config side).
Related #587