Skip to content

feat: add OAuth2ConsentHandler with VK, session, and user identity resolution modes#4507

Open
Pratham-Mishra04 wants to merge 1 commit into
06-17-feat_adds_mcp_oauth2_authorization_serverfrom
06-17-feat_adds_mcp_oauth2_consent_flow_backend
Open

feat: add OAuth2ConsentHandler with VK, session, and user identity resolution modes#4507
Pratham-Mishra04 wants to merge 1 commit into
06-17-feat_adds_mcp_oauth2_authorization_serverfrom
06-17-feat_adds_mcp_oauth2_consent_flow_backend

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

Introduces the OAuth2 consent flow API, enabling MCP clients to complete the OAuth2 authorization code flow by presenting a consent page where users can select how they want to identify themselves (via virtual key, session, or user identity).

Changes

  • Added OAuth2ConsentHandler with two endpoints:
    • GET /api/oauth2/consent/flows/{id} — returns flow details, available identity modes, and the currently logged-in user (if a valid session exists)
    • PUT /api/oauth2/consent/flows/{id} — resolves identity, mints an authorization code (storing only its SHA256 hash), marks the flow as consented, invalidates the temp token, and returns the redirect URL with the code and state per RFC 6749 §4.1.2 and RFC 9207
  • Added OAuth2IdentityResolver interface as an optional extension point for user identity resolution. When nil, only vk and session modes are offered. When provided, a user mode becomes available if the resolver reports it is configured.
  • VK identity resolution includes a user-binding upgrade path: if a VK is bound to a specific user and an identity provider is configured, the currently logged-in session must match the VK owner before the upgrade proceeds.
  • Session mode tokens are server-minted and never client-asserted.
  • The consent handler is registered in RegisterAPIRoutes, defaulting to a nil identity resolver if one has not been pre-assigned to BifrostHTTPServer.OAuth2ConsentHandler.

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 ./...
  1. Start the server and initiate an OAuth2 authorization request to obtain a flow ID and a consent temp token.
  2. Call GET /api/oauth2/consent/flows/{id} with the temp token — verify client_name, available_modes, expires_at, and optionally logged_in_user are returned.
  3. Call PUT /api/oauth2/consent/flows/{id} with {"mode": "vk", "value": "<plaintext_vk>"} — verify the response contains a redirect_url with code, state, and iss query parameters.
  4. Confirm the flow status is updated to consented and the temp token is invalidated (a second PUT should fail).
  5. Repeat with mode: session (when EnforceAuthOnInference is false) and mode: user (when an identity resolver is configured).

Screenshots/Recordings

N/A

Breaking changes

  • Yes
  • No

Related issues

N/A

Security considerations

  • Authorization codes are generated with 32 bytes of cryptographic randomness; only the SHA256 hash is persisted — the plaintext is transmitted once via the redirect URI and never stored (RFC 6749 §4.1.2).
  • The issuer is included in the redirect per RFC 9207 to allow clients to validate the authorization server.
  • The consent temp token is invalidated immediately after a successful submission to prevent replay.
  • User-bound VKs require a matching active session before the identity upgrade is granted, preventing possession of a VK alone from impersonating a bound user.

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

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.

Pratham-Mishra04 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Pratham-Mishra04, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 7 minutes and 16 seconds. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 71669b96-decd-4fd7-8291-e08ba2604937

📥 Commits

Reviewing files that changed from the base of the PR and between 76885d3 and b8e3cc8.

📒 Files selected for processing (2)
  • transports/bifrost-http/handlers/oauth2_consent.go
  • transports/bifrost-http/server/server.go
📝 Walkthrough

Walkthrough

Adds a new oauth2_consent.go handler file implementing two endpoints (GET and PUT /api/oauth2/consent/flows/{id}) for the OAuth2 consent flow. The handler validates pending flows, computes available identity modes from server config and an optional OAuth2IdentityResolver interface, resolves identity, mints an authorization code, and returns a redirect URL. BifrostHTTPServer gains an OAuth2ConsentHandler field that is lazily initialized and registered in RegisterAPIRoutes.

