使用golang.org/x/oauth2进行golang的Facebook身份验证。

huangapple go评论111阅读模式
英文:

golang Facebook authentication using golang.org/x/oauth2

问题

我正在尝试使用golang.org/x/oauth2包编写一个简单的程序。但是我似乎无法用授权码交换访问令牌。以下错误有点误导人,因为它说授权码已被使用,但每次调用登录对话框时我都会看到一个新的代码。我对golang还不熟悉,可能犯了一个基本的错误,任何指针都将非常有帮助 使用golang.org/x/oauth2进行golang的Facebook身份验证。

  1. clientOptions, err = oauth2.New(
  2. oauth2.Client("xxxxxx", "22222222222222"),
  3. oauth2.RedirectURL("http://localhost:3000/auth/cb/fb2"),
  4. oauth2.Scope("public_profile", "email", "user_friends"),
  5. oauth2.Endpoint(
  6. "https://www.facebook.com/dialog/oauth",
  7. "https://graph.facebook.com/oauth/access_token",
  8. ),
  9. )
  10. func handleFBSetupOauth(w http.ResponseWriter, r *http.Request) {
  11. url := clientOptions.AuthCodeURL("state", "online", "auto")
  12. fmt.Printf("Visit the URL for the auth dialog: %v", url)
  13. http.Redirect(w, r, url, http.StatusFound)
  14. }
  15. func handleFBOauthCB(w http.ResponseWriter, r *http.Request) (int, string) {
  16. var err error
  17. code := r.FormValue("code")
  18. if code == "" {
  19. return 500, "No code!"
  20. }
  21. fmt.Printf("code - %s", code)
  22. t, err := clientOptions.NewTransportFromCode(code)
  23. if err != nil {
  24. log.Fatal(err)
  25. }
  26. client := http.Client{Transport: t}
  27. url := "https://graph.facebook.com/oauth/access_token?client_id=xxxxxxx&redirect_uri=http://localhost:3000/auth/cb/fb2&client_secret=22222222&code=" + code + ""
  28. resp, err := client.Get(url)

我从最后一个GET请求中得到以下错误 -

{"error":{"message":"This authorization code has been used.","type":"OAuthException","code":100}}

我正在遵循这两个指南 -
Facebook登录流程 -
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2

最新的goauth文档 -
https://godoc.org/golang.org/x/oauth2

英文:

I am trying to write a simple program using the golang.org/x/oauth2 package. But I can't seem to exchange code for an access token. The following error is a bit misleading as it says the authorisation code has been used but I see a new code every time I invoke the login dialog. I am new to golang and I might be making a basic mistake, any pointers would be really helpful 使用golang.org/x/oauth2进行golang的Facebook身份验证。

  1. clientOptions, err = oauth2.New(
  2. oauth2.Client("xxxxxx", "22222222222222"),
  3. oauth2.RedirectURL("http://localhost:3000/auth/cb/fb2"),
  4. oauth2.Scope("public_profile", "email", "user_friends"),
  5. oauth2.Endpoint(
  6. "https://www.facebook.com/dialog/oauth",
  7. "https://graph.facebook.com/oauth/access_token",
  8. ),
  9. )
  10. func handleFBSetupOauth(w http.ResponseWriter, r *http.Request) {
  11. url := clientOptions.AuthCodeURL("state", "online", "auto")
  12. fmt.Printf("Visit the URL for the auth dialog: %v", url)
  13. http.Redirect(w, r, url, http.StatusFound)
  14. }
  15. func handleFBOauthCB(w http.ResponseWriter, r *http.Request) (int, string) {
  16. var err error
  17. code := r.FormValue("code")
  18. if code == "" {
  19. return 500, "No code!"
  20. }
  21. fmt.Printf("code - %s", code)
  22. t, err := clientOptions.NewTransportFromCode(code)
  23. if err != nil {
  24. log.Fatal(err)
  25. }
  26. client := http.Client{Transport: t}
  27. url := "https://graph.facebook.com/oauth/access_token?client_id=xxxxxxx&redirect_uri=http://localhost:3000/auth/cb/fb2&client_secret=22222222&code=" + code + ""
  28. resp, err := client.Get(url)

I get the following error from the last get request -

{"error":{"message":"This authorization code has been used.","type":"OAuthException","code":100}}

I am following these 2 guides -
Facebook login flow -
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2

Latest goauth doc -
https://godoc.org/golang.org/x/oauth2

答案1

得分: 51

以下是一个简单的示例,帮助你入门:

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "log"
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. "golang.org/x/oauth2"
  10. "golang.org/x/oauth2/facebook"
  11. )
  12. var (
  13. oauthConf = &oauth2.Config{
  14. ClientID: "YOUR_CLIENT_ID",
  15. ClientSecret: "YOUR_CLIENT_SECRET",
  16. RedirectURL: "YOUR_REDIRECT_URL_CALLBACK",
  17. Scopes: []string{"public_profile"},
  18. Endpoint: facebook.Endpoint,
  19. }
  20. oauthStateString = "thisshouldberandom"
  21. )
  22. const htmlIndex = `<html><body>
  23. Logged in with <a href="/login">facebook</a>
  24. </body></html>
  25. `
  26. func handleMain(w http.ResponseWriter, r *http.Request) {
  27. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  28. w.WriteHeader(http.StatusOK)
  29. w.Write([]byte(htmlIndex))
  30. }
  31. func handleFacebookLogin(w http.ResponseWriter, r *http.Request) {
  32. Url, err := url.Parse(oauthConf.Endpoint.AuthURL)
  33. if err != nil {
  34. log.Fatal("Parse: ", err)
  35. }
  36. parameters := url.Values{}
  37. parameters.Add("client_id", oauthConf.ClientID)
  38. parameters.Add("scope", strings.Join(oauthConf.Scopes, " "))
  39. parameters.Add("redirect_uri", oauthConf.RedirectURL)
  40. parameters.Add("response_type", "code")
  41. parameters.Add("state", oauthStateString)
  42. Url.RawQuery = parameters.Encode()
  43. url := Url.String()
  44. http.Redirect(w, r, url, http.StatusTemporaryRedirect)
  45. }
  46. func handleFacebookCallback(w http.ResponseWriter, r *http.Request) {
  47. state := r.FormValue("state")
  48. if state != oauthStateString {
  49. fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
  50. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  51. return
  52. }
  53. code := r.FormValue("code")
  54. token, err := oauthConf.Exchange(oauth2.NoContext, code)
  55. if err != nil {
  56. fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err)
  57. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  58. return
  59. }
  60. resp, err := http.Get("https://graph.facebook.com/me?access_token=" +
  61. url.QueryEscape(token.AccessToken))
  62. if err != nil {
  63. fmt.Printf("Get: %s\n", err)
  64. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  65. return
  66. }
  67. defer resp.Body.Close()
  68. response, err := ioutil.ReadAll(resp.Body)
  69. if err != nil {
  70. fmt.Printf("ReadAll: %s\n", err)
  71. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  72. return
  73. }
  74. log.Printf("parseResponseBody: %s\n", string(response))
  75. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  76. }
  77. func main() {
  78. http.HandleFunc("/", handleMain)
  79. http.HandleFunc("/login", handleFacebookLogin)
  80. http.HandleFunc("/oauth2callback", handleFacebookCallback)
  81. fmt.Print("Started running on http://localhost:9090\n")
  82. log.Fatal(http.ListenAndServe(":9090", nil))
  83. }

