mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-02-20 07:37:26 -05:00
Fix Dropbox OAuth configuration
- Fix 'access_token and refresh_token can not be set at the same time' error - Update Dropbox to use only refresh_token (OpenDAL requirement) - Add client_id/client_secret to OAuth credential storage - Add validation for empty OAuth credentials - Update CLI and volume manager for enhanced OAuth support
This commit is contained in:
@@ -238,8 +238,7 @@ async fn add_dropbox_interactive(ctx: &Context) -> Result<()> {
|
||||
let client_id = text("App Key (Client ID)", false)?.unwrap();
|
||||
let client_secret = password("App Secret (Client Secret)", false)?.unwrap();
|
||||
|
||||
println!("\nAfter authorizing, you'll receive tokens:");
|
||||
let access_token = password("Access Token", false)?.unwrap();
|
||||
println!("\nAfter completing OAuth flow, you'll receive a refresh token:");
|
||||
let refresh_token = password("Refresh Token", false)?.unwrap();
|
||||
|
||||
println!("\nSummary:");
|
||||
@@ -257,7 +256,6 @@ async fn add_dropbox_interactive(ctx: &Context) -> Result<()> {
|
||||
display_name: name.clone(),
|
||||
config: CloudStorageConfig::Dropbox {
|
||||
root,
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
|
||||
@@ -177,9 +177,6 @@ impl VolumeAddCloudArgs {
|
||||
}
|
||||
}
|
||||
CloudServiceArg::Dropbox => {
|
||||
let access_token = self
|
||||
.access_token
|
||||
.ok_or("--access-token is required for Dropbox")?;
|
||||
let refresh_token = self
|
||||
.refresh_token
|
||||
.ok_or("--refresh-token is required for Dropbox")?;
|
||||
@@ -192,7 +189,6 @@ impl VolumeAddCloudArgs {
|
||||
|
||||
CloudStorageConfig::Dropbox {
|
||||
root: self.root,
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
|
||||
4420
apps/tauri/packages/ts-client/src/generated/types.ts
Normal file
4420
apps/tauri/packages/ts-client/src/generated/types.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -276,6 +276,8 @@ pub enum CredentialData {
|
||||
OAuth {
|
||||
access_token: String,
|
||||
refresh_token: String,
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
},
|
||||
|
||||
/// Simple API key
|
||||
@@ -310,6 +312,8 @@ impl CloudCredential {
|
||||
service: crate::volume::CloudServiceType,
|
||||
access_token: String,
|
||||
refresh_token: String,
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -317,6 +321,8 @@ impl CloudCredential {
|
||||
data: CredentialData::OAuth {
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
},
|
||||
created_at: chrono::Utc::now(),
|
||||
expires_at,
|
||||
@@ -426,6 +432,8 @@ mod tests {
|
||||
crate::volume::CloudServiceType::GoogleDrive,
|
||||
"access_token".to_string(),
|
||||
"refresh_token".to_string(),
|
||||
"client_id".to_string(),
|
||||
"client_secret".to_string(),
|
||||
Some(future),
|
||||
);
|
||||
|
||||
@@ -433,6 +441,8 @@ mod tests {
|
||||
crate::volume::CloudServiceType::GoogleDrive,
|
||||
"access_token".to_string(),
|
||||
"refresh_token".to_string(),
|
||||
"client_id".to_string(),
|
||||
"client_secret".to_string(),
|
||||
Some(past),
|
||||
);
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ pub enum CloudStorageConfig {
|
||||
secret_access_key: String,
|
||||
endpoint: Option<String>,
|
||||
},
|
||||
/// Google Drive with OAuth 2.0 credentials.
|
||||
/// Requires both access_token and refresh_token for automatic token renewal.
|
||||
GoogleDrive {
|
||||
root: Option<String>,
|
||||
access_token: String,
|
||||
@@ -39,6 +41,8 @@ pub enum CloudStorageConfig {
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
},
|
||||
/// OneDrive with OAuth 2.0 credentials.
|
||||
/// Requires both access_token and refresh_token for automatic token renewal.
|
||||
OneDrive {
|
||||
root: Option<String>,
|
||||
access_token: String,
|
||||
@@ -46,9 +50,11 @@ pub enum CloudStorageConfig {
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
},
|
||||
/// Dropbox with OAuth 2.0 refresh token for long-term access.
|
||||
/// OpenDAL automatically obtains and refreshes access tokens as needed.
|
||||
/// Only refresh_token is required (not access_token).
|
||||
Dropbox {
|
||||
root: Option<String>,
|
||||
access_token: String,
|
||||
refresh_token: String,
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
@@ -148,6 +154,28 @@ impl LibraryAction for VolumeAddCloudAction {
|
||||
client_id,
|
||||
client_secret,
|
||||
} => {
|
||||
// Validate required OAuth credentials for Google Drive
|
||||
if access_token.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"Google Drive requires a valid access_token".to_string(),
|
||||
));
|
||||
}
|
||||
if refresh_token.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"Google Drive requires a valid refresh_token".to_string(),
|
||||
));
|
||||
}
|
||||
if client_id.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"Google Drive requires a valid client_id".to_string(),
|
||||
));
|
||||
}
|
||||
if client_secret.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"Google Drive requires a valid client_secret".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let backend = CloudBackend::new_google_drive(
|
||||
access_token,
|
||||
refresh_token,
|
||||
@@ -167,6 +195,8 @@ impl LibraryAction for VolumeAddCloudAction {
|
||||
CloudServiceType::GoogleDrive,
|
||||
access_token.clone(),
|
||||
refresh_token.clone(),
|
||||
client_id.clone(),
|
||||
client_secret.clone(),
|
||||
None, // Google Drive tokens typically don't have a fixed expiry in the refresh flow
|
||||
);
|
||||
|
||||
@@ -190,6 +220,28 @@ impl LibraryAction for VolumeAddCloudAction {
|
||||
client_id,
|
||||
client_secret,
|
||||
} => {
|
||||
// Validate required OAuth credentials for OneDrive
|
||||
if access_token.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"OneDrive requires a valid access_token".to_string(),
|
||||
));
|
||||
}
|
||||
if refresh_token.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"OneDrive requires a valid refresh_token".to_string(),
|
||||
));
|
||||
}
|
||||
if client_id.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"OneDrive requires a valid client_id".to_string(),
|
||||
));
|
||||
}
|
||||
if client_secret.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"OneDrive requires a valid client_secret".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let backend = CloudBackend::new_onedrive(
|
||||
access_token,
|
||||
refresh_token,
|
||||
@@ -206,6 +258,8 @@ impl LibraryAction for VolumeAddCloudAction {
|
||||
CloudServiceType::OneDrive,
|
||||
access_token.clone(),
|
||||
refresh_token.clone(),
|
||||
client_id.clone(),
|
||||
client_secret.clone(),
|
||||
None,
|
||||
);
|
||||
|
||||
@@ -224,13 +278,28 @@ impl LibraryAction for VolumeAddCloudAction {
|
||||
}
|
||||
CloudStorageConfig::Dropbox {
|
||||
root,
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
} => {
|
||||
// Validate required OAuth credentials for Dropbox
|
||||
if refresh_token.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"Dropbox requires a valid refresh_token".to_string(),
|
||||
));
|
||||
}
|
||||
if client_id.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"Dropbox requires a valid client_id".to_string(),
|
||||
));
|
||||
}
|
||||
if client_secret.trim().is_empty() {
|
||||
return Err(ActionError::InvalidInput(
|
||||
"Dropbox requires a valid client_secret".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let backend = CloudBackend::new_dropbox(
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
@@ -243,8 +312,10 @@ impl LibraryAction for VolumeAddCloudAction {
|
||||
|
||||
let credential = CloudCredential::new_oauth(
|
||||
CloudServiceType::Dropbox,
|
||||
access_token.clone(),
|
||||
"".to_string(),
|
||||
refresh_token.clone(),
|
||||
client_id.clone(),
|
||||
client_secret.clone(),
|
||||
None,
|
||||
);
|
||||
|
||||
|
||||
@@ -136,15 +136,17 @@ impl CloudBackend {
|
||||
}
|
||||
|
||||
/// Create a new cloud backend for Dropbox
|
||||
///
|
||||
/// Uses OAuth 2.0 refresh token for long-term access. OpenDAL automatically
|
||||
/// refreshes the access token when it expires, ensuring continuous operation
|
||||
/// without manual intervention.
|
||||
pub async fn new_dropbox(
|
||||
access_token: impl AsRef<str>,
|
||||
refresh_token: impl AsRef<str>,
|
||||
client_id: impl AsRef<str>,
|
||||
client_secret: impl AsRef<str>,
|
||||
root: Option<String>,
|
||||
) -> Result<Self, VolumeError> {
|
||||
let mut builder = opendal::services::Dropbox::default()
|
||||
.access_token(access_token.as_ref())
|
||||
.refresh_token(refresh_token.as_ref())
|
||||
.client_id(client_id.as_ref())
|
||||
.client_secret(client_secret.as_ref());
|
||||
|
||||
@@ -238,13 +238,15 @@ impl VolumeManager {
|
||||
if let crate::crypto::cloud_credentials::CredentialData::OAuth {
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
} = &credential.data
|
||||
{
|
||||
crate::volume::CloudBackend::new_google_drive(
|
||||
access_token,
|
||||
refresh_token,
|
||||
"", // client_id not stored yet
|
||||
"", // client_secret not stored yet
|
||||
client_id,
|
||||
client_secret,
|
||||
Some(cloud_identifier.clone()),
|
||||
).await
|
||||
} else {
|
||||
@@ -256,13 +258,15 @@ impl VolumeManager {
|
||||
if let crate::crypto::cloud_credentials::CredentialData::OAuth {
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
} = &credential.data
|
||||
{
|
||||
crate::volume::CloudBackend::new_onedrive(
|
||||
access_token,
|
||||
refresh_token,
|
||||
"",
|
||||
"",
|
||||
client_id,
|
||||
client_secret,
|
||||
Some(cloud_identifier.clone()),
|
||||
).await
|
||||
} else {
|
||||
@@ -272,15 +276,16 @@ impl VolumeManager {
|
||||
}
|
||||
crate::volume::CloudServiceType::Dropbox => {
|
||||
if let crate::crypto::cloud_credentials::CredentialData::OAuth {
|
||||
access_token,
|
||||
refresh_token,
|
||||
client_id,
|
||||
client_secret,
|
||||
..
|
||||
} = &credential.data
|
||||
{
|
||||
crate::volume::CloudBackend::new_dropbox(
|
||||
access_token,
|
||||
refresh_token,
|
||||
"",
|
||||
"",
|
||||
client_id,
|
||||
client_secret,
|
||||
Some(cloud_identifier.clone()),
|
||||
).await
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user