Changes

OAuth2 Consent Flow Handler

Layer / File(s) Summary
Interface, struct, and server wiring
transports/bifrost-http/handlers/oauth2_consent.go, transports/bifrost-http/server/server.go
Defines OAuth2IdentityResolver interface (IsUserModeAvailable, ResolveUserIdentity, ResolveVKUserUpgrade) and OAuth2ConsentHandler struct with constructor and RegisterRoutes. Adds the handler field to BifrostHTTPServer and lazily initializes and wires its routes in RegisterAPIRoutes.
GET consent flow endpoint
transports/bifrost-http/handlers/oauth2_consent.go
Defines JSON response shapes including optional logged_in_user block. Implements the GET handler: validates config store and flow ID, loads the pending flow, fetches the OAuth2 client, computes available modes, and optionally attaches the logged-in user from the identity resolver.
PUT consent submission, identity resolution, and redirect URL
transports/bifrost-http/handlers/oauth2_consent.go
Defines consent mode constants and request/response types. Implements the PUT handler (JSON decode, mode validation, identity resolution, auth-code minting with SHA256 storage, status update, temp-token invalidation, redirect URL construction). Includes loadPendingFlow, availableModes, resolveIdentity, resolveVKIdentity (VK validation and optional user upgrade), and buildRedirectURL.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • akshaydeo
  • danpiths

Poem

🐇 A new consent flow hops into view,
With VK and session and user modes too.
SHA256 hashes the code just right,
The redirect URL built with delight.
The rabbit checks modes and TTL with care —
OAuth2 consent is now handled with flair! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature addition: an OAuth2ConsentHandler supporting VK, session, and user identity resolution modes, which directly corresponds to the primary changes in the PR.
Description check ✅ Passed The PR description includes all required template sections: summary, changes, type of change, affected areas, testing instructions, breaking changes, security considerations, and a completed checklist. The content is comprehensive and well-detailed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-17-feat_adds_mcp_oauth2_consent_flow_backend

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: 4/5

Safe to merge after addressing the raw error forwarding to OAuth clients, which leaks infrastructure details on resolver failures.

The atomic consent operation and corrected buildRedirectURL remove the most serious concerns from earlier rounds. One new defect remains: errors from ResolveVKUserUpgrade and ResolveUserIdentity are %w-wrapped and passed verbatim to the OAuth client via err.Error(), exposing internal addresses and driver messages on infrastructure failures.

transports/bifrost-http/handlers/oauth2_consent.go — the identity-resolver error forwarding paths at resolveVKIdentity line 322, resolveIdentity line 285, and flowSubmit line 178.

Important Files Changed

