Describe the bug
StoredCredentials and StoredAuthorizationState in crates/rmcp/src/transport/auth.rs both derive #[derive(Debug)]. This causes secret fields — OAuth access tokens, refresh tokens, PKCE verifiers, and CSRF tokens — to be printed in plaintext whenever these types are formatted via a Debug formatter. There is no compiler warning or type-system protection against this.
To Reproduce
- Add the
auth feature to your rmcp dependency. - Run an OAuth flow to populate a
StoredAuthorizationState or StoredCredentials. - Format either type using
{:?} in any log call:
tracing::error!("auth state: {:?}", stored_state);- Observe that live PKCE verifiers, CSRF tokens, access tokens, and refresh tokens are emitted in plaintext to log output.
Expected behavior
Secret fields should be redacted in Debug output:
StoredAuthorizationState { pkce_verifier: [REDACTED], csrf_token: [REDACTED], created_at: 1234567890 }
Logs
Current {:?} output for StoredAuthorizationState:
StoredAuthorizationState { pkce_verifier: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", csrf_token: "xK9mP2nQ8rL4vT6wY1uZ3aB5cD7eF0gH",
created_at: 1741564800
}
Additional context
Affected types:
StoredCredentials (auth.rs line 62) — token_response contains access_token and refresh_token:
#[derive(Debug,Clone,Serialize,Deserialize)]pubstructStoredCredentials{pubclient_id:String,pubtoken_response:Option<OAuthTokenResponse>,pubgranted_scopes:Vec<String>,pubtoken_received_at:Option<u64>,}StoredAuthorizationState (auth.rs line 122):
#[derive(Debug,Clone,Serialize,Deserialize)]pubstructStoredAuthorizationState{pubpkce_verifier:String,pubcsrf_token:String,pubcreated_at:u64,}The MCP spec's own auth guidance explicitly states: "Don't log credentials. Never log Authorization headers, tokens, codes, or secrets." The current type definitions make it easy to violate this by accident — including via anyhow/thiserror error chains that automatically format Debug representations of involved values.
Suggested fix: use the [secrecy](https://crates.io/crates/secrecy) crate. Wrap secret fields in SecretString, whose Debug impl prints [REDACTED] and requires .expose_secret() for access. Its serde feature keeps serialisation transparent so CredentialStore implementations are unaffected. Note that OAuthTokenResponse comes from the oauth2 crate and cannot be wrapped directly — Debug should be implemented manually on StoredCredentials to redact that field.
Describe the bug
StoredCredentialsandStoredAuthorizationStateincrates/rmcp/src/transport/auth.rsboth derive#[derive(Debug)]. This causes secret fields — OAuth access tokens, refresh tokens, PKCE verifiers, and CSRF tokens — to be printed in plaintext whenever these types are formatted via aDebugformatter. There is no compiler warning or type-system protection against this.To Reproduce
authfeature to yourrmcpdependency.StoredAuthorizationStateorStoredCredentials.{:?}in any log call:Expected behavior
Secret fields should be redacted in
Debugoutput:Logs
Current
{:?}output forStoredAuthorizationState:Additional context
Affected types:
StoredCredentials(auth.rsline 62) —token_responsecontainsaccess_tokenandrefresh_token:StoredAuthorizationState(auth.rsline 122):The MCP spec's own auth guidance explicitly states: "Don't log credentials. Never log Authorization headers, tokens, codes, or secrets." The current type definitions make it easy to violate this by accident — including via
anyhow/thiserrorerror chains that automatically formatDebugrepresentations of involved values.Suggested fix: use the
[secrecy](https://crates.io/crates/secrecy)crate. Wrap secret fields inSecretString, whoseDebugimpl prints[REDACTED]and requires.expose_secret()for access. Itsserdefeature keeps serialisation transparent soCredentialStoreimplementations are unaffected. Note thatOAuthTokenResponsecomes from theoauth2crate and cannot be wrapped directly —Debugshould be implemented manually onStoredCredentialsto redact that field.