Skip to content

feat: add OAuth2 AS discovery endpoints, signing key management, and MCPServerAuthMode config#4505

Open
Pratham-Mishra04 wants to merge 10 commits into
devfrom
06-16-feat_adds_mcp_server_oauth_tables
Open

feat: add OAuth2 AS discovery endpoints, signing key management, and MCPServerAuthMode config#4505
Pratham-Mishra04 wants to merge 10 commits into
devfrom
06-16-feat_adds_mcp_server_oauth_tables

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces the foundational OAuth 2.1 authorization server infrastructure for Bifrost's /mcp endpoint. It adds a configurable mcp_server_auth_mode that controls how inbound MCP clients are authenticated, enabling Bifrost to act as a spec-compliant OAuth 2.1 AS with RFC-mandated discovery endpoints, a JWKS endpoint, and a persistent RS256 signing keypair.

Changes

  • MCPServerAuthMode — new varchar column on TableClientConfig with three modes:
    • headers (default): existing VK/api-key/session header auth only; discovery endpoints return 404.
    • both: accepts both header credentials and Bifrost-issued JWTs; discovery endpoints are live.
    • oauth: Bifrost JWTs only; header credentials are rejected on /mcp. Breaking for existing VK-based MCP integrations.
  • OAuth2ServerConfig — new JSON blob column on TableClientConfig holding AS-specific settings (IssuerURL, AuthCodeTTL, AccessTokenTTL). Serialized via BeforeSave/AfterFind hooks. Only meaningful when mode is both or oauth.
  • OAuth2SigningKey — RS2048 keypair generated on first use and persisted in governance_config under oauth2_signing_key. The private key PEM is encrypted at rest via framework/encrypt when encryption is enabled.
  • GetOAuth2SigningKey — new ConfigStore interface method that lazily generates and persists the signing keypair on first call, always returning a usable key.
  • OAuth2DiscoveryHandler — serves the three well-known discovery endpoints:
    • GET /.well-known/oauth-protected-resource[/{path}] (RFC 9728)
    • GET /.well-known/oauth-authorization-server[/{path}] (RFC 8414)
    • GET /.well-known/jwks.json (RFC 7517)
      All three return 404 when MCPServerAuthMode is headers. Routes are always registered; the mode flag is the feature toggle.
  • oauth2IssuerURL / oauth2ServerCfg — utility helpers that resolve the effective issuer URL (configured IssuerURL or request-derived fallback) and AS config defaults.
  • OAuth2ConsentScopeName — new temp-token scope for binding browser sessions to pending authorization requests on the public consent page.
  • Database migration add_oauth2_server_tables — adds mcp_server_auth_mode and oauth2_server_config_json columns to config_client.
  • Config schemamcp_server_auth_mode and oauth2_server_config added to config.schema.json with full descriptions and validation.
  • IsMCPOAuthDiscoveryEnabled — helper on ClientConfig that returns true when mode is both or oauth.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./framework/configstore/... ./transports/bifrost-http/...
  1. Start Bifrost with mcp_server_auth_mode unset (or "headers"). Confirm GET /.well-known/oauth-authorization-server returns 404.
  2. Set mcp_server_auth_mode to "both" and restart. Confirm:
    • GET /.well-known/oauth-authorization-server returns a valid JSON document with issuer, authorization_endpoint, token_endpoint, etc.
    • GET /.well-known/oauth-protected-resource returns a document pointing to /mcp.
    • GET /.well-known/jwks.json returns a JWKS with one RS256 key entry.
  3. Confirm the signing keypair is persisted in governance_config and survives a restart (same kid returned).
  4. Set mcp_server_auth_mode to "oauth" and confirm header-credential MCP requests are rejected.

New config fields:

Field Type Default Description
mcp_server_auth_mode "headers" | "both" | "oauth" "headers" Inbound MCP client auth mode
oauth2_server_config.issuer_url string / env var (request host) Stable AS issuer URL
oauth2_server_config.auth_code_ttl int (seconds) 600 Authorization code lifetime
oauth2_server_config.access_token_ttl int (seconds) 600 JWT Bearer token lifetime