Filename Overview
transports/bifrost-http/handlers/oauth2_consent.go New OAuth2 consent handler with GET (flow detail) and PUT (identity resolution + code mint) endpoints. The atomic ConsentOAuth2AuthorizeRequest and error-returning buildRedirectURL address previously flagged concerns, but internal resolver errors are forwarded verbatim to OAuth clients and temp-token deletion errors are silently swallowed.
transports/bifrost-http/server/server.go Adds OAuth2ConsentHandler as an exported field on BifrostHTTPServer and wires it in RegisterAPIRoutes with a nil-guard that defaults to no identity resolver. Change is minimal and self-contained.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as MCP Client
    participant AS as Authorization Server
    participant UI as Consent Page
    participant CH as OAuth2ConsentHandler
    participant DB as ConfigStore

    C->>AS: GET /oauth2/authorize
    AS->>DB: "CreateOAuth2AuthorizeRequest (status=pending)"
    AS->>AS: Mint temp token
    AS-->>C: "302 to /oauth/consent?flow={id}"

    UI->>CH: "GET /api/oauth2/consent/flows/{id}"
    CH->>DB: GetOAuth2AuthorizeRequestByID
    CH->>DB: GetOAuth2ClientByClientID
    CH-->>UI: client_name, available_modes, expires_at

    UI->>CH: "PUT /api/oauth2/consent/flows/{id}"
    CH->>CH: resolveIdentity
    CH->>CH: generateSecureToken(32)
    CH->>DB: ConsentOAuth2AuthorizeRequest (atomic)
    CH->>CH: TempTokens.DeleteByResourceID
    CH-->>UI: redirect_url

    UI->>C: Navigate to redirect_url
    C->>AS: POST /oauth2/token
    AS->>DB: GetOAuth2AuthorizeRequestByCodeHash
    AS->>AS: Verify PKCE S256
    AS->>DB: ConsumeOAuth2AuthorizeRequest
    AS-->>C: access_token, refresh_token
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 C as MCP Client
    participant AS as Authorization Server
    participant UI as Consent Page
    participant CH as OAuth2ConsentHandler
    participant DB as ConfigStore

    C->>AS: GET /oauth2/authorize
    AS->>DB: "CreateOAuth2AuthorizeRequest (status=pending)"
    AS->>AS: Mint temp token
    AS-->>C: "302 to /oauth/consent?flow={id}"

    UI->>CH: "GET /api/oauth2/consent/flows/{id}"
    CH->>DB: GetOAuth2AuthorizeRequestByID
    CH->>DB: GetOAuth2ClientByClientID
    CH-->>UI: client_name, available_modes, expires_at

    UI->>CH: "PUT /api/oauth2/consent/flows/{id}"
    CH->>CH: resolveIdentity
    CH->>CH: generateSecureToken(32)
    CH->>DB: ConsentOAuth2AuthorizeRequest (atomic)
    CH->>CH: TempTokens.DeleteByResourceID
    CH-->>UI: redirect_url

    UI->>C: Navigate to redirect_url
    C->>AS: POST /oauth2/token
    AS->>DB: GetOAuth2AuthorizeRequestByCodeHash
    AS->>AS: Verify PKCE S256
    AS->>DB: ConsumeOAuth2AuthorizeRequest
    AS-->>C: access_token, refresh_token
Loading

Reviews (10): Last reviewed commit: "feat: adds mcp oauth2 consent flow backe..." | Re-trigger Greptile

Comment thread transports/bifrost-http/handlers/oauth2_consent.go
Comment thread transports/bifrost-http/handlers/oauth2_consent.go
Comment thread transports/bifrost-http/handlers/oauth2_consent.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.

🧹 Nitpick comments (2)
transports/bifrost-http/handlers/oauth2_consent.go (2)

335-348: 💤 Low value

Consider handling URL parse failures more explicitly.

If url.Parse fails, the function returns the base string unchanged without logging or propagating the error. While the redirect_uri is sourced from the database and should have been validated during the authorize request creation, silent failures can hide configuration or data-integrity issues.

Optional improvement
 func buildRedirectURL(base string, params map[string]string) string {
 	u, err := url.Parse(base)
 	if err != nil {
+		logger.Warn("failed to parse redirect_uri %q: %v; returning base unchanged", base, err)
 		return base
 	}
 	q := u.Query()
 	for k, v := range params {
 		if v != "" {
 			q.Set(k, v)
 		}
 	}
 	u.RawQuery = q.Encode()
 	return u.String()
 }
