diff --git a/src/duplicacy_oneclient.go b/src/duplicacy_oneclient.go index ec0358c6..9bdeb6f1 100644 --- a/src/duplicacy_oneclient.go +++ b/src/duplicacy_oneclient.go @@ -5,6 +5,7 @@ package duplicacy import ( + "context" "bytes" "encoding/json" "fmt" @@ -39,6 +40,7 @@ type OneDriveClient struct { TokenFile string Token *oauth2.Token + OAConfig *oauth2.Config TokenLock *sync.Mutex IsConnected bool @@ -49,7 +51,7 @@ type OneDriveClient struct { APIURL string } -func NewOneDriveClient(tokenFile string, isBusiness bool) (*OneDriveClient, error) { +func NewOneDriveClient(tokenFile string, isBusiness bool, client_id string, client_secret string) (*OneDriveClient, error) { description, err := ioutil.ReadFile(tokenFile) if err != nil { @@ -65,10 +67,25 @@ func NewOneDriveClient(tokenFile string, isBusiness bool) (*OneDriveClient, erro HTTPClient: http.DefaultClient, TokenFile: tokenFile, Token: token, + OAConfig: nil, TokenLock: &sync.Mutex{}, IsBusiness: isBusiness, } + if (client_id != "") { + oneOauthConfig := oauth2.Config{ + ClientID: client_id, + ClientSecret: client_secret, + Scopes: []string{"Files.ReadWrite", "offline_access"}, + Endpoint: oauth2.Endpoint{ + AuthURL: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", + TokenURL: "https://login.microsoftonline.com/common/oauth2/v2.0/token", + }, + } + + client.OAConfig = &oneOauthConfig + } + if isBusiness { client.RefreshTokenURL = "https://duplicacy.com/odb_refresh" client.APIURL = "https://graph.microsoft.com/v1.0/me" @@ -218,15 +235,25 @@ func (client *OneDriveClient) RefreshToken(force bool) (err error) { return nil } - readCloser, _, err := client.call(client.RefreshTokenURL, "POST", client.Token, "") - if err != nil { - return fmt.Errorf("failed to refresh the access token: %v", err) - } + if (client.OAConfig == nil) { + readCloser, _, err := client.call(client.RefreshTokenURL, "POST", client.Token, "") + if err != nil { + return fmt.Errorf("failed to refresh the access token: %v", err) + } - defer readCloser.Close() + defer readCloser.Close() - if err = json.NewDecoder(readCloser).Decode(client.Token); err != nil { - return err + if err = json.NewDecoder(readCloser).Decode(client.Token); err != nil { + return err + } + } else { + ctx := context.Background() + tokenSource := client.OAConfig.TokenSource(ctx, client.Token) + token, err := tokenSource.Token() + if err != nil { + return fmt.Errorf("failed to refresh the access token: %v", err) + } + client.Token = token } description, err := json.Marshal(client.Token) diff --git a/src/duplicacy_onestorage.go b/src/duplicacy_onestorage.go index e6cb9c21..2e24333c 100644 --- a/src/duplicacy_onestorage.go +++ b/src/duplicacy_onestorage.go @@ -19,13 +19,13 @@ type OneDriveStorage struct { } // CreateOneDriveStorage creates an OneDrive storage object. -func CreateOneDriveStorage(tokenFile string, isBusiness bool, storagePath string, threads int) (storage *OneDriveStorage, err error) { +func CreateOneDriveStorage(tokenFile string, isBusiness bool, storagePath string, threads int, client_id string, client_secret string) (storage *OneDriveStorage, err error) { for len(storagePath) > 0 && storagePath[len(storagePath)-1] == '/' { storagePath = storagePath[:len(storagePath)-1] } - client, err := NewOneDriveClient(tokenFile, isBusiness) + client, err := NewOneDriveClient(tokenFile, isBusiness, client_id, client_secret) if err != nil { return nil, err } diff --git a/src/duplicacy_storage.go b/src/duplicacy_storage.go index e098026c..e97f42d4 100644 --- a/src/duplicacy_storage.go +++ b/src/duplicacy_storage.go @@ -647,12 +647,28 @@ func CreateStorage(preference Preference, resetPassword bool, threads int) (stor storagePath := matched[3] + matched[4] prompt := fmt.Sprintf("Enter the path of the OneDrive token file (downloadable from https://duplicacy.com/one_start):") tokenFile := GetPassword(preference, matched[1] + "_token", prompt, true, resetPassword) - oneDriveStorage, err := CreateOneDriveStorage(tokenFile, matched[1] == "odb", storagePath, threads) + + // client_id, just like tokenFile, can be stored in preferences + //prompt = fmt.Sprintf("Enter client_id for custom Azure app (if empty will use duplicacy.com one):") + client_id := GetPasswordFromPreference(preference, matched[1] + "_client_id") + client_secret := "" + + if client_id != "" { + // client_secret should go into keyring + prompt = fmt.Sprintf("Enter client_secret for custom Azure app (if empty will use duplicacy.com one):") + client_secret = GetPassword(preference, matched[1] + "_client_secret", prompt, true, resetPassword) + } + + oneDriveStorage, err := CreateOneDriveStorage(tokenFile, matched[1] == "odb", storagePath, threads, client_id, client_secret) if err != nil { LOG_ERROR("STORAGE_CREATE", "Failed to load the OneDrive storage at %s: %v", storageURL, err) return nil } + SavePassword(preference, matched[1] + "_token", tokenFile) + if client_id != "" { + SavePassword(preference, matched[1] + "_client_secret", client_secret) + } return oneDriveStorage } else if matched[1] == "hubic" { storagePath := matched[3] + matched[4]