* fix(auth): redact secrets in Debug output for StoredCredentials and StoredAuthorizationState
Removes `Debug` from the derive macros on `StoredCredentials` and
`StoredAuthorizationState` and replaces them with manual `Debug` impls
that print `[REDACTED]` for sensitive fields (access/refresh tokens,
PKCE verifiers, and CSRF tokens), preventing accidental credential
leakage via `{:?}` formatters, log calls, and error chains.
Fixes#741
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(auth): assert Debug output redacts secrets for credential types
Adds regression tests for the fix in the previous commit, verifying
that `{:?}` formatting of `StoredAuthorizationState` and
`StoredCredentials` does not emit plaintext secrets.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(auth): address review feedback on debug redaction tests
- Remove redundant VendorExtraTokenFields from use super:: in
test_stored_credentials_debug_redacts_token_response (already
imported at module scope)
- Add assert!(debug_output.contains("created_at")) to
test_stored_authorization_state_debug_redacts_secrets to verify
non-secret fields remain visible in Debug output
- Run cargo fmt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update crates/rmcp/src/transport/auth.rs
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
* fix: remaining formatting issue
* fix: formatting
* fix: formatting
* fix: please
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
* fix(auth): pass WWW-Authenticate scopes to DCR registration request
When an MCP server returns a 401 with `WWW-Authenticate: Bearer scope="..."`,
the scopes are parsed but never included in the Dynamic Client Registration
(DCR) request. Per RFC 7591, the DCR request should include a `scope` field
so the authorization server knows what scopes the client intends to use.
Servers that enforce scope-matching between registration and authorization
will reject the flow without this.
Changes:
- Add optional `scope` field to `ClientRegistrationRequest` with
`skip_serializing_if` for backward compatibility
- Update `register_client()` to accept scopes parameter and include
them in the DCR request body and returned `OAuthClientConfig`
- Thread scopes from `AuthorizationSession::new()` into both
`register_client()` call sites
- Re-export `oauth2::TokenResponse` trait so consumers can extract
scopes from token responses
- Add serialization tests for the new `scope` field
* refactor(auth): change register_client to accept &[&str] instead of &[String]
Avoids unnecessary Vec<String> allocation in callers that already have &[&str].
* fix(auth): make ClientRegistrationRequest crate-private
* refactor(auth): stop re-exporting oauth2 TokenResponse trait
* style(auth): merge TokenResponse into grouped oauth2 import
Fix nightly rustfmt check by consolidating the separate
`use oauth2::TokenResponse` into the existing `use oauth2::{...}` block.
* feat(auth): support returning extra fields that may be returned from token generation
exchange_code_for_token and refresh_token now return a StandardTokenResponse which includes
any additionalfields which might have been sent by the vendor
BREAKING CHANGE: Return type of exchange_code_for_token and refresh_token has changed
and may require code changes.
* fix: doc links
* feat: add support for custom HTTP headers in StreamableHttpClient
* feat: implement reserved header checks for custom HTTP headers in StreamableHttpClient
* feat(auth): add token_endpoint_auth_method to OAuthClientConfig
Some OAuth providers (e.g. HubSpot) require client credentials to be
sent as POST body parameters (client_secret_post) instead of via HTTP
Basic Auth header. The oauth2 crate defaults to BasicAuth, and rmcp
had no way to override this, causing TokenExchangeFailed errors.
Add an optional `token_endpoint_auth_method` field to OAuthClientConfig
that accepts "client_secret_post" (RequestBody) and "client_secret_basic"
(BasicAuth). Unknown values are silently ignored, preserving the default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(auth): derive token_endpoint_auth_method from server metadata
Move auth method selection from per-client config to server's
AuthorizationMetadata, which is the correct OAuth 2.0 approach.
Servers like HubSpot advertise token_endpoint_auth_methods_supported
in their metadata; reading it from there avoids manual configuration
and prevents TokenExchangeFailed errors with non-BasicAuth providers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(auth): read token_endpoint_auth_methods_supported from additional_fields
Move token_endpoint_auth_methods_supported out of AuthorizationMetadata
as an explicit field and read it from the serde(flatten) additional_fields
HashMap instead. This avoids serializing `null` when the field is absent,
which broke Zod validation in downstream consumers like MCP Inspector.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(auth): prefer basic auth when both methods supported and improve test assertions
When token_endpoint_auth_methods_supported contains both client_secret_post
and client_secret_basic, default to basic auth per RFC 6749 §2.3.1.
Update configure_client tests to assert actual AuthType instead of is_some().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style(auth): apply cargo fmt formatting
* style(auth): apply nightly cargo fmt import grouping
* revert: undo .gitignore change
---------
Co-authored-by: Anar Azadaliyev <anar.azadaliye@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add OpenID Connect discovery support per spec-2025-11-25 4.3
Previously only tried OAuth 2.0 endpoints. Now tries OAuth first, then
OpenID Connect Discovery 1.0 in the spec-mandated priority order.
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* fix: format auth.rs test assertions
Reformat assert_eq! statements to satisfy rustfmt checks in CI.
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
---------
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* feat(auth): add cimd support for SEP-991
add cimd support for url-based client ids
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* test(auth): add unit tests for is_https_url helper
Add test coverage for is_https_url helper to validate HTTPS scheme, non-root paths,
and reject http, javascript, data schemes, and invalid inputs per SEP-991 requirements.
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* feat(example): add CIMD OAuth server for SEP-991 testing
Implements a new server example (servers_cimd_auth_streamhttp) that
demonstrates CIMD (Client ID Metadata Document) support for URL-based
client IDs. The server validates client_id URLs, fetches and validates
client metadata documents, and provides OAuth 2.0 authorization endpoints
with MCP integration for end-to-end testing.
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* fix(oauth): add CORS headers to token endpoint
Add CORS headers to token endpoint to allow cross-origin requests from browsers
during OAuth authorization code exchange flow.
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* refactor: improve is_https_url function and consolidate tests
- Improve is_https_url function formatting and readability
- Merge all test cases into single test_is_https_url_scenarios function
- Add missing test case for "https://" URL
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* refactor: use map_err instead of match for error handling in auth.rs
Replace the verbose match statement with
map_err for more idiomatic
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
* feat: add client-metadata.json
Add client metadata file for SEP-991 CIMD
authentication support
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
---------
Signed-off-by: tanish111 <tanishdesai37@gmail.com>
SSE transport has been removed from the MCP specification in favor of
streamable HTTP. This removes all SSE-specific transport code:
- Remove `transport-sse-client` and `transport-sse-server` features
- Remove `SseClientTransport` and `SseServer` types
- Remove SSE-specific examples (`counter_sse`, `counter_sse_directly`)
- Migrate auth examples from SSE to streamable HTTP
- Update tests to remove SSE transport usage
- Update documentation
BREAKING CHANGE: The following have been removed:
- `transport-sse-client` feature
- `transport-sse-client-reqwest` feature
- `transport-sse-server` feature
- `SseClientTransport` type
- `SseServer` type
- `sse_client` and `sse_server` modules
Users should migrate to streamable HTTP transport which provides
equivalent functionality. See `StreamableHttpClientTransport` and
`StreamableHttpService` for the replacement APIs.
Ref: https://github.com/modelcontextprotocol/rust-sdk/pull/561#issuecomment-3576551699
auth.rs was using an in-memory expires-at which is only set on initial token exchange.
Instead, this PR switches it to use the expires-at set in the credentials that are passed in.
Many MCP Servers use client_name for a variety of things including:
* Whitelisting
* Logos
* Copy shown directly on the page
* etc
As a result, it's important for MCP Clients to be able to override the client name.
* refactor: a transport trait and streamable http client
* test: test with js streamable http server
* refactor: separate sse stream connection
* fix(test): streamable http client test with js
* fix(test): wait longer time for js server startup