Skip to content

Commit

Permalink
fix for oauth2 refresh tokens obtained using client secret (#152)
Browse files Browse the repository at this point in the history
They are different from tokens obtained with the PKCE flow, and need the client secret to be provided again along with the refresh token when obtaining new tokens.
  • Loading branch information
wfraser authored May 6, 2024
1 parent 0e4b257 commit cd3e2e3
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "dropbox-sdk"
version = "0.18.0"
version = "0.18.1"
authors = ["Bill Fraser <[email protected]>"]
edition = "2018"
description = "Rust bindings to the Dropbox API, generated by Stone from the official spec."
Expand Down
7 changes: 7 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 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
Expand Down
46 changes: 34 additions & 12 deletions src/oauth2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ enum AuthorizationState {
},
Refresh {
refresh_token: String,
client_secret: Option<String>,
},
AccessToken {
client_secret: Option<String>,
Expand Down Expand Up @@ -365,14 +366,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),
},
}
}

Expand Down Expand Up @@ -431,8 +452,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);
}
}
}

Expand All @@ -448,14 +472,12 @@ impl Authorization {

params.append_pair("client_id", &self.client_id);

if refresh_token.is_none() {
if let Some(pkce) = pkce_code {
params.append_pair("code_verifier", &pkce);
} else {
params.append_pair(
"client_secret",
client_secret.as_ref().expect("need either PKCE code or client secret"));
}
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 let Some(value) = redirect_uri {
Expand Down Expand Up @@ -499,7 +521,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 {
Expand Down

0 comments on commit cd3e2e3

Please sign in to comment.