diff --git a/CHANGELOG.md b/CHANGELOG.md index 843e2dc..5278554 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ Version numbers follow [Semantic Versioning](https://semver.org/). They were previously deprecated in v4.1.0 (#197) - Breaking: Fixed typo in RoomStateMessage's follower mode (was `follwers_only`, is now `followers_only`. (#200) - Minor: Added support for reply-parent tags (#189) +- Minor: Tokens in `CredentialsPair` and `UserAccessToken` are now redacted in their `Debug` output. Same + applies to the `client_secret` in `RefreshingLoginCredentials`. (#199) ## v5.0.1 diff --git a/src/login.rs b/src/login.rs index 70c3b1a..b47cb95 100644 --- a/src/login.rs +++ b/src/login.rs @@ -33,7 +33,7 @@ use { use {serde::Deserialize, serde::Serialize}; /// A pair of login name and OAuth token. -#[derive(Debug, Clone)] +#[derive(Clone)] #[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))] pub struct CredentialsPair { /// Login name of the user that the library should log into chat as. @@ -44,6 +44,16 @@ pub struct CredentialsPair { pub token: Option, } +// Custom implementation to display [redacted] in place of the token +impl Debug for CredentialsPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CredentialsPair") + .field("login", &self.login) + .field("token", &self.token.as_ref().map(|_| "[redacted]")) + .finish() + } +} + /// Encapsulates logic for getting the credentials to log into chat, whenever /// a new connection is made. #[async_trait] @@ -91,7 +101,7 @@ impl LoginCredentials for StaticLoginCredentials { /// The necessary details about a Twitch OAuth Access Token. This information is provided /// by Twitch's OAuth API after completing the user's authorization. #[cfg(feature = "__refreshing-token")] -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct UserAccessToken { /// OAuth access token pub access_token: String, @@ -103,6 +113,19 @@ pub struct UserAccessToken { pub expires_at: Option>, } +// Custom implementation to display [redacted] in place of the token +#[cfg(feature = "__refreshing-token")] +impl Debug for UserAccessToken { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UserAccessToken") + .field("access_token", &"[redacted]") + .field("refresh_token", &"[redacted]") + .field("created_at", &self.created_at) + .field("expires_at", &self.expires_at) + .finish() + } +} + /// Represents the Twitch API response to `POST /oauth2/token` API requests. /// /// Provided as a convenience for your own implementations, as you will typically need @@ -175,7 +198,7 @@ pub trait TokenStorage: Debug + Send + 'static { /// These can also be cloned before being passed to a `Client` so you can use them in other places, /// such as API calls. #[cfg(feature = "__refreshing-token")] -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct RefreshingLoginCredentials { http_client: reqwest::Client, user_login: Arc>>, @@ -184,6 +207,20 @@ pub struct RefreshingLoginCredentials { token_storage: Arc>, } +// Custom implementation to display [redacted] in place of the client secret +#[cfg(feature = "__refreshing-token")] +impl Debug for RefreshingLoginCredentials { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RefreshingLoginCredentials") + .field("http_client", &self.http_client) + .field("user_login", &self.user_login) + .field("client_id", &self.client_id) + .field("client_secret", &"[redacted]") + .field("token_storage", &self.token_storage) + .finish() + } +} + #[cfg(feature = "__refreshing-token")] impl RefreshingLoginCredentials { /// Create new login credentials with a backing token storage. The username belonging to the