🤖 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/bifrost-http/handlers/oauth2_consent.go` around lines 335 - 348,
The buildRedirectURL function silently returns the base string when url.Parse
fails without logging the error, which can hide configuration or data-integrity
issues. Add explicit error logging when the url.Parse call fails in the
buildRedirectURL function so that URL parsing failures are properly reported and
can be tracked for debugging configuration or data issues.

202-205: ⚡ Quick win

Consider logging temp token deletion failures.

The temp token invalidation error is silently ignored. While the primary replay protection is the consented status in the database (line 194), logging cleanup failures would improve observability and help detect issues with the temp token service.

Suggested improvement
 // Invalidate the temp token so the consent page cannot be submitted twice.
 if h.tempTokens != nil {
-	_, _ = h.tempTokens.DeleteByResourceID(ctx, temptoken.OAuth2ConsentScopeName, flowID)
+	if _, err := h.tempTokens.DeleteByResourceID(ctx, temptoken.OAuth2ConsentScopeName, flowID); err != nil {
+		logger.Warn("failed to invalidate oauth2 consent temp token for flow %s: %v", flowID, err)
+	}
 }
🤖 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/bifrost-http/handlers/oauth2_consent.go` around lines 202 - 205,
The error returned from the h.tempTokens.DeleteByResourceID call is being
silently ignored with blank identifiers. Instead of discarding the error,
capture it and check if it is not nil, then log the error using an appropriate
logger (such as h's logger or a context logger) to improve observability and
help detect issues with the temp token service. This logging should still occur
even though the primary replay protection is the consented status in the
database, as it helps track cleanup failures.
🤖 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.

Nitpick comments:
In `@transports/bifrost-http/handlers/oauth2_consent.go`:
- Around line 335-348: The buildRedirectURL function silently returns the base
string when url.Parse fails without logging the error, which can hide
configuration or data-integrity issues. Add explicit error logging when the
url.Parse call fails in the buildRedirectURL function so that URL parsing
failures are properly reported and can be tracked for debugging configuration or
data issues.
- Around line 202-205: The error returned from the
h.tempTokens.DeleteByResourceID call is being silently ignored with blank
identifiers. Instead of discarding the error, capture it and check if it is not
nil, then log the error using an appropriate logger (such as h's logger or a
context logger) to improve observability and help detect issues with the temp
token service. This logging should still occur even though the primary replay
protection is the consented status in the database, as it helps track cleanup
failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5019294d-9091-4622-a89e-cc35d7b83b21

📥 Commits

Reviewing files that changed from the base of the PR and between a3e6af6 and df31ae6.

📒 Files selected for processing (2)
  • transports/bifrost-http/handlers/oauth2_consent.go
  • transports/bifrost-http/server/server.go

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_authorization_server branch from a3e6af6 to 8bebc42 Compare June 18, 2026 07:34
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from df31ae6 to 6ebebaf Compare June 18, 2026 07:34
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_authorization_server branch from 8bebc42 to 0c48164 Compare June 18, 2026 07:37
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from 6ebebaf to 7a09a9d Compare June 18, 2026 07:37
Comment thread transports/bifrost-http/handlers/oauth2_consent.go Outdated
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_authorization_server branch from 0c48164 to 4eb43f0 Compare June 18, 2026 08:17
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from 7a09a9d to 2be7d78 Compare June 18, 2026 08:17
Comment thread transports/bifrost-http/handlers/oauth2_consent.go
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from 76885d3 to d82df7b Compare June 18, 2026 12:22
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_authorization_server branch from d37e5e4 to 5117e2b Compare June 18, 2026 12:22
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from d82df7b to b8e3cc8 Compare June 22, 2026 13:15
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_authorization_server branch from 5117e2b to 6f3e346 Compare June 22, 2026 13:15
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from b8e3cc8 to b4ad168 Compare June 22, 2026 16:58
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_authorization_server branch from 6f3e346 to a9ce98e Compare June 22, 2026 16:58
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from b4ad168 to 314e59d Compare June 23, 2026 05:30
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_authorization_server branch from a9ce98e to 2ca91f0 Compare June 23, 2026 05:30
Comment thread transports/bifrost-http/handlers/oauth2_consent.go
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_adds_mcp_oauth2_consent_flow_backend branch from 314e59d to 2c2f614 Compare June 23, 2026 05:53
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