Breaking changes

  • Yes
  • No

Setting mcp_server_auth_mode to "oauth" disables VK/api-key/session header authentication on /mcp. Existing virtual-key MCP integrations will stop working. Use "both" for a non-breaking migration path that accepts both credential types simultaneously.

Security considerations

  • The RS256 private key is encrypted at rest using framework/encrypt when encryption is enabled. The plaintext key is only held in memory during the signing operation.
  • The OAuth2ConsentScopeName temp token is the sole credential binding a browser session to a pending authorization request on the public (unauthenticated) consent page — it must be treated as a short-lived secret.
  • Refresh tokens have no timer-based expiry; they are invalidated only by rotation on use, subject liveness checks, explicit revocation, or enforcement policy changes.
  • Multi-host / reverse-proxy deployments must set a stable issuer_url; omitting it causes token verification failures when the Host header differs across nodes.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@CLAassistant

CLAassistant commented Jun 17, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features
    • Added OAuth2 authentication support for MCP endpoints with configurable authorization server settings.
    • Introduced MCP authentication mode options: headers-only, mixed header/OAuth2, or OAuth2-only.
    • Added well-known OAuth2 discovery endpoints for metadata, protected resource configuration, and key information.
    • Configured authorization code and access token lifetimes for OAuth2 flows.

Walkthrough

Adds MCP inbound OAuth2 authentication infrastructure: new MCPServerAuthMode and OAuth2ServerConfig types, a DB migration to persist them, RSA-2048 signing-key generation and encrypted storage via GetOAuth2SigningKey, three well-known OAuth2 discovery endpoints (PRM, AS metadata, JWKS), config handler validation, auth middleware whitelisting, and a new OAuth2ConsentScopeName constant.

Changes

MCP OAuth2 Discovery Infrastructure

Layer / File(s) Summary
OAuth2 types, constants, and config fields
framework/configstore/tables/oauth2_server.go, framework/configstore/tables/clientconfig.go, framework/configstore/clientconfig.go, framework/temptoken/scope.go, transports/config.schema.json
Defines MCPServerAuthMode modes, OAuth2ServerConfig, OAuth2SigningKey with Encrypt/Decrypt, and GovernanceConfigKeyOAuth2SigningKey; extends ClientConfig and TableClientConfig with new fields, BeforeSave/AfterFind JSON hooks, IsMCPOAuthDiscoveryEnabled(), and config-hash incorporation; adds OAuth2ConsentScopeName; documents new schema properties.
DB migration and RDB read/write/key-gen paths
framework/configstore/migrations.go, framework/configstore/store.go, framework/configstore/rdb.go
Adds add_oauth2_server_tables migration with conditional DDL and rollback; extends UpdateClientConfig/GetClientConfig to persist the new fields; adds GetOAuth2SigningKey to the ConfigStore interface and implements RSA-2048 keypair generation, optional encryption, governance config persistence with ON CONFLICT DO NOTHING race handling, and decrypt-on-load.
OAuth2 discovery handler and well-known endpoints
transports/bifrost-http/handlers/oauth2_discovery.go, transports/bifrost-http/handlers/oauth2_utils.go
Implements OAuth2DiscoveryHandler with PRM, AS metadata, and JWKS endpoints gated by IsMCPOAuthDiscoveryEnabled; JWKS loads the signing key, parses RSA public PEM, and converts to JWK; adds oauth2IssuerURL, oauth2MCPResourceURL, and oauth2ServerCfg helpers.
Config handler updates, middleware, and route wiring
transports/bifrost-http/handlers/config.go, transports/bifrost-http/handlers/middlewares.go, transports/bifrost-http/server/server.go, transports/bifrost-http/lib/config_test.go
Validates and conditionally copies MCPServerAuthMode/OAuth2ServerConfig in updateConfig with effective-mode computation for partial updates; whitelists /.well-known/ in APIMiddleware; registers OAuth2DiscoveryHandler routes in RegisterAPIRoutes; adds GetOAuth2SigningKey stub to MockConfigStore.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OAuth2DiscoveryHandler
  participant ClientConfig
  participant RDBConfigStore
  participant GovernanceConfigDB as GovernanceConfig DB

  Client->>OAuth2DiscoveryHandler: GET /.well-known/jwks.json
  OAuth2DiscoveryHandler->>ClientConfig: IsMCPOAuthDiscoveryEnabled()
  alt OAuth discovery disabled
    OAuth2DiscoveryHandler-->>Client: 404 Not Found
  else OAuth discovery enabled
    OAuth2DiscoveryHandler->>RDBConfigStore: GetOAuth2SigningKey(ctx)
    RDBConfigStore->>GovernanceConfigDB: SELECT by GovernanceConfigKeyOAuth2SigningKey
    alt key exists
      GovernanceConfigDB-->>RDBConfigStore: stored JSON
      RDBConfigStore->>RDBConfigStore: unmarshal + Decrypt()
    else key missing
      RDBConfigStore->>RDBConfigStore: rsa.GenerateKey RSA-2048
      RDBConfigStore->>RDBConfigStore: Encrypt() private key
      RDBConfigStore->>GovernanceConfigDB: INSERT ON CONFLICT DO NOTHING
      RDBConfigStore->>RDBConfigStore: use plaintext private PEM
    end
    RDBConfigStore-->>OAuth2DiscoveryHandler: OAuth2SigningKey
    OAuth2DiscoveryHandler->>OAuth2DiscoveryHandler: parseRSAPublicKeyPEM
    OAuth2DiscoveryHandler->>OAuth2DiscoveryHandler: rsaPublicKeyToJWK
    OAuth2DiscoveryHandler-->>Client: 200 {"keys":[...]}
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • akshaydeo
  • danpiths

