* Add consent binding cookie to prevent confused deputy attacks (GHSA-rww4-4w9c-7733)
The OAuthProxy's consent page verified user intent but didn't bind the
consenting browser to the IdP callback. An attacker could intercept the
upstream authorization URL after consent and send it to a victim, whose
browser would complete the flow without having the consent cookie.
This adds a signed consent binding cookie set during consent approval
(both manual and auto-approve paths) and verified in the IdP callback
handler. A different browser won't have this cookie and gets a 403.
* Use startswith for URL assertion in consent binding test
* Store consent bindings as per-transaction map to support parallel flows
* Only accept __Host- consent binding cookie on HTTPS
* chore: Update SDK documentation
---------
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
* fix: enforce redirect URI validation when patterns are explicitly configured
Security fix: When allowed_redirect_uri_patterns is explicitly set, reject redirect URIs that don't match the patterns instead of falling back to parent validation. This prevents unauthorized OAuth clients from bypassing the allowlist and accessing protected resources.
* Update models.py
no need to return twice
* fix redirect uri access issue
* update style
* feat: add unit test to enforce fallback not applied when redirect uri's supplied
* fix: improve test case
* apply linter
* refactor: simplify logic and do not exposed allowed redirect patterns
---------
Co-authored-by: Nathan <2381793w@student.gla.ac.uk>
Updated all OAuthProxy test instantiations to use MemoryStore instead of defaulting to DiskStore, avoiding SQLite timeout issues on Windows and improving test performance.
Co-authored-by: Bill Easton <strawgate@users.noreply.github.com>
Server authors opt-in by setting list_page_size on FastMCP.
Client convenience methods auto-fetch all pages transparently.
Use _mcp methods with cursor parameter for manual pagination.
- get_*() now does aggregation + component auth (raises AuthorizationError)
- Deleted _get_*() overrides - inherited from Provider applies transforms
- Simplified AuthMiddleware to global auth only
- Changed version params to VersionSpec | None (not str | None)
- Updated tests to use _get_*() where visibility filtering is expected
- Add None checks in auth and tool transform tests
- Add assertions in component_service.py for None returns
- Add type ignore comments for max() with version_sort_key
FastMCP now properly inherits from Provider, eliminating ~200 lines of
duplicated _source_* methods. Key changes:
- get_tool/resource/prompt return None instead of raising NotFoundError
- Visibility filter separated from transforms (applied last)
- Nested server middleware runs on both list and execution operations
- Resource auth failure doesn't fall back to templates
- AggregateProvider kept as user-facing utility class
* Replace type: ignore[attr-defined] with isinstance assertions in tests
* Fix isinstance assertions in failing tests
- Fix enum test to check for ResponseEnum instead of str
- Fix binary resource test to check for BlobResourceContents instead of TextResourceContents
- Fix Root type tests to check attributes directly instead of isinstance checks
* Fix type errors without using type: ignore
- Remove execution methods from TransformingProvider (only handles transformations)
- Add execution methods to base Provider class with default implementations
- Fix type narrowing in tests using cast() instead of type: ignore
- Fix PromptResult type handling in prompt render tests
- Fix type narrowing in middleware test for arguments and structured_content
When upstream OAuth providers don't return expires_in (like GitHub OAuth
Apps), use smart defaults: 1 hour if refresh token available, 1 year if
not. Adds fallback_access_token_expiry_seconds parameter to override.
* Fix type errors for ty 0.0.1-alpha.31 upgrade
Add type ignores and fixes for ty's stricter checking:
- Path(None) guards in cli.py
- isinstance checks for ElicitRequestFormParams (URL elicitation support)
- TODO(ty) comments for match/isinstance narrowing bugs
- Method override type ignores for generic covariance
- Starlette Middleware typing workarounds
- Dynamic type construction ignores in json_schema_type.py
* Fix remaining type errors for ty 0.0.1-alpha.31
- Add asserts for optional attribute access in tests
- Add type ignores for dynamic httpx transport internals
- Add TODO(ty) comments for `in` operator on str|bytes
- Add TODO(ty) comments for Starlette Middleware typing
- Use cast for prompt.fn async validation in server.py
* Upgrade ty to 0.0.1-alpha.31
Fixes additional test file type errors discovered after upgrade.
All OAuth providers now return correct invalid_client error codes
instead of unauthorized_client for auth failures. Previously only
OAuthProxy had this fix; now OAuthProvider (and InMemoryOAuthProvider)
also benefit.
* Fix RFC 8414 path-aware authorization server metadata discovery
Override get_well_known_routes() in OAuthProvider to rewrite the
authorization server metadata route to be path-aware based on issuer_url,
matching how protected resource metadata already works.
Closes#2527
* Update readme
* Fix Azure provider to handle OIDC scopes correctly
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
* Fix dot-notation scopes to be prefixed (custom scopes can have dots)
* Improve Azure scope handling docs with clear examples
* Add Discord OAuth provider and corresponding tests
* Update DiscordProvider client_secret and required_scopes documentation
* Add Discord to authentication support list in README
* Fix Discord token verifier to match actual API response format
Discord's /api/oauth2/@me endpoint returns:
- "scopes" as a list, not "scope" as a space-separated string
- "expires" as ISO timestamp, not "expires_in" as seconds
- "user" data directly in the response (no need for extra API call)
* Simplify Discord token verifier
---------
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
* Add consent_csp_policy parameter to OAuthProxy
Allows customization or disabling of CSP directives on the consent page.
Fixes#2476.
* Add consent_csp_policy to OIDCProxy and update docs
* Fix HTML injection vulnerability in CSP policy
HTML-escape the CSP policy value before inserting into meta tag to prevent HTML injection when CSP policies contain quotes.
* sk-provider updates - aud not enforce, scopes enforce if present
* updating env_prefix, adding debug logs
* updating docs
* ruff formatting
* not changing prefix for backward compatiblity
* backward compatibility changes
* give more preference to base_url than mcp_url if both passed
* updating docs
* refactor
* updating example server
* updating readme of example
* updating docs
* updating tests to reflect what should ideally go in the parameter