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

时间:2014-08-19 08:39:56

标签: oauth go google-oauth

这是我到目前为止返回“400错误”的内容。难道我做错了什么?我无法弄清楚为什么不能正常工作,因为请求非常简单

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)
}

结果

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

1 个答案:

答案 0 :(得分:2)

我建议使用'一次性代码流',如上所述in the documentation

  

要充分利用Google+登录的所有优势,您必须使用混合服务器端流程,用户使用JavaScript API客户端在客户端授权您的应用,并发送特殊的一次性授权码到你的服务器。您的服务器交换此一次性代码以获取其自己的访问权限并从Google刷新令牌,以便服务器能够进行自己的API调用,这可以在用户离线时完成。这种一次性代码流比纯服务器端流和向服务器发送访问令牌都具有安全优势。

由于代码只能使用一次,因此损害用户帐户的机会就会减少。

客户端代码非常简单,请遵循示例in step 3

对于服务器端,我建议使用包oauth2而不是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

由于某些原因,oauth2包也需要appengine包。

可以使用函数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
}

最后,您在步骤3中创建的代码可以将一个时间代码提交给在/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))
}

缺少一些错误处理,但你明白了。