Poem

🐇 A bunny hops through .well-known/ land,
With RSA keys clutched in each paw and hand.
OAuth modes—headers, both, or oauth pure—
Signing JWKs to keep the MCP secure.
Discovery endpoints bloom like clover in spring,
What wondrous JWT tokens these new routes shall bring! 🗝️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: OAuth2 AS discovery endpoints, signing key management, and MCPServerAuthMode config introduction.
Description check ✅ Passed The description comprehensively covers all required sections: summary, detailed changes, type of change, affected areas, testing instructions, breaking changes disclosure, and security considerations.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-16-feat_adds_mcp_server_oauth_tables

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge; all new functionality is gated behind the opt-in MCPServerAuthMode flag and does not affect existing deployments in their default headers mode.

The key-generation race addressed by ON CONFLICT DO NOTHING is handled correctly. Encrypt/decrypt lifecycle mirrors existing hooks. The two remaining findings are both narrow quality issues on a code path that has no active callers yet (token issuance lands in a follow-up PR).

transports/bifrost-http/handlers/oauth2_discovery.go (double lock / inconsistent PRM response) and transports/bifrost-http/handlers/config.go (missing TTL bounds check).

Important Files Changed

Filename Overview
framework/configstore/rdb.go Adds GetOAuth2SigningKey with lazy key generation, ON CONFLICT DO NOTHING for race-safe first-use creation, and correct encrypt/decrypt lifecycle mirroring existing table hooks.
transports/bifrost-http/handlers/oauth2_discovery.go Discovery handler for RFC 8414, RFC 9728, and RFC 7517 endpoints; double lock acquisition in handlePRM can produce an inconsistent resource/authorization_servers pair under a concurrent config update.
transports/bifrost-http/handlers/config.go Adds MCPServerAuthMode and OAuth2ServerConfig update logic with partial-update semantics; missing server-side enforcement of minimum: 1 for TTL fields that the schema declares.
framework/configstore/tables/oauth2_server.go Defines MCPServerAuthMode enum, OAuth2ServerConfig struct, and OAuth2SigningKey with encrypt/decrypt helpers mirroring the existing BeforeSave/AfterFind pattern; IsSet/GetValue nil-safe via EnvVar methods.
framework/configstore/migrations.go Adds add_oauth2_server_tables migration to add mcp_server_auth_mode and oauth2_server_config_json columns to config_client; includes a rollback; only ADD COLUMN (no index), so no lock risk on large tables.
transports/bifrost-http/handlers/oauth2_utils.go Utility helpers for issuer URL resolution and AS config fallback; EnvVar nil-safety confirmed via IsSet/GetValue nil-pointer-safe receivers.
transports/bifrost-http/handlers/middlewares.go Adds /.well-known/ to the prefix whitelist, correctly using strings.HasPrefix to cover all discovery paths; individual handlers gate 404 when mode is headers.
framework/configstore/tables/clientconfig.go Adds MCPServerAuthMode and OAuth2ServerConfigJSON columns to TableClientConfig with BeforeSave/AfterFind JSON serialization hooks following the existing Metadata pattern.
transports/bifrost-http/server/server.go Registers the OAuth2DiscoveryHandler routes before other handlers; single-line change, correct placement.
transports/config.schema.json Adds mcp_server_auth_mode enum and oauth2_server_config object with issuer_url, auth_code_ttl (minimum: 1), and access_token_ttl (minimum: 1) to the client config schema.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client as MCP Client
    participant WK as /.well-known/* Handler
    participant Store as ConfigStore
    participant DB as governance_config

    Client->>WK: GET /.well-known/oauth-authorization-server
    WK->>Store: ClientConfig.IsMCPOAuthDiscoveryEnabled()
    alt "mode == headers"
        WK-->>Client: 404 Not Found
    else "mode == both | oauth"
        WK->>Store: GetOAuth2SigningKey()
        Store->>DB: "SELECT key WHERE key='oauth2_signing_key'"
        alt key exists
            DB-->>Store: encrypted JSON blob
            Store-->>WK: OAuth2SigningKey (decrypted)
        else first use
            Store->>Store: rsa.GenerateKey(2048)
            Store->>DB: INSERT ON CONFLICT DO NOTHING
            alt insert won
                Store-->>WK: OAuth2SigningKey (plaintext)
            else another caller won
                Store->>DB: SELECT reload
                DB-->>Store: winner's key
                Store-->>WK: OAuth2SigningKey (decrypted)
            end
        end
        WK-->>Client: JSON discovery document / JWKS
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client as MCP Client
    participant WK as /.well-known/* Handler
    participant Store as ConfigStore
    participant DB as governance_config

    Client->>WK: GET /.well-known/oauth-authorization-server
    WK->>Store: ClientConfig.IsMCPOAuthDiscoveryEnabled()
    alt "mode == headers"
        WK-->>Client: 404 Not Found
    else "mode == both | oauth"
        WK->>Store: GetOAuth2SigningKey()
        Store->>DB: "SELECT key WHERE key='oauth2_signing_key'"
        alt key exists
            DB-->>Store: encrypted JSON blob
            Store-->>WK: OAuth2SigningKey (decrypted)
        else first use
            Store->>Store: rsa.GenerateKey(2048)
            Store->>DB: INSERT ON CONFLICT DO NOTHING
            alt insert won
                Store-->>WK: OAuth2SigningKey (plaintext)
            else another caller won
                Store->>DB: SELECT reload
                DB-->>Store: winner's key
                Store-->>WK: OAuth2SigningKey (decrypted)
            end
        end
        WK-->>Client: JSON discovery document / JWKS
    end
Loading

Reviews (5): Last reviewed commit: "feat: adds mcp server oauth tables" | Re-trigger Greptile

Comment thread framework/configstore/rdb.go
Comment thread framework/configstore/tables/oauth2_server.go
Comment thread transports/bifrost-http/handlers/oauth2_discovery.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (1)
transports/config.schema.json (1)

276-280: ⚡ Quick win

Declare the default for mcp_server_auth_mode in schema.

The description documents 'headers' as the default, but the schema does not set "default": "headers". Add it so schema-driven tooling and generated config stay aligned with runtime expectations.

Suggested fix
         "mcp_server_auth_mode": {
           "type": "string",
           "enum": ["headers", "both", "oauth"],
+          "default": "headers",
           "description": "How /mcp authenticates inbound MCP clients. 'headers' (default): VK/api-key/session headers only, discovery disabled. 'both': accepts header credentials and Bifrost-issued JWTs, discovery enabled. 'oauth': Bifrost JWTs only — WARNING: disables VK/header MCP access."
         },
As per coding guidelines, `transports/config.schema.json` is the source of truth for config fields.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@transports/config.schema.json` around lines 276 - 280, The
mcp_server_auth_mode property in the schema documents 'headers' as the default
in its description, but the schema object itself is missing the "default" field
declaration. Add "default": "headers" to the mcp_server_auth_mode object
definition to ensure schema-driven tooling and generated configurations are
aligned with the documented runtime expectations.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/configstore/clientconfig.go`:
- Around line 101-102: The GenerateClientConfigHash function does not include
the newly added MCPServerAuthMode and OAuth2ServerConfig fields in its hash
calculation, which means configuration changes to OAuth2 authentication settings
will not trigger reconciliation. Update the GenerateClientConfigHash function to
include both the MCPServerAuthMode field and the OAuth2ServerConfig field in the
hash computation to ensure config drift is properly detected.

In `@framework/configstore/migrations.go`:
- Around line 10905-10924: The migration with ID migrationName only defines the
Migrate function but lacks explicit rollback behavior. Add a Rollback function
to the migration object that removes the two columns (MCPServerAuthMode and
OAuth2ServerConfigJSON) from tables.TableClientConfig using the migrator's
DropColumn method, or if the migration is non-rollbackable, explicitly return an
error indicating that downgrade is not supported so operators have clear
expectations.

In `@framework/configstore/rdb.go`:
- Around line 6701-6707: The read path decrypts PrivateKeyPEM whenever
encrypt.IsEnabled() is true, but keys created before encryption was enabled are
stored as plaintext and will fail decryption, breaking OAuth operations. To fix
this, add an encryption status marker or version field to track whether each key
was encrypted when stored, then in the decrypt logic (around the
encrypt.IsEnabled() check in the code block starting at line 6701), only attempt
decryption if the encryption marker indicates the key was actually encrypted, or
detect plaintext keys and re-encrypt them on first read when encryption becomes
enabled. Apply the same logic to all similar decrypt paths mentioned in the
comment (also applies to lines 6731-6743).
- Around line 6688-6710: The current lazy signing-key creation has a race
condition where multiple concurrent callers can both miss the row, each
generating and saving different RSA keys, leading to inconsistency between
signed tokens and advertised keys. Fix this by making the creation atomic in the
createOAuth2SigningKey method: use an insert-on-conflict clause with DoNothing
set to true and Columns set to the key field, then check the RowsAffected count.
If RowsAffected is 0 (indicating a conflict where another process inserted
first), reload and decrypt the existing row before returning instead of
returning the newly generated key. Apply this fix to both the
createOAuth2SigningKey call path within GetOAuth2SigningKey and the other
similar signing-key creation location mentioned in the comment range.

In `@framework/configstore/tables/oauth2_server.go`:
- Around line 53-55: The comment for AccessTokenTTL in the OAuth2ServerConfig
incorrectly states the default value as 900 seconds, but the actual
DefaultAccessTokenTTL constant and schema both define it as 600 seconds. Update
the comment for AccessTokenTTL to correct the default value from 900 to 600
seconds to match the actual implementation and prevent confusion during
operational troubleshooting.

In `@transports/bifrost-http/handlers/config.go`:
- Around line 503-504: The assignments to updatedConfig.MCPServerAuthMode and
updatedConfig.OAuth2ServerConfig always overwrite existing values with zero
values when those fields are omitted from the payload during partial updates.
Instead of unconditionally assigning these fields, add conditional checks to
only update updatedConfig.MCPServerAuthMode and updatedConfig.OAuth2ServerConfig
when the corresponding payload.ClientConfig fields are actually provided (not
empty or zero values). This preserves existing runtime and database state when
these OAuth settings are not included in the update request.
- Around line 503-504: The MCPServerAuthMode and OAuth2ServerConfig fields in
the config handler are being assigned directly from the payload without
validating against the schema constraints defined in
transports/config.schema.json. Add validation logic before the assignments to
ensure MCPServerAuthMode conforms to the allowed enum values (headers, both,
oauth) and that OAuth2ServerConfig has the correct shape and applicability
according to the schema. If validation fails, return an appropriate error
response instead of persisting invalid configuration.

In `@transports/bifrost-http/handlers/plugins.go`:
- Around line 589-591: The isEnvVarObject function at line 589 is too permissive
in detecting EnvVar objects, treating any map containing the keys value,
env_var, and from_env as an EnvVar placeholder, even if additional fields are
present. This causes the function to incorrectly return the existing
configuration at line 591, discarding user updates. Tighten the isEnvVarObject
detection logic to only return true for objects that are exclusively EnvVar
objects with only the expected fields and no additional properties. Apply this
same fix to the similar check at lines 633-637 to prevent the same config
rollback issue from occurring in both locations.

In `@transports/bifrost-http/server/server.go`:
- Line 1397: The OAuth2 discovery routes registered by NewOAuth2DiscoveryHandler
are being blocked by APIMiddleware because the `/.well-known/` endpoint prefixes
are not whitelisted. Requests to these endpoints are rejected before reaching
the handler's discoveryEnabled() check. Add "/.well-known/" as a new entry to
the whitelistedPrefixes list in handlers/middlewares.go to allow these endpoints
through the APIMiddleware while letting the individual discovery handlers
continue to control access through their own discoveryEnabled() validation
logic.

In `@transports/config.schema.json`:
- Around line 299-306: The schema definitions for auth_code_ttl and
access_token_ttl fields currently only specify type as integer without enforcing
positive values, allowing zero or negative TTL values that would create invalid
credentials. Add a minimum constraint property to both auth_code_ttl and
access_token_ttl field definitions in the JSON schema to enforce that values
must be greater than zero, ensuring only valid positive integer values are
accepted.

---

Nitpick comments:
In `@transports/config.schema.json`:
- Around line 276-280: The mcp_server_auth_mode property in the schema documents
'headers' as the default in its description, but the schema object itself is
missing the "default" field declaration. Add "default": "headers" to the
mcp_server_auth_mode object definition to ensure schema-driven tooling and
generated configurations are aligned with the documented runtime expectations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b0d9090-5ec7-407d-ace5-a4ae23b1050b

📥 Commits

Reviewing files that changed from the base of the PR and between 8806020 and 53a04f6.

📒 Files selected for processing (14)
  • framework/configstore/clientconfig.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/oauth2_server.go
  • framework/temptoken/scope.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/oauth2_discovery.go
  • transports/bifrost-http/handlers/oauth2_utils.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/plugins_test.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json

Comment thread framework/configstore/clientconfig.go
Comment thread framework/configstore/migrations.go
Comment thread framework/configstore/rdb.go Outdated
Comment thread framework/configstore/rdb.go Outdated
Comment thread framework/configstore/tables/oauth2_server.go
Comment thread transports/bifrost-http/handlers/config.go Outdated
Comment thread transports/bifrost-http/handlers/plugins.go
Comment thread transports/bifrost-http/server/server.go
Comment thread transports/config.schema.json
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-16-feat_adds_mcp_server_oauth_tables branch 2 times, most recently from 226ccda to bd05880 Compare June 18, 2026 07:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/configstore/rdb.go`:
- Around line 6709-6710: In the createOAuth2SigningKey function, before the
final reload attempt (the call to loadOAuth2SigningKey after handling insert
conflicts), add logic to repair any existing empty rows in the database. When an
insert conflict occurs, check if an empty row already exists (where Value is an
empty string or NULL), and if so, update that row with the newly generated key
data before reloading. This repair step should be applied in both locations
mentioned in the issue (around line 6709-6710 in the loadOAuth2SigningKey check
and also around line 6775), preventing the infinite retry loop that occurs when
loadOAuth2SigningKey returns ErrNotFound for empty rows.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 110527b2-c815-4a08-8d76-630f8379c0ad

📥 Commits

Reviewing files that changed from the base of the PR and between 53a04f6 and 226ccda.

📒 Files selected for processing (14)
  • framework/configstore/clientconfig.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/oauth2_server.go
  • framework/temptoken/scope.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/oauth2_discovery.go
  • transports/bifrost-http/handlers/oauth2_utils.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • transports/bifrost-http/handlers/oauth2_utils.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
  • framework/configstore/store.go
  • framework/temptoken/scope.go
  • transports/bifrost-http/handlers/oauth2_discovery.go
  • framework/configstore/tables/clientconfig.go

Comment thread framework/configstore/rdb.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@transports/bifrost-http/handlers/oauth2_utils.go`:
- Around line 27-29: The oauth2MCPResourceURL function concatenates the issuer
URL with "/mcp" without normalizing trailing slashes, which results in "//mcp"
when the issuer URL is configured with a trailing slash. This causes resource
URL mismatches that break token validation. Fix this by trimming any trailing
slash from the result of oauth2IssuerURL(ctx, store) before appending "/mcp" to
ensure consistent, canonical resource URLs across discovery, authorization, and
verification paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f8dbb913-0854-4528-8915-13626b1d3311

📥 Commits

Reviewing files that changed from the base of the PR and between 226ccda and 67e3649.

📒 Files selected for processing (14)
  • framework/configstore/clientconfig.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/oauth2_server.go
  • framework/temptoken/scope.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/oauth2_discovery.go
  • transports/bifrost-http/handlers/oauth2_utils.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (13)
  • framework/temptoken/scope.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/configstore/clientconfig.go
  • transports/bifrost-http/server/server.go
  • framework/configstore/migrations.go
  • transports/config.schema.json
  • framework/configstore/store.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/config.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/oauth2_server.go
  • framework/configstore/rdb.go
  • transports/bifrost-http/handlers/oauth2_discovery.go

Comment thread transports/bifrost-http/handlers/oauth2_utils.go
roroghost17 and others added 4 commits June 18, 2026 17:39
## Summary

Fixes a bug where OTEL plugin headers were being overwritten with redacted placeholder values when saving a plugin configuration. After the multi-profile change, header values stored as plain strings inside the `profiles` array were not being restored from the database before saving, causing real credentials to be replaced with masked values like `****`.

## Changes

- Extracted `restoreRedactedValue` as a standalone recursive helper, replacing the inline logic in `restoreRedactedFromExisting`. This allows the restoration logic to descend into both nested maps and slices.
- Added slice traversal support (index-aligned) so that elements within arrays like the OTEL `profiles` array are individually checked and restored.
- Added plain-string redaction detection so that header values stored as raw strings (rather than `EnvVar` objects) are also restored from the existing DB config when they carry a redaction artifact. Empty strings are intentionally left as-is to allow clearing a value.
- Added `TestRestoreRedacted_OTELProfilesHeaders` to cover both failure modes: slice traversal and plain-string secret restoration. Also asserts that genuinely new (non-redacted) values pass through unchanged.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./transports/bifrost-http/handlers/...
```

Verify that saving an OTEL plugin configuration with multiple profiles, after a GET that returns redacted header values, does not overwrite the stored credentials in the database. Confirm that providing a genuinely new header value still persists correctly.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

This fix ensures that redacted credential placeholders returned to the client are never written back over real secrets stored in the database. The restoration logic only replaces values that are confirmed redaction artifacts; empty strings and non-redacted values are always passed through as-is, preserving the ability to clear a credential intentionally.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-16-feat_adds_mcp_server_oauth_tables branch from 67e3649 to 46280b1 Compare June 18, 2026 12:22
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 18, 2026
@akshaydeo akshaydeo dismissed coderabbitai[bot]’s stale review June 19, 2026 07:23

The merge-base changed after approval.

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.

7 participants