Skip to content

feat(oauth): Add RFC 8707 resource indicator support#736

Open
MariaChrysafis wants to merge 3 commits into
mark3labs:mainfrom
MariaChrysafis:maria/resource
Open

feat(oauth): Add RFC 8707 resource indicator support#736
MariaChrysafis wants to merge 3 commits into
mark3labs:mainfrom
MariaChrysafis:maria/resource

Conversation

@MariaChrysafis
Copy link
Copy Markdown
Contributor

@MariaChrysafis MariaChrysafis commented Mar 7, 2026

Description

Adds the resource parameter (RFC 8707) to OAuth authorization, token exchange, and refresh requests so authorization servers can audience-restrict issued tokens per the MCP auth specification.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • MCP spec compatibility implementation
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Performance improvement
  • Tests only (no functional changes)
  • Other (please describe):

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly

MCP Spec Compliance

  • This PR implements a feature defined in the MCP specification
  • Link to relevant spec section: RFC 8707 Resource Indicators
  • Implementation follows the specification exactly

Summary by CodeRabbit

  • Bug Fixes
    • Improved OAuth resource indicator handling and discovery, ensuring authorization and token flows consistently include the correct resource and fall back gracefully when metadata is missing.
  • Tests
    • Expanded OAuth test coverage for resource indicator behavior, discovery fallbacks, token/refresh edge cases, and various error and cancellation scenarios.

@MariaChrysafis MariaChrysafis changed the title resource feat(oauth): Add RFC 8707 resource indicator support Mar 7, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 7, 2026

Walkthrough

Adds RFC 8707 resource indicator handling to OAuth flows: new resourceURL field and getResourceURL() helper on OAuthHandler; propagates resource into authorization URL, token exchange, and refresh flows; populates resourceURL from protected-resource metadata during server discovery with fallbacks.

Changes

Cohort / File(s) Summary
OAuth Handler Implementation
client/transport/oauth.go
Adds resourceURL field to OAuthHandler and getResourceURL() method. Propagates RFC 8707 resource indicator into GetAuthorizationURL(), token exchange, and refreshToken(); captures protected-resource metadata during discovery and uses it as the resource indicator.
OAuth Handler Tests
client/transport/oauth_test.go
Adds comprehensive tests validating resource indicator propagation (auth URL, token exchange, refresh), preference for protected-resource canonical URL, omission cases, context cancellation/deadline scenarios, discovery fallbacks, and multiple OAuth edge-case responses.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically summarizes the main change: adding RFC 8707 resource indicator support to OAuth flows.
Description check ✅ Passed The PR description follows the template structure with all required sections completed: Description, Type of Change (MCP spec compatibility selected), Checklist items marked appropriately, and MCP Spec Compliance section fully filled with RFC 8707 reference and spec compliance confirmation.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@MariaChrysafis
Copy link
Copy Markdown
Contributor Author

@ezynda3 for review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
client/transport/oauth_test.go (1)

23-89: Please fold the new resource-indicator scenarios into a table.

These cases vary mainly by discovery inputs and expected resource, so a tests := []struct{...} table would remove most of the duplicated httptest scaffolding and make it easier to add the explicit AuthServerMetadataURL + canonical-resource regression case that this change needs.

As per coding guidelines, "Write table-driven tests using a tests := []struct{ name, ... } pattern".

