如何使用Google API交换OAUTH令牌(Go)

Here is what I have so far which returns a "400 error". Am I doing something wrong? I can't figure it out why is not working as the request is pretty straightforward

package main

import (
    "code.google.com/p/goauth2/oauth"
    "fmt"
    "log"
)

func main() {
    cachefile := "cache.json"
    code := "4/xxxxx.8uFT5Z0slpMbJvIeHux6iLY_9k7ajw" //the code received from the URL redirect
    // Set up a configuration.
    config := &oauth.Config{
        ClientId:     "xx.apps.googleusercontent.com",
        ClientSecret: "cWP3HudD3XmaP33j8",
        RedirectURL:  "https://crm.com/sender/gmail/auth/callBack",
        Scope:        "https://www.googleapis.com/auth/gmail.compose",
        AuthURL:      "https://accounts.google.com/o/oauth2/auth",
        TokenURL:     "https://accounts.google.com/o/oauth2/token",
        AccessType:   "offline",
        TokenCache:   oauth.CacheFile(cachefile),
    }

    // Set up a Transport using the config.
    transport := &oauth.Transport{Config: config}
    token, err := config.TokenCache.Token()
    if err != nil {
        token, err = transport.Exchange(code)
        if err != nil {
            log.Fatal("Exchange:", err)
        }
    }
    // (The Exchange method will automatically cache the token.)

    transport.Token = token
    fmt.Println(token)
}

Result

Exchange:OAuthError: updateToken: Unexpected HTTP status 400 Bad Request

I would recommend to use 'one-time code flow', as described in the documentation:

To take advantage of all of the benefits of Google+ Sign-In you must use a hybrid server-side flow where a user authorizes your app on the client side using the JavaScript API client and you send a special one-time authorization code to your server. Your server exchanges this one-time-use code to acquire its own access and refresh tokens from Google for the server to be able to make its own API calls, which can be done while the user is offline. This one-time code flow has security advantages over both a pure server-side flow and over sending access tokens to your server.

As code can be used just once there are less chances of compromising user's account.

Client code is pretty straight forward, follow the example in step 3.

For server-side, I would recommend using the package oauth2 instead of goauth2.

$ go get code.google.com/p/google-api-go-client/plus/v1
$ go get github.com/golang/oauth2
$ go get google.golang.org/appengine

For some reason, oauth2 package requires also appengine package.

Exchanging the one-time code for a reusable token can be done using function NewTransportWithCode:

func exchangeCode(code string) (*oauth2.Token, *oauth2.Transport, error) {
    config, err := google.NewConfig(&oauth2.Options{
        ClientID:     CLIENT_ID,
        ClientSecret: CLIENT_SECRET,
        RedirectURL:  "postmessage",
        Scopes:       []string{"https://www.googleapis.com/auth/plus.login"},
    })
    if err != nil {
        return &oauth2.Token{}, &oauth2.Transport{}, err
    }

    transport, err := config.NewTransportWithCode(code)
    if err != nil {
        return &oauth2.Token{}, &oauth2.Transport{}, err
    }

    token := transport.Token()
    return token, transport, nil
}

And finally the code you created in step 3 can submit one time code to a handler listening at /oauth2:

func oauth2Handler(w http.ResponseWriter, r *http.Request) {
    // TODO Check request has...
    // - Method: POST
    // - Content-Type: application/octet-stream; charset=utf-8
    // - CSRF Token http://goo.gl/mNCjJm

    body, err := ioutil.ReadAll(r.Body)
    defer r.Body.Close()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    code := string(body[:])
    token, transport, err := exchangeCode(code)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // From here you can use the transport
    client := http.Client{Transport: transport}
    service, err := plus.New(&client)
    if err != nil {
        return nil, err
    }

    // https://www.googleapis.com/plus/v1/people/me
    person, err := service.People.Get("me").Do()
    // ...
}

func main() {
    http.HandleFunc("/oauth2", oauth2Handler)
    log.Fatal(http.ListenAndServe(":8000", nil))
}

Some error handling is missing, but you get the idea.