diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5894923..46e86d2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,13 @@ xxxx-yy-zz any header. * Users who use the default client shouldn't notice any changes. +# v0.18.1 +2024-05-06 +* fixed bug when using oauth2 refresh tokens using client secret instead of PKCE: + * See https://github.com/dropbox/dropbox-sdk-rust/issues/151 for details + * New function Authorize::from_client_secret_refresh_token() should be used for any refresh tokens previously obtained using this flow + * Thanks to Peerat Vichivanives for reporting and testing the fix + # v0.18.0 2024-01-12 * MSRV raised to 1.65.0 diff --git a/src/oauth2.rs b/src/oauth2.rs index da870ad..d4cc426 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -296,6 +296,7 @@ enum AuthorizationState { }, Refresh { refresh_token: String, + client_secret: Option, }, AccessToken { client_secret: Option, @@ -373,14 +374,34 @@ impl Authorization { }) } - /// Recreate the authorization from a refresh token. + /// Recreate the authorization from a refresh token obtained using the [`Oauth2Type::PKCE`] + /// flow. pub fn from_refresh_token( client_id: String, refresh_token: String, ) -> Self { Self { client_id, - state: AuthorizationState::Refresh { refresh_token }, + state: AuthorizationState::Refresh { + refresh_token, + client_secret: None, + }, + } + } + + /// Recreate the authorization from a refresh token obtained using the + /// [`Oauth2Type::AuthorizationCode`] flow. This requires the client secret as well. + pub fn from_client_secret_refresh_token( + client_id: String, + client_secret: String, + refresh_token: String, + ) -> Self { + Self { + client_id, + state: AuthorizationState::Refresh { + refresh_token, + client_secret: Some(client_secret), + }, } } @@ -452,8 +473,11 @@ impl Authorization { auth_code = Some(code); redirect_uri = uri; } - AuthorizationState::Refresh { refresh_token: refresh } => { + AuthorizationState::Refresh { refresh_token: refresh, client_secret: secret } => { refresh_token = Some(refresh); + if let Some(secret) = secret { + client_secret = Some(secret); + } } } @@ -470,6 +494,14 @@ impl Authorization { params.append_pair("client_id", &self.client_id); + if let Some(client_secret) = client_secret.as_deref() { + params.append_pair("client_secret", client_secret); + } + + if let Some(pkce) = &pkce_code { + params.append_pair("code_verifier", pkce); + } + if refresh_token.is_none() { if let Some(pkce) = pkce_code { params.append_pair("code_verifier", &pkce); @@ -531,7 +563,7 @@ impl Authorization { match refresh_token { Some(refresh) => { - self.state = AuthorizationState::Refresh { refresh_token: refresh }; + self.state = AuthorizationState::Refresh { refresh_token: refresh, client_secret }; } None if !matches!(self.state, AuthorizationState::Refresh {..}) => { self.state = AuthorizationState::AccessToken {