这是一个使用Go语言编写的简单示例,用于演示如何使用Facebook的OAuth2进行登录。你需要将其中的YOUR_CLIENT_IDYOUR_CLIENT_SECRETYOUR_REDIRECT_URL_CALLBACK替换为你自己的值。该示例创建了一个基本的Web服务器,当用户访问主页时,显示一个链接用于通过Facebook登录。用户点击链接后,将被重定向到Facebook的登录页面,登录成功后会返回一个授权码,然后通过授权码获取访问令牌,最后使用访问令牌获取用户信息。

英文:

Here is a simple example to get you started:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;io/ioutil&quot;
  5. &quot;log&quot;
  6. &quot;net/http&quot;
  7. &quot;net/url&quot;
  8. &quot;strings&quot;
  9. &quot;golang.org/x/oauth2&quot;
  10. &quot;golang.org/x/oauth2/facebook&quot;
  11. )
  12. var (
  13. oauthConf = &amp;oauth2.Config{
  14. ClientID: &quot;YOUR_CLIENT_ID&quot;,
  15. ClientSecret: &quot;YOUR_CLIENT_SECRET&quot;,
  16. RedirectURL: &quot;YOUR_REDIRECT_URL_CALLBACK&quot;,
  17. Scopes: []string{&quot;public_profile&quot;},
  18. Endpoint: facebook.Endpoint,
  19. }
  20. oauthStateString = &quot;thisshouldberandom&quot;
  21. )
  22. const htmlIndex = `&lt;html&gt;&lt;body&gt;
  23. Logged in with &lt;a href=&quot;/login&quot;&gt;facebook&lt;/a&gt;
  24. &lt;/body&gt;&lt;/html&gt;
  25. `
  26. func handleMain(w http.ResponseWriter, r *http.Request) {
  27. w.Header().Set(&quot;Content-Type&quot;, &quot;text/html; charset=utf-8&quot;)
  28. w.WriteHeader(http.StatusOK)
  29. w.Write([]byte(htmlIndex))
  30. }
  31. func handleFacebookLogin(w http.ResponseWriter, r *http.Request) {
  32. Url, err := url.Parse(oauthConf.Endpoint.AuthURL)
  33. if err != nil {
  34. log.Fatal(&quot;Parse: &quot;, err)
  35. }
  36. parameters := url.Values{}
  37. parameters.Add(&quot;client_id&quot;, oauthConf.ClientID)
  38. parameters.Add(&quot;scope&quot;, strings.Join(oauthConf.Scopes, &quot; &quot;))
  39. parameters.Add(&quot;redirect_uri&quot;, oauthConf.RedirectURL)
  40. parameters.Add(&quot;response_type&quot;, &quot;code&quot;)
  41. parameters.Add(&quot;state&quot;, oauthStateString)
  42. Url.RawQuery = parameters.Encode()
  43. url := Url.String()
  44. http.Redirect(w, r, url, http.StatusTemporaryRedirect)
  45. }
  46. func handleFacebookCallback(w http.ResponseWriter, r *http.Request) {
  47. state := r.FormValue(&quot;state&quot;)
  48. if state != oauthStateString {
  49. fmt.Printf(&quot;invalid oauth state, expected &#39;%s&#39;, got &#39;%s&#39;\n&quot;, oauthStateString, state)
  50. http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
  51. return
  52. }
  53. code := r.FormValue(&quot;code&quot;)
  54. token, err := oauthConf.Exchange(oauth2.NoContext, code)
  55. if err != nil {
  56. fmt.Printf(&quot;oauthConf.Exchange() failed with &#39;%s&#39;\n&quot;, err)
  57. http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
  58. return
  59. }
  60. resp, err := http.Get(&quot;https://graph.facebook.com/me?access_token=&quot; +
  61. url.QueryEscape(token.AccessToken))
  62. if err != nil {
  63. fmt.Printf(&quot;Get: %s\n&quot;, err)
  64. http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
  65. return
  66. }
  67. defer resp.Body.Close()
  68. response, err := ioutil.ReadAll(resp.Body)
  69. if err != nil {
  70. fmt.Printf(&quot;ReadAll: %s\n&quot;, err)
  71. http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
  72. return
  73. }
  74. log.Printf(&quot;parseResponseBody: %s\n&quot;, string(response))
  75. http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
  76. }
  77. func main() {
  78. http.HandleFunc(&quot;/&quot;, handleMain)
  79. http.HandleFunc(&quot;/login&quot;, handleFacebookLogin)
  80. http.HandleFunc(&quot;/oauth2callback&quot;, handleFacebookCallback)
  81. fmt.Print(&quot;Started running on http://localhost:9090\n&quot;)
  82. log.Fatal(http.ListenAndServe(&quot;:9090&quot;, nil))
  83. }

答案2

得分: 0

我只会翻译你提供的文本,以下是翻译的结果:

我只是把它作为额外的补充放在这里,希望能为你节省一些时间:

要请求特定的权限,你需要在配置的Scopes字段中定义它们,就像上面的答案中一样:

// 设置 Facebook OAuth 配置
a.FacebookOauthConfig = &oauth2.Config{
ClientID: a.Config.FacebookClientID,
ClientSecret: a.Config.FacebookClientSecret,
RedirectURL: a.Config.FacebookRedirectURL,
Scopes: []string{"public_profile", "email"},
Endpoint: facebook.Endpoint,
}

但是,当请求资源服务器时,你需要显式地请求所需的字段,要特别注意fields=id,name,email

// 使用令牌访问资源服务器
res, _ := http.Get("https://graph.facebook.com/me?fields=id,name,email&access_token=" + token.AccessToken)

我花了一些时间才弄清楚为什么我没有在响应中得到电子邮件。

此外,你可以自定义对话行为,通过为a.FacebookOauthConfig.AuthCodeURL提供auth_type查询参数的特定值。
oauth2包提供的辅助方法oauth2.SetAuthURLParam可以帮助你初始化oauth2.AuthCodeOption

func (a *Auth) FacebookLoginHandler(c echo.Context) error {

  1. DialogOption := oauth2.SetAuthURLParam("auth_type", "rerequest")
  2. // reauthorize - 总是需要权限
  3. // rerequest - 用于被拒绝/撤销的权限
  4. // reauthenticate - 总是要求用户确认密码
  5. url := a.FacebookOauthConfig.AuthCodeURL(a.Config.FacebookClientState, DialogOption)
  6. return c.Redirect(http.StatusTemporaryRedirect, url)

}

英文:

I'll just leave it here as a small addition to rated answer, hopefully, it saves you some time:

To request for certain permissions, you have to define them in config Scopes field, like in answer above:

  1. // setup facebook oauth config
  2. a.FacebookOauthConfig = &amp;oauth2.Config{
  3. ClientID: a.Config.FacebookClientID,
  4. ClientSecret: a.Config.FacebookClientSecret,
  5. RedirectURL: a.Config.FacebookRedirectURL,
  6. Scopes: []string{&quot;public_profile&quot;, &quot;email&quot;},
  7. Endpoint: facebook.Endpoint,
  8. }

All tho, you need to request desired fields explicitly, when requesting resource server, close attention to fields=id,name,email:

  1. // access resource server using token
  2. res, _ := http.Get(&quot;https://graph.facebook.com/me?fields=id,name,email&amp;access_token=&quot; + token.AccessToken)

Took me a while to figure out why I'm not getting email in response.


In addition, you can customise your dialogue behaviour, providing auth_type query parameter of certain value to a.FacebookOauthConfig.AuthCodeURL.
Helper method oauth2.SetAuthURLParam, provided by oauth2 package, helps you to initialise oauth2.AuthCodeOption:

  1. func (a *Auth) FacebookLoginHandler(c echo.Context) error {
  2. DialogOption := oauth2.SetAuthURLParam(&quot;auth_type&quot;, &quot;rerequest&quot;)
  3. // reauthorize - always has for permissions
  4. // rerequest - for declined/revoked permissions
  5. // reauthenticate - always as user to confirm password
  6. url := a.FacebookOauthConfig.AuthCodeURL(a.Config.FacebookClientState, DialogOption)
  7. return c.Redirect(http.StatusTemporaryRedirect, url)
  8. }

huangapple
  • 本文由 发表于 2014年12月9日 07:32:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/27368973.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定