Also applies to: 94-142, 146-183

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/transport/oauth_test.go` around lines 23 - 89, Refactor
TestOAuthHandler_ResourceIndicator_RFC8707 into a table-driven test: create a
tests := []struct{name string, discoveryURL string, expectedResource string,
initialMetadata string, ...} and loop over them; for each case spin up the
httptest server responses (the /.well-known document and /token handler)
configured from the table row, then construct OAuthConfig, call NewOAuthHandler,
use GetAuthorizationURL, ProcessAuthorizationResponse and RefreshToken and
assert the expectedResource for authorization URL, token exchange and refresh.
Ensure you include a case that passes an explicit AuthServerMetadataURL
(canonical-resource regression) and any permutations of discovery inputs
mentioned in the original test so common setup (httptest server, serverURL
variable, tokenExchangeResource/refreshResource capture) is reused inside the
table loop rather than duplicated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/transport/oauth.go`:
- Around line 317-325: getResourceURL currently only returns h.resourceURL if
populated, but h.resourceURL is only set via the protected-resource discovery
path so when AuthServerMetadataURL is provided and baseURL is configured the
handler never attempts discovery and may use baseURL as the audience; update
OAuthHandler initialization to perform a best-effort protected-resource lookup
even when AuthServerMetadataURL is set (populate h.resourceURL from the
protected-resource's advertised canonical identifier if available) and keep
getResourceURL unchanged so it prefers the discovered value over h.baseURL; add
a regression test exercising the case where AuthServerMetadataURL and baseURL
are both configured but the protected-resource metadata advertises a different
resource to ensure the client sends the discovered resource as the audience.

---

Nitpick comments:
In `@client/transport/oauth_test.go`:
- Around line 23-89: Refactor TestOAuthHandler_ResourceIndicator_RFC8707 into a
table-driven test: create a tests := []struct{name string, discoveryURL string,
expectedResource string, initialMetadata string, ...} and loop over them; for
each case spin up the httptest server responses (the /.well-known document and
/token handler) configured from the table row, then construct OAuthConfig, call
NewOAuthHandler, use GetAuthorizationURL, ProcessAuthorizationResponse and
RefreshToken and assert the expectedResource for authorization URL, token
exchange and refresh. Ensure you include a case that passes an explicit
AuthServerMetadataURL (canonical-resource regression) and any permutations of
discovery inputs mentioned in the original test so common setup (httptest
server, serverURL variable, tokenExchangeResource/refreshResource capture) is
reused inside the table loop rather than duplicated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4a067c00-c315-46da-9738-d9b16a1da0a7

📥 Commits

Reviewing files that changed from the base of the PR and between 37c4d97 and f7a293d.

📒 Files selected for processing (2)
  • client/transport/oauth.go
  • client/transport/oauth_test.go

Comment thread client/transport/oauth.go
Comment on lines +317 to +325
// getResourceURL returns the RFC 8707 resource indicator to send to the
// authorization server so it can audience-restrict issued tokens. It prefers
// the resource value advertised via RFC 9728 protected resource metadata and
// falls back to the MCP server base URL. Returns empty string if unknown.
func (h *OAuthHandler) getResourceURL() string {
if h.resourceURL != "" {
return h.resourceURL
}
return h.baseURL
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.

⚠️ Potential issue | 🟠 Major

Explicit auth-metadata config still misses the canonical resource.

getResourceURL() only prefers h.resourceURL, but that field is populated exclusively on the protected-resource discovery path. When AuthServerMetadataURL is set and baseURL is also configured, these new resource parameters fall back to baseURL and never see the protected resource’s advertised canonical identifier. If those differ, the client can send the wrong audience to the authorization server. Please make protected-resource lookup a best-effort step even when auth metadata is supplied directly, and add a regression test for that combination.

Also applies to: 431-434

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/transport/oauth.go` around lines 317 - 325, getResourceURL currently
only returns h.resourceURL if populated, but h.resourceURL is only set via the
protected-resource discovery path so when AuthServerMetadataURL is provided and
baseURL is configured the handler never attempts discovery and may use baseURL
as the audience; update OAuthHandler initialization to perform a best-effort
protected-resource lookup even when AuthServerMetadataURL is set (populate
h.resourceURL from the protected-resource's advertised canonical identifier if
available) and keep getResourceURL unchanged so it prefers the discovered value
over h.baseURL; add a regression test exercising the case where
AuthServerMetadataURL and baseURL are both configured but the protected-resource
metadata advertises a different resource to ensure the client sends the
discovered resource as the audience.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
client/transport/oauth.go (1)

147-147: ⚠️ Potential issue | 🟠 Major

Explicit auth-metadata config still misses the canonical resource.

This still only populates h.resourceURL on the protected-resource discovery path. When AuthServerMetadataURL is set, discovery is skipped, getResourceURL() falls back to baseURL, and the client can send the wrong audience if the protected resource advertises a different canonical identifier. Please keep the direct metadata fetch, but do a best-effort protected-resource lookup to seed h.resourceURL and add a regression test for that combination.

Also applies to: 317-324, 429-432

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/transport/oauth.go` at line 147, The handler currently only sets
h.resourceURL during the protected-resource discovery path, so when
AuthServerMetadataURL is provided discovery is skipped and getResourceURL()
falls back to baseURL causing wrong audience; keep the explicit metadata fetch
path when AuthServerMetadataURL is set (i.e., still call the direct metadata
retrieval routine that parses protected_resource or resource indicators), then
do a best-effort protected-resource lookup to seed h.resourceURL (prefer
protected-resource discovery result but fall back to metadata's resource or
baseURL), update getResourceURL() to prefer h.resourceURL if populated, and add
a regression test covering AuthServerMetadataURL + protected-resource
advertising a different canonical identifier to ensure h.resourceURL is set
correctly; refer to symbols resourceURL, h.resourceURL, AuthServerMetadataURL,
getResourceURL(), and the protected-resource discovery code paths when
implementing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@client/transport/oauth.go`:
- Line 147: The handler currently only sets h.resourceURL during the
protected-resource discovery path, so when AuthServerMetadataURL is provided
discovery is skipped and getResourceURL() falls back to baseURL causing wrong
audience; keep the explicit metadata fetch path when AuthServerMetadataURL is
set (i.e., still call the direct metadata retrieval routine that parses
protected_resource or resource indicators), then do a best-effort
protected-resource lookup to seed h.resourceURL (prefer protected-resource
discovery result but fall back to metadata's resource or baseURL), update
getResourceURL() to prefer h.resourceURL if populated, and add a regression test
covering AuthServerMetadataURL + protected-resource advertising a different
canonical identifier to ensure h.resourceURL is set correctly; refer to symbols
resourceURL, h.resourceURL, AuthServerMetadataURL, getResourceURL(), and the
protected-resource discovery code paths when implementing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf947bf-efdd-45de-a2a9-459bd6b96fff

📥 Commits

Reviewing files that changed from the base of the PR and between f7a293d and ed2d89e.

📒 Files selected for processing (1)
  • client/transport/oauth.go

@ezynda3
Copy link
Copy Markdown
Contributor

ezynda3 commented Apr 20, 2026

@MariaChrysafis sorry please have a look at and fix the merge conflicts

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