Skip to content

feat(config): per-upstream auth_broker block + teams credential keys (spec 074, MCP-1035)#588

Merged
Dumbris merged 4 commits into
mainfrom
074-t2-config-auth-broker
Jun 4, 2026
Merged

feat(config): per-upstream auth_broker block + teams credential keys (spec 074, MCP-1035)#588
Dumbris merged 4 commits into
mainfrom
074-t2-config-auth-broker

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 4, 2026

Copy link
Copy Markdown
Member

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_connect
  • token_endpoint, resource (RFC 8707 audience), scopes[], client_id, client_secret
  • header (default Authorization) + header_format (default Bearer {token}) — FR-016

Teams-level keys (teams_config.go)

  • credential_encryption_key — env fallback MCPPROXY_CRED_KEY (explicit config wins)
  • store_idp_tokens booldefault false, privacy-preserving (FR-006)

Validation

  • Rejects auth_broker on stdio / non-HTTP-family upstreams with a clear "unsupported in this phase" message (FR-002).
  • HTTP-family upstreams (http / sse / streamable-http, plus inferred URL-only) pass; header defaults applied in place.
  • Opt-in per server; upstreams without a broker behave exactly as today (FR-003).

Edition isolation

AuthBrokerConfig + its validator live behind //go:build server; the personal edition gets an empty-struct stub + no-op validator (mirrors the existing TeamsConfig pattern). Personal edition unaffected. AuthBroker carries swaggerignore like Teamsmake swagger-verify confirms 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_tokens default 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

…(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
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

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

View logs

Dumbris added 3 commits June 4, 2026 19:38
# 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-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: 074-t2-config-auth-broker

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (24 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 26966810712 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@Dumbris Dumbris merged commit 0d998b0 into main Jun 4, 2026
37 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants