diff --git a/cloud/storage/dropbox/dropbox.go b/cloud/storage/dropbox/dropbox.go index de72677..3135f6f 100644 --- a/cloud/storage/dropbox/dropbox.go +++ b/cloud/storage/dropbox/dropbox.go @@ -14,27 +14,22 @@ import ( "net/http" "strings" + "dropbox.upspin.io/oauth2" "upspin.io/cloud/storage" "upspin.io/errors" "upspin.io/upspin" ) -// apiToken is the key for the dial options in the storage.Storage interface. -const apiToken = "token" - // New initializes a Storage implementation that stores data to Dropbox. func New(opts *storage.Opts) (storage.Storage, error) { const op errors.Op = "cloud/storage/dropbox.New" - tok, ok := opts.Opts[apiToken] + token, ok := opts.Opts["refresh_token"] if !ok { - return nil, errors.E(op, errors.Invalid, errors.Errorf("%q option is required", apiToken)) + return nil, errors.E(op, errors.Invalid, errors.Errorf("refresh_token option is required")) } - return &dropboxImpl{ - client: http.DefaultClient, - token: tok, - }, nil + return &dropboxImpl{oauth2.Client(token)}, nil } func init() { @@ -44,7 +39,6 @@ func init() { // dropboxImpl is an implementation of Storage that connects to a Dropbox backend. type dropboxImpl struct { client *http.Client - token string } var ( @@ -216,7 +210,6 @@ func (d *dropboxImpl) newRequest(path string, body io.Reader, arg string) (*http return nil, err } - req.Header.Add("Authorization", "Bearer "+d.token) req.Header.Add("Content-Type", "application/octet-stream") if arg != "" { diff --git a/cloud/storage/dropbox/dropbox_test.go b/cloud/storage/dropbox/dropbox_test.go index 948b9d8..e9fe8fd 100644 --- a/cloud/storage/dropbox/dropbox_test.go +++ b/cloud/storage/dropbox/dropbox_test.go @@ -16,6 +16,7 @@ import ( "golang.org/x/oauth2" + dboauth2 "dropbox.upspin.io/oauth2" "upspin.io/cloud/storage" "upspin.io/upspin" ) @@ -30,6 +31,17 @@ var ( useDropbox = flag.Bool("use_dropbox", false, "enable to run dropbox tests; requires authentication code") ) +func init() { + dboauth2.Config = &oauth2.Config{ + ClientID: "ufhy41x7g4obzqz", + ClientSecret: "vuhgmucmxm93dp5", + Endpoint: oauth2.Endpoint{ + AuthURL: "https://www.dropbox.com/oauth2/authorize", + TokenURL: "https://api.dropboxapi.com/oauth2/token", + }, + } +} + func TestList(t *testing.T) { ls, ok := client.(storage.Lister) if !ok { @@ -139,7 +151,7 @@ func TestMain(m *testing.M) { cloud/storage/dropbox: skipping test as it requires Dropbox access. To enable this test, on the first run get an authentication code by visiting: -https://www.dropbox.com/oauth2/authorize?client_id=ufhy41x7g4obzqz&response_type=code +https://www.dropbox.com/oauth2/authorize?client_id=ufhy41x7g4obzqz&response_type=code&token_access_type=offline Copy the code and pass it by the -code flag. This will get an oAuth2 access token, store it and reuse it in successive test calls. @@ -155,7 +167,7 @@ it and reuse it in successive test calls. // Create client that writes to your Dropbox. client, err = storage.Dial("Dropbox", - storage.WithKeyValue("token", t)) + storage.WithKeyValue("refresh_token", t)) if err != nil { log.Fatalf("cloud/storage/dropbox: couldn't set up client: %v", err) } @@ -173,23 +185,14 @@ func token() (string, error) { return string(token), nil } - conf := &oauth2.Config{ - ClientID: "ufhy41x7g4obzqz", - ClientSecret: "vuhgmucmxm93dp5", - Endpoint: oauth2.Endpoint{ - AuthURL: "https://www.dropbox.com/oauth2/authorize", - TokenURL: "https://api.dropboxapi.com/oauth2/token", - }, - } - - tok, err := conf.Exchange(oauth2.NoContext, *authCode) + tok, err := dboauth2.Exchange(*authCode) if err != nil { return "", err } - if err := ioutil.WriteFile(tokenFile, []byte(tok.AccessToken), 0600); err != nil { + if err := ioutil.WriteFile(tokenFile, []byte(tok), 0600); err != nil { return "", err } - return tok.AccessToken, nil + return tok, nil } diff --git a/cmd/upspin-setupstorage-dropbox/setupstorage.go b/cmd/upspin-setupstorage-dropbox/setupstorage.go index 9603008..4b2ea4b 100644 --- a/cmd/upspin-setupstorage-dropbox/setupstorage.go +++ b/cmd/upspin-setupstorage-dropbox/setupstorage.go @@ -14,8 +14,7 @@ import ( "os" "path/filepath" - "golang.org/x/oauth2" - + "dropbox.upspin.io/oauth2" "upspin.io/subcmd" ) @@ -33,7 +32,7 @@ the specified authorization code to access your Dropbox. Before running this command, you must obtain an authorization code: -1. Go to https://www.dropbox.com/oauth2/authorize?client_id=wt1281n3q768jj3&response_type=code +1. Go to https://www.dropbox.com/oauth2/authorize?client_id=wt1281n3q768jj3&response_type=code&token_access_type=offline 2. Click "Allow" (you might have to log in first). 3. Copy the authorization code 4. Run setupstorage-dropbox -domain @@ -65,10 +64,14 @@ func main() { cfgPath := filepath.Join(*where, *domain) cfg := s.ReadServerConfig(cfgPath) + token, err := oauth2.Exchange(authCode) + if err != nil { + s.Exit(err) + } cfg.StoreConfig = []string{ "backend=Dropbox", - "token=" + s.token(authCode), + "refresh_token=" + token, } s.WriteServerConfig(cfgPath, cfg) @@ -76,21 +79,3 @@ func main() { s.ExitNow() } - -func (s *state) token(code string) string { - conf := &oauth2.Config{ - ClientID: "wt1281n3q768jj3", - ClientSecret: "blk944sx4oyf6aq", - Endpoint: oauth2.Endpoint{ - AuthURL: "https://www.dropbox.com/oauth2/authorize", - TokenURL: "https://api.dropboxapi.com/oauth2/token", - }, - } - - token, err := conf.Exchange(oauth2.NoContext, code) - if err != nil { - s.Exit(err) - } - - return token.AccessToken -} diff --git a/oauth2/config.go b/oauth2/config.go new file mode 100644 index 0000000..fb38fbe --- /dev/null +++ b/oauth2/config.go @@ -0,0 +1,40 @@ +// Copyright 2017 The Upspin Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "context" + "net/http" + + "golang.org/x/oauth2" +) + +// The default Dropbox client configuration; this client uses the `Apps/upspin/` +// folder in the linked Dropbox account for storage. +var Config = &oauth2.Config{ + ClientID: "wt1281n3q768jj3", + ClientSecret: "blk944sx4oyf6aq", + Endpoint: oauth2.Endpoint{ + AuthURL: "https://www.dropbox.com/oauth2/authorize", + TokenURL: "https://api.dropboxapi.com/oauth2/token", + }, +} + +// Client returns an HTTP client that populates the Authorization header of all +// requests with an access token. +func Client(refreshToken string) *http.Client { + token := oauth2.Token{RefreshToken: refreshToken} + return Config.Client(context.Background(), &token) +} + +// Exchange converts the authorization code into a refresh token. +func Exchange(authCode string) (string, error) { + token, err := Config.Exchange(context.Background(), authCode) + if err != nil { + return "", err + } + + return token.RefreshToken, nil +}