// Common authentication between Google Drive and Google Cloud Storage package googleauth import ( "encoding/json" "fmt" "log" "code.google.com/p/goauth2/oauth" "github.com/ncw/rclone/fs" ) // A token cache to save the token in the config file section named type TokenCache string // Get the token from the config file - returns an error if it isn't present func (name TokenCache) Token() (*oauth.Token, error) { tokenString, err := fs.ConfigFile.GetValue(string(name), "token") if err != nil { return nil, err } if tokenString == "" { return nil, fmt.Errorf("Empty token found - please reconfigure") } token := new(oauth.Token) err = json.Unmarshal([]byte(tokenString), token) if err != nil { return nil, err } return token, nil } // Save the token to the config file // // This saves the config file if it changes func (name TokenCache) PutToken(token *oauth.Token) error { tokenBytes, err := json.Marshal(token) if err != nil { return err } tokenString := string(tokenBytes) old := fs.ConfigFile.MustValue(string(name), "token") if tokenString != old { fs.ConfigFile.SetValue(string(name), "token", tokenString) fs.SaveConfig() } return nil } // Auth contains information to authenticate an app against google services type Auth struct { Scope string DefaultClientId string DefaultClientSecret string } // Makes a new transport using authorisation from the config // // Doesn't have a token yet func (auth *Auth) newTransport(name string) (*oauth.Transport, error) { clientId := fs.ConfigFile.MustValue(name, "client_id") if clientId == "" { clientId = auth.DefaultClientId } clientSecret := fs.ConfigFile.MustValue(name, "client_secret") if clientSecret == "" { clientSecret = auth.DefaultClientSecret } // Settings for authorization. var config = &oauth.Config{ ClientId: clientId, ClientSecret: clientSecret, Scope: auth.Scope, RedirectURL: "urn:ietf:wg:oauth:2.0:oob", AuthURL: "https://accounts.google.com/o/oauth2/auth", TokenURL: "https://accounts.google.com/o/oauth2/token", TokenCache: TokenCache(name), } t := &oauth.Transport{ Config: config, Transport: fs.Config.Transport(), } return t, nil } // Makes a new transport using authorisation from the config with token func (auth *Auth) NewTransport(name string) (*oauth.Transport, error) { t, err := auth.newTransport(name) if err != nil { return nil, err } // Try to pull the token from the cache; if this fails, we need to get one. token, err := t.Config.TokenCache.Token() if err != nil { return nil, fmt.Errorf("Failed to get token: %s", err) } t.Token = token return t, nil } // Configuration helper - called after the user has put in the defaults func (auth *Auth) Config(name string) { // See if already have a token tokenString := fs.ConfigFile.MustValue(name, "token") if tokenString != "" { fmt.Printf("Already have a token - refresh?\n") if !fs.Confirm() { return } } // Get a transport t, err := auth.newTransport(name) if err != nil { log.Fatalf("Couldn't make transport: %v", err) } // Generate a URL for the user to visit for authorization. authUrl := t.Config.AuthCodeURL("state") fmt.Printf("Go to the following link in your browser\n") fmt.Printf("%s\n", authUrl) fmt.Printf("Log in, then type paste the token that is returned in the browser here\n") // Read the code, and exchange it for a token. fmt.Printf("Enter verification code> ") authCode := fs.ReadLine() _, err = t.Exchange(authCode) if err != nil { log.Fatalf("Failed to get token: %v", err) } }