简体   繁体   English

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

[英]How to exchange OAUTH token ( Go ) using Google API

Here is what I have so far which returns a "400 error". 到目前为止,这是我返回“ 400错误”的内容。 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. 要利用Google+登录的所有优势,您必须使用混合服务器端流程,其中用户使用JavaScript API客户端在客户端上对您的应用进行授权,并且您需要向服务器发送特殊的一次性授权代码。 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. 您的服务器交换一次使用的代码,以获取自己的访问权限,并从Google刷新令牌,以使服务器能够进行自己的API调用,这可以在用户离线时完成。 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 . 客户端代码非常简单,请遵循步骤3中的示例。

For server-side, I would recommend using the package oauth2 instead of goauth2 . 对于服务器端,我建议使用软件包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

For some reason, oauth2 package requires also appengine package. 由于某些原因, oauth2软件包还需要appengine软件包。

Exchanging the one-time code for a reusable token can be done using function NewTransportWithCode : 可以使用功能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 : 最后,您在步骤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))
}

Some error handling is missing, but you get the idea. 缺少一些错误处理,但是您明白了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM