Conversation
OIDC scopes (openid, profile, email, offline_access) were being incorrectly prefixed with identifier_uri, causing Azure to reject authorization requests. This fix: - Detects OIDC scopes and sends them unprefixed to Azure - Filters OIDC scopes from token validation (Azure doesn't include them in access token scp claims) - Still advertises OIDC scopes to clients via valid_scopes - Also handles dot-notation scopes (e.g., User.Read) correctly Fixes #2451, #2420
WalkthroughA module-level constant Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/fastmcp/server/auth/providers/azure.py (1)
368-382: Dot-notation detection may affect custom scopes with dots.The check for
"." in scopeassumes dot-notation indicates Microsoft Graph scopes. However, a custom API scope containing a dot (e.g.,my.custom.scope) would be treated as a Graph scope and not prefixed.While uncommon, consider documenting this behavior or using a more specific check if Graph scope detection needs to be more precise (e.g., checking against a known Graph scope pattern).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/server/auth/providers/test_azure.pyis excluded by none and included by none
📒 Files selected for processing (2)
docs/integrations/azure.mdx(2 hunks)src/fastmcp/server/auth/providers/azure.py(5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write in second person ('you') for instructions and procedures in MDX documentation
Use active voice over passive voice in MDX technical documentation
Employ present tense for current states and future tense for outcomes in MDX documentation
Maintain consistent terminology throughout all MDX documentation
Keep sentences concise while providing necessary context in MDX documentation
Use parallel structure in lists, headings, and procedures in MDX documentation
Lead with the most important information using inverted pyramid structure in MDX documentation
Use progressive disclosure in MDX documentation: present basic concepts before advanced ones
Break complex procedures into numbered steps in MDX documentation
Include prerequisites and context before instructions in MDX documentation
Provide expected outcomes for each major step in MDX documentation
End sections with next steps or related information in MDX documentation
Use descriptive, keyword-rich headings for navigation and SEO in MDX documentation
Focus on user goals and outcomes rather than system features in MDX documentation
Anticipate common questions and address them proactively in MDX documentation
Include troubleshooting for likely failure points in MDX documentation
Provide multiple pathways (beginner vs advanced) but offer an opinionated path to avoid overwhelming users in MDX documentation
Always include complete, runnable code examples that users can copy and execute in MDX documentation
Show proper error handling and edge case management in MDX code examples
Use realistic data instead of placeholder values in MDX code examples
Include expected outputs and results for verification in MDX code examples
Test all code examples thoroughly before publishing in MDX documentation
Specify language and include filename when relevant in MDX code examples
Add explanatory comments for complex logic in MDX code examples
Document all API...
Files:
docs/integrations/azure.mdx
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Python source code must use Python ≥3.10 with full type annotations
Never use bare except - be specific with exception types
Follow Ruff linting and Prettier formatting standards - run uv run prek run --all-files before committing
Prioritize readable, understandable code - clarity over cleverness, avoid obfuscated or confusing patterns
Files:
src/fastmcp/server/auth/providers/azure.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: jlowin
Repo: jlowin/fastmcp PR: 0
File: :0-0
Timestamp: 2025-12-01T15:48:05.083Z
Learning: PR #2505 in fastmcp adds NEW functionality to get_access_token(): it now first checks request.scope["user"] for the token (which never existed before), then falls back to _sdk_get_access_token() (the only thing the original code did). This is not a reversal of order but entirely new functionality to fix stale token issues.
📚 Learning: 2025-12-01T15:48:05.083Z
Learnt from: jlowin
Repo: jlowin/fastmcp PR: 0
File: :0-0
Timestamp: 2025-12-01T15:48:05.083Z
Learning: PR #2505 in fastmcp adds NEW functionality to get_access_token(): it now first checks request.scope["user"] for the token (which never existed before), then falls back to _sdk_get_access_token() (the only thing the original code did). This is not a reversal of order but entirely new functionality to fix stale token issues.
Applied to files:
docs/integrations/azure.mdxsrc/fastmcp/server/auth/providers/azure.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (6)
src/fastmcp/server/auth/providers/azure.py (4)
28-31: LGTM!Good choice using
frozensetfor the OIDC scope constant—immutable and provides O(1) membership testing. The referenced Microsoft documentation accurately describes these as the standard OIDC scopes that should never be prefixed.
261-267: LGTM!Correctly passes only non-OIDC scopes to the JWT verifier. This aligns with Azure v2.0 token format where the
scpclaim contains unprefixed custom API scopes only.
297-299: LGTM!Clean separation between advertised scopes (full set for MCP client discovery) and validated scopes (non-OIDC only). The comment accurately documents this intentional distinction.
248-260: No action needed—scope validation behavior is intentional and correct.When
required_scopescontains only OIDC scopes,validation_scopescorrectly becomesNone. This is the intended design: Azure v2.0 access tokens don't include OIDC scopes in thescpclaim, so validating them would fail. The code and inline comments (lines 248–251, 257, 266) adequately document this behavior.docs/integrations/azure.mdx (2)
167-183: LGTM!Clear and accurate documentation of the scope handling behavior. The table provides an excellent quick reference, and the Info block with the concrete example helps users understand the actual flow. The documentation correctly reflects the implementation.
325-326: LGTM!Accurate explanation of OIDC scope handling within the
required_scopesparameter documentation. This proactively addresses a common question users might have about including standard OIDC scopes.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
docs/integrations/azure.mdx (1)
167-187: Scope Handling section is clear and accurate.The new section effectively explains the three scope categories and their behaviors. The table format is well-structured, the
<Info>block clearly articulates the common use case (OIDC + custom scopes inrequired_scopes), and the<Tip>about Graph scopes provides helpful guidance on a likely follow-up question.Minor observation: The table cell for fully-qualified scopes uses "Already has URI"—consider slightly more explicit phrasing like "Sent as-is (already fully qualified)" for added clarity, though the current wording is adequate.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/server/auth/providers/test_azure.pyis excluded by none and included by none
📒 Files selected for processing (2)
docs/integrations/azure.mdx(2 hunks)src/fastmcp/server/auth/providers/azure.py(5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Python source code must use Python ≥3.10 with full type annotations
Never use bare except - be specific with exception types
Follow Ruff linting and Prettier formatting standards - run uv run prek run --all-files before committing
Prioritize readable, understandable code - clarity over cleverness, avoid obfuscated or confusing patterns
Files:
src/fastmcp/server/auth/providers/azure.py
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write in second person ('you') for instructions and procedures in MDX documentation
Use active voice over passive voice in MDX technical documentation
Employ present tense for current states and future tense for outcomes in MDX documentation
Maintain consistent terminology throughout all MDX documentation
Keep sentences concise while providing necessary context in MDX documentation
Use parallel structure in lists, headings, and procedures in MDX documentation
Lead with the most important information using inverted pyramid structure in MDX documentation
Use progressive disclosure in MDX documentation: present basic concepts before advanced ones
Break complex procedures into numbered steps in MDX documentation
Include prerequisites and context before instructions in MDX documentation
Provide expected outcomes for each major step in MDX documentation
End sections with next steps or related information in MDX documentation
Use descriptive, keyword-rich headings for navigation and SEO in MDX documentation
Focus on user goals and outcomes rather than system features in MDX documentation
Anticipate common questions and address them proactively in MDX documentation
Include troubleshooting for likely failure points in MDX documentation
Provide multiple pathways (beginner vs advanced) but offer an opinionated path to avoid overwhelming users in MDX documentation
Always include complete, runnable code examples that users can copy and execute in MDX documentation
Show proper error handling and edge case management in MDX code examples
Use realistic data instead of placeholder values in MDX code examples
Include expected outputs and results for verification in MDX code examples
Test all code examples thoroughly before publishing in MDX documentation
Specify language and include filename when relevant in MDX code examples
Add explanatory comments for complex logic in MDX code examples
Document all API...
Files:
docs/integrations/azure.mdx
🧠 Learnings (2)
📓 Common learnings
Learnt from: jlowin
Repo: jlowin/fastmcp PR: 0
File: :0-0
Timestamp: 2025-12-01T15:48:05.083Z
Learning: PR #2505 in fastmcp adds NEW functionality to get_access_token(): it now first checks request.scope["user"] for the token (which never existed before), then falls back to _sdk_get_access_token() (the only thing the original code did). This is not a reversal of order but entirely new functionality to fix stale token issues.
📚 Learning: 2025-12-01T15:48:05.083Z
Learnt from: jlowin
Repo: jlowin/fastmcp PR: 0
File: :0-0
Timestamp: 2025-12-01T15:48:05.083Z
Learning: PR #2505 in fastmcp adds NEW functionality to get_access_token(): it now first checks request.scope["user"] for the token (which never existed before), then falls back to _sdk_get_access_token() (the only thing the original code did). This is not a reversal of order but entirely new functionality to fix stale token issues.
Applied to files:
src/fastmcp/server/auth/providers/azure.pydocs/integrations/azure.mdx
🧬 Code graph analysis (1)
src/fastmcp/server/auth/providers/azure.py (1)
src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run tests with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (8)
src/fastmcp/server/auth/providers/azure.py (6)
28-32: OIDC scopes constant is well-defined.Good use of
frozensetfor immutability and O(1) membership testing. The included scopes (openid,profile,offline_access) match Microsoft's documented standard OIDC scopes.
248-260: Validation scope filtering logic is correct.The implementation properly:
- Excludes OIDC scopes from token validation (since Azure doesn't include them in
scpclaims).- Falls back to
Noneif only OIDC scopes are configured (disabling scope validation).- Is protected by the earlier validation (lines 227-234) ensuring at least one scope exists.
This aligns with the PR objective to fix Azure's rejection of OIDC scope prefixing.
261-267: JWTVerifier receives correctly filtered scopes.Passing
validation_scopes(which excludes OIDC scopes) to the verifier ensures token validation won't fail due to missing OIDC scopes in Azure'sscpclaim. This directly addresses issue #2451.
297-299: Scope advertisement correctly includes all scopes.Advertising the full
required_scopes(including OIDC scopes) to MCP clients viavalid_scopeswhile only validating non-OIDC scopes is the intended behavior per the PR objectives. This ensures clients can request OIDC scopes without causing validation failures.
372-381: Dot-notation Graph scopes inrequired_scopeswould be incorrectly prefixed.The PR description states: "Dot-notation Graph scopes (User.Read, Mail.Send): sent as-is (dot-notation recognized)." However, the implementation only checks for
"://"or"/"to identify already-qualified scopes, not for dot-notation patterns.If a user includes
"User.Read"inrequired_scopesinstead ofadditional_authorize_scopes, it would be prefixed toapi://xxx/User.Read, which is incorrect.Consider adding a check for dot-notation scopes:
for scope in scopes: if scope in OIDC_SCOPES: # Standard OIDC scopes - never prefix prefixed.append(scope) - elif "://" in scope or "/" in scope: + elif "://" in scope or "/" in scope or "." in scope: # Already fully-qualified (e.g., "api://xxx/read" or - # "https://graph.microsoft.com/User.Read") + # "https://graph.microsoft.com/User.Read") or + # dot-notation Graph scopes (e.g., "User.Read") prefixed.append(scope) else: # Unprefixed custom API scope - prefix with identifier_uri prefixed.append(f"{self.identifier_uri}/{scope}")Alternatively, if the current behavior is intentional (requiring Graph scopes via
additional_authorize_scopesonly), update the PR description to clarify.
409-446: Token refresh scope handling reuses the centralized prefixing logic.The
_prepare_scopes_for_upstream_refreshmethod correctly delegates to_prefix_scopes_for_azure, ensuring consistent scope handling across authorization and refresh flows. The deduplication usingdict.fromkeys()is an idiomatic O(n) approach.docs/integrations/azure.mdx (2)
328-329: ParamField update accurately documents OIDC scope behavior.The addition clearly states that OIDC scopes can be included in
required_scopes, are sent unprefixed to Azure, and are excluded from validation—directly addressing the PR objective. The note about Azure requiring at least one scope is consistent with earlier sections.
1-383: No revisions needed—the Azure API claims are accurate.The documentation correctly states that:
- OIDC scopes (
openid,profile,offline_access) do not appear in the access token'sscpclaim (they control ID token/UserInfo content instead)- The
scopeparameter is required in Azure Entra ID v2.0 authorization requests- OIDC scopes can be mixed with API scopes in a single authorization request
- FastMCP's automatic scope prefixing and handling aligns with Microsoft Entra ID's actual behavior
All technical claims match current Microsoft Entra ID documentation.
The Azure provider was incorrectly prefixing OIDC scopes (
openid,profile,email,offline_access) withidentifier_uri, causing Azure to reject authorization requests with errors likeAADSTS65005: The application asked for scope 'openid' that doesn't exist.Per Microsoft documentation, OIDC scopes are always sent as simple string identifiers without resource prefixes.
This fix allows users to include OIDC scopes in
required_scopesjust like other providers (Google, Auth0):FastMCP automatically:
scpclaims)read,write,my.scopeidentifier_uriopenid,profile,email,offline_accessapi://xxx/read,https://graph.microsoft.com/User.ReadNote: Microsoft Graph scopes like
User.Readshould useadditional_authorize_scopesor the fully-qualified formathttps://graph.microsoft.com/User.Read.Fixes #2451, Fixes #2420