Commit graph

90 commits

Author SHA1 Message Date
Jeremiah Lowin
7f8a010798
Move TokenHandler to OAuthProvider for consistent error codes (#2538)
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.
2025-12-04 10:35:00 -05:00
Jeremiah Lowin
9cade6c8c8
Fix RFC 8414 path-aware authorization server metadata discovery (#2533)
* 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
2025-12-03 19:16:59 -05:00
Jeremiah Lowin
e3b103de40 Fix test to use handle() instead of deleted response() method 2025-12-03 16:46:01 -05:00
Jeremiah Lowin
8e57fe43a0 Update tests for client_secret=None behavior 2025-12-03 15:48:47 -05:00
Jeremiah Lowin
9c21754a45
Fix Azure provider OIDC scope handling (#2506)
* 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
2025-12-01 13:42:36 -05:00
Ayesha Shafique
e1d41f5e3b
Add Discord OAuth provider and corresponding tests (#2428)
* 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>
2025-12-01 12:58:29 -05:00
Jeremiah Lowin
ba69fba305
Add consent_csp_policy parameter for CSP customization (#2484)
* 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.
2025-11-26 16:53:40 -05:00
Akshay Parihar
a57a155c2e
Scalekit provider updates (#2413)
* 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
2025-11-22 12:22:13 -05:00
Kevin J Gao
05db547d86
feat: Made Changes to DescopeProvider to Support New Well Known URLs (#2392)
* added new DescopeProvider for Agentic Identity Hub

* fixed formatting with ruff

* made coderabbit change

* changed wording in descope.mdx

* added backwards compatibility

* ran ruff

* added scopes_supported to protected metadata resource

* empty commit to re-run tests
2025-11-22 12:20:50 -05:00
Nestor Qin
119bf445ea Resolve comments 2025-11-20 15:16:50 -08:00
Nestor Qin
463652a878 Add unit tests 2025-11-20 15:08:50 -08:00
Jeremiah Lowin
dd876b5833
Merge pull request #2439 from jlowin/oidc-proxy-extra-params
Add extra_authorize_params and extra_token_params to OIDCProxy
2025-11-17 12:23:11 -05:00
Jeremiah Lowin
e0f0f816b7 Add extra_authorize_params and extra_token_params to OIDCProxy 2025-11-17 11:57:58 -05:00
Jeremiah Lowin
ea31747572 Add refresh token support defaults to GoogleProvider
Set access_type=offline and prompt=consent by default to ensure
refresh tokens are returned by Google. Also expose extra_authorize_params
so users can customize authorization behavior.
2025-11-17 11:54:00 -05:00
Patrick Stöckle
18d4a6ab63
chore(typos): fix additional typos (#2396) 2025-11-08 10:47:56 -05:00
Jeremiah Lowin
f8b896490e
Fix Windows test timeout and restore parallel testing (#2383)
OAuth consent tests were timing out on Windows due to SQLite database
locking when OAuthProxy instances used the default DiskStore without
explicit MemoryStore configuration. Added explicit client_storage=MemoryStore()
to three tests in TestConsentPageServerIcon.

Also restored parallel testing on Windows (--numprocesses auto) which
was previously disabled but is now safe with proper test isolation.
2025-11-05 20:25:10 -05:00
Cemal Kılıç
f540385b58
feat: add algorithm configuration to Supabase auth provider (#2376) 2025-11-05 20:21:10 -05:00
Jeremiah Lowin
48fc8cbc9a
Fix consent form action for subpath mounting (#2382)
* Fix consent form action for subpath mounting

When a FastMCP server is mounted at a subpath (e.g., /api/v1), the consent
form used a hardcoded absolute path (/consent/submit) that didn't include
the mount prefix, causing 404 errors on submission.

Changed the form to use an empty action (action="") which submits to the
current URL, making it work correctly regardless of mount path. Also
consolidated the consent endpoints to handle both GET and POST at /consent.

Fixes #2380

* Update integration test to use /consent instead of /consent/submit
2025-11-05 20:20:38 -05:00
Jeremiah Lowin
5747cb6237
Fix OAuth metadata endpoint URLs when base_url differs from issuer_url (#2353)
* Fix OAuth metadata endpoint URLs when base_url differs from issuer_url

OAuth operational endpoints (/authorize, /token) are mounted at base_url,
but metadata was incorrectly declaring them at issuer_url. This caused
clients following the documented mounting pattern to receive incorrect
endpoint URLs in /.well-known/oauth-authorization-server.

Fixes #2287

* Update auth.py

* Remove unnecessary assertion from OAuthProvider init

* Add info log when issuer_url differs from base_url
2025-11-04 10:38:41 -05:00
Jeremiah Lowin
6cc9559f84
Bump ty to ==0.0.1a25 (#2350)
* Bump ty to >=0.0.1a25 with type fixes

Follow-up to #2295. Updates ty and fixes compatibility issues with alpha 25, including:

- Updated ignore comment syntax (possibly-unbound-attribute → possibly-missing-attribute)
- Fixed async generator type handling with anext()
- Fixed type narrowing for timeout parameters
- Converted base_url assignments to AnyHttpUrl after string manipulation
- Added CallToolResult to return type annotations
- Removed redundant type casts
- Fixed test form data to use strings instead of bytes

ty alpha 25 has limitations with isinstance() narrowing on unions (see pyproject.toml for details), requiring some targeted type ignores.

* Pin ty to ==0.0.1a25

Alpha releases can have breaking changes, so pin to the tested version.
2025-11-02 20:02:45 -05:00
Jeremiah Lowin
de58bb0e6c
Add DebugTokenVerifier with custom sync/async validation (#2296)
* Add DebugTokenVerifier with custom sync/async validation

* move import
2025-10-31 10:38:01 -04:00
Jeremiah Lowin
1ca53b4134
Add base_authority parameter to AzureProvider for Azure Government support (#2306) 2025-10-29 14:06:21 -04:00
mhassaninmsft
b24d771457
Supporting Multiple Issuers For JWTVerifier Oauth Workflow (#2233)
* multiple issuers

* Update tests/server/auth/test_jwt_provider.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Fix static checks

---------

Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-10-27 18:13:40 -04:00
Jeremiah Lowin
15dbe7ecf0
Add custom token verifier support to OIDCProxy (#2279)
* Add custom token verifier support to OIDCProxy

OIDCProxy now accepts an optional token_verifier parameter to support
non-JWT token formats like opaque tokens from providers such as Clerk.

When provided, the custom verifier is used instead of creating a default
JWTVerifier. Parameters that only apply to JWTVerifier creation (algorithm,
required_scopes) raise clear errors when specified alongside a custom
verifier. Parameters with other purposes (audience for OAuth flow,
timeout_seconds for config fetch) remain allowed.

The custom verifier's required_scopes are automatically loaded and
advertised through OAuth discovery endpoints.

* Document custom token verifier support in OIDC proxy
2025-10-27 13:38:20 -04:00
Jeremiah Lowin
8a48146aad
Cleanly render oauth errors from proxy (#2268) 2025-10-26 21:08:05 -04:00
Jeremiah Lowin
ba47db9b8c
Fix Azure scope validation (#2269)
* Update docs for required scopes

* add scopes

* Fix Azure scope validation

Azure returns unprefixed scopes in JWT tokens but requires prefixed scopes in authorization requests. The previous implementation incorrectly validated tokens against prefixed scopes, causing "invalid_token" errors.

Simplified AzureProvider to use standard JWTVerifier with unprefixed scopes for validation. Scopes are only prefixed when building the Azure authorization URL via _build_upstream_authorize_url() override.

Closes #2263
2025-10-26 11:41:22 -04:00
William Easton
c002bc389c
Small Clean-up (#2247)
* Project Clean-up

* Move pytest-asyncio to dev dependencies and update agents.md
2025-10-24 19:11:01 -04:00
William Easton
063ffe9f64
Derive jwt_signing_key from Client Secret, default to Encrypted Disk Store (#2223)
* Checkpoint progress

* Checkpoint progress

* add derive b64 method

* PR clean-up

* refactor da proxy

* Updates to tests

* Make jwt_signing_key required for oauth proxy

* use typing_extensions and fix tests

* PR Cleanup

* also adjust integration tests

* Update docs, use client secret to derive jwt signing key

* You win some you lose some, gg claude

* check for both in derive

* update documentation / clean up

* Update http.mdx

---------

Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2025-10-24 19:08:58 -04:00
Jeremiah Lowin
a1a5cca765
Fix Azure scope mismatch causing MCP client validation errors (#2243)
* Fix Azure scope mismatch causing MCP client validation errors

The AzureProvider was prefixing scopes during authorization but not in
token validation or Protected Resource Metadata, causing MCP clients to
reject tokens with "Server granted unauthorized scopes".

Changes:
- Prefix required_scopes once during __init__ and use consistently
- Pass prefixed scopes to JWTVerifier for token validation
- PRM now advertises prefixed scopes to MCP clients
- Remove unnecessary idempotent prefixing logic in authorize()
- Update comprehensive documentation explaining scope handling
- Update tests to reflect corrected behavior

Closes #2151

* Clarify that identifier_uri is optional in docstring
2025-10-24 17:45:49 -04:00
Jeremiah Lowin
562e51b378
Improve OAuth error messages with custom handlers and middleware (#2221)
* Enhance OAuth Proxy error responses with branded HTML pages

OAuth Proxy authentication errors now show styled HTML error pages in browsers
instead of raw JSON, with content negotiation for API clients. Enhanced error
messages explain common causes (ephemeral storage, server restarts) and provide
clear remediation steps.

Changes:
- Created enhanced authorization handler that extends SDK's AuthorizationHandler
- Created enhanced auth middleware that extends SDK's RequireAuthMiddleware
- HTML error pages use server branding (icon, name) from FastMCP instance
- Added comprehensive troubleshooting section to OAuth Proxy docs
- Added FAQ entry linking to detailed troubleshooting

* Add comprehensive tests for enhanced OAuth error responses

Tests cover:
- HTML error pages for browser requests with server branding
- Enhanced JSON responses with registration endpoint hints
- Content negotiation between HTML and JSON
- Enhanced middleware error messages for invalid_token
- WWW-Authenticate header format consistency with SDK

* Update language for new storage defaults

* update docs

* Update tests for simplified error messages

* Clean up messages
2025-10-22 21:29:14 -04:00
Jeremiah Lowin
686082a5b5
Add platform-aware OAuth token persistence (#2218)
* Add comprehensive keyring integration tests

Prevents OS keyring pollution during testing by adding a global mock in
conftest.py. Tests verify keyring behavior across platforms and fallback
scenarios without writing to the actual system keyring.

- Add global mock_keyring fixture to tests/conftest.py
- Add TestOAuthProxyKeyring class with 6 keyring-specific tests
- Remove try/except ImportError for keyring (now required dependency)
- Add keyring extra to py-key-value-aio dependency
- Clean up extraneous implementation comments in oauth_proxy.py

* Update OAuth keyring documentation

Update all OAuth-related documentation to reflect keyring-based key management:
- Add version badges to jwt_signing_key, token_encryption_key, and client_storage parameters
- Standardize "Default behavior (`None`):" formatting with backticks
- Ensure consistent messaging about development-only defaults across all docs
- Update oauth-proxy.mdx, oidc-proxy.mdx, http.mdx, storage-backends.mdx, and upgrade-guide.mdx
2025-10-22 20:42:24 -04:00
Jeremiah Lowin
3321644ad3
Replace subprocess tests with in-process async servers (#2006)
* Use anyio as testing backend

* Remove asyncio markers

* Update streamable http tests

* Replace all subprocess tests

* Replace anyio task groups with asyncio context managers in tests

- Convert run_server_async from anyio task group pattern to asyncio.create_task with async context manager
- Remove task_group fixture from conftest
- Update all test fixtures to use async with run_server_async pattern
- Remove TaskGroup imports from all test files
- Tests now work with pytest-asyncio instead of pytest-anyio

* Update test_github_provider_integration.py
2025-10-19 10:47:54 -04:00
Jeremiah Lowin
9e78d755c2
Support custom server name, icons, and link in OAuth Proxy consent page (#2135)
* Implement icon support in fastmcp

* Fix icon feature tests

- Update snapshot for ResourceTemplate to include icons field
- Remove OAuth mounting tests (belong to PR #2119, not this feature)

* Update docs

* Customize consent screen

* Use server website link if available

* Anchor link shouldnt have trailing slash

* Remove 'a FastMCP server named' from consent page message

* Update docs
2025-10-18 11:49:26 -04:00
Jeremiah Lowin
330eaed11f
OAuth proxy issues its own tokens (#2109)
* OAuth proxy issues its own tokens

Implement token factory pattern where proxy issues FastMCP JWTs
instead of forwarding upstream tokens. Tokens are minimal references
(JTI) that map to encrypted upstream credentials stored server-side.

* Update run-tests.yml

* Update secret generation and docs

* Add upgrade guide
2025-10-17 14:31:53 -04:00
Jeremiah Lowin
d472e30765
Support mounting OAuth-protected servers under path prefixes (#2119)
* Add issuer_url parameter to OAuth providers for mounting scenarios

* Add get_well_known_routes

* Update docs

* Improve docs and tests

* Trigger CI

* Fix conditional test execution for Windows
2025-10-17 11:44:49 -04:00
Jeremiah Lowin
4a9f02c87c
Upgrade to MCP 1.17+ with RFC 9728 compliance (#2122)
* Upgrade to MCP 1.17+ with RFC 9728 compliance

Updates FastMCP to require MCP 1.17+ and implements RFC 9728-compliant
OAuth protected resource metadata URL handling.

The key change is that .well-known/oauth-protected-resource endpoints
are now registered at path-aware locations. For example, if an MCP
server is mounted at /mcp, the metadata endpoint is now at
/.well-known/oauth-protected-resource/mcp instead of
/.well-known/oauth-protected-resource.

This ensures proper OAuth discovery for path-based resource servers
and aligns with the MCP SDK's implementation of RFC 9728 §3.1.

Changes include:
- Update minimum MCP version from 1.12.4 to 1.17.0
- Use build_resource_metadata_url() for RFC 9728 compliance
- Configure CI to test with latest package versions (--upgrade)
- Update tests for path-aware metadata URLs
- Add icons field to Tool model (introduced in MCP 1.17)

* Fix RemoteAuthProvider integration tests for RFC 9728

* Fix parameterized test for nested base URL paths
2025-10-17 09:29:23 -04:00
Jeremiah Lowin
52e9af1214
Add RFC 7662 token introspection provider (#2074) 2025-10-16 10:57:06 -04:00
Jeremiah Lowin
7b7e277d5d
Prevent confused deputy attacks in OAuth proxy (#2056) 2025-10-11 10:53:54 -04:00
William Easton
93c41e815c
Merge branch 'main' into switch-kvstore 2025-10-09 09:20:55 -04:00
William Easton
de0c39c1b3
Add raise on validation error and set uniform collection names 2025-10-08 23:40:58 -04:00
Adam Azzam
a8d2a667d6
Add supabase auth (#1997) 2025-10-08 20:27:55 -04:00
William Easton
00ffa6006a
Merge branch 'main' into switch-kvstore 2025-10-08 18:07:05 -04:00
Anders Julton
82cf3888dd
Azure (Entra) OAuth: Validate token against FastMCP app, not Graph. (#1891)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2025-10-08 09:06:35 -04:00
William Easton
1a9f95299a
lint cuz i dont have precommit 2025-10-07 17:40:52 -04:00
William Easton
dd81a0beb3
Removing central storage configuration for now 2025-10-06 11:58:11 -05:00
William Easton
a9ee0b577a
Merge branch 'main' into switch-kvstore 2025-09-29 17:12:09 -05:00
Jeremiah Lowin
e8673b4d8b
Fix Python 3.13 websockets deprecation warning (#1949) 2025-09-29 15:24:20 -04:00
Jeremiah Lowin
d7c60511b9
Refactor OAuth 2.1 error handling with TokenHandler subclass (#1948) 2025-09-29 15:00:57 -04:00
Tomas Caraccia
1c323a8583
feat: Follow OAuth 2.1 spec requirements on auth failures (#1923)
Co-authored-by: Tomas <>
2025-09-29 14:10:59 -04:00
William Easton
55ac070a59
Merge branch 'main' into switch-kvstore 2025-09-28 21:35:46 -05:00