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

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

golang Facebook authentication using golang.org/x/oauth2

问题

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

clientOptions, err = oauth2.New(
	oauth2.Client("xxxxxx", "22222222222222"),
	oauth2.RedirectURL("http://localhost:3000/auth/cb/fb2"),
	oauth2.Scope("public_profile", "email", "user_friends"),
	oauth2.Endpoint(
		"https://www.facebook.com/dialog/oauth",
		"https://graph.facebook.com/oauth/access_token",
	),
)

func handleFBSetupOauth(w http.ResponseWriter, r *http.Request) {
	url := clientOptions.AuthCodeURL("state", "online", "auto")
	fmt.Printf("Visit the URL for the auth dialog: %v", url)

	http.Redirect(w, r, url, http.StatusFound)
}

func handleFBOauthCB(w http.ResponseWriter, r *http.Request) (int, string) {
	var err error

	code := r.FormValue("code")
	if code == "" {
		return 500, "No code!"
	}

	fmt.Printf("code - %s", code)

	t, err := clientOptions.NewTransportFromCode(code)
	if err != nil {
		log.Fatal(err)
	}

	client := http.Client{Transport: t}

	url := "https://graph.facebook.com/oauth/access_token?client_id=xxxxxxx&redirect_uri=http://localhost:3000/auth/cb/fb2&client_secret=22222222&code=" + code + ""

	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身份验证。

	clientOptions, err = oauth2.New(
		oauth2.Client("xxxxxx", "22222222222222"),
		oauth2.RedirectURL("http://localhost:3000/auth/cb/fb2"),
		oauth2.Scope("public_profile", "email", "user_friends"),
		oauth2.Endpoint(
			"https://www.facebook.com/dialog/oauth",
			"https://graph.facebook.com/oauth/access_token",
		),
	)
	
func handleFBSetupOauth(w http.ResponseWriter, r *http.Request) {
	url := clientOptions.AuthCodeURL("state", "online", "auto")
	fmt.Printf("Visit the URL for the auth dialog: %v", url)

	http.Redirect(w, r, url, http.StatusFound)
}

func handleFBOauthCB(w http.ResponseWriter, r *http.Request) (int, string) {
	var err error

	code := r.FormValue("code")
	if code == "" {
		return 500, "No code!"
	}

	fmt.Printf("code - %s", code)

	t, err := clientOptions.NewTransportFromCode(code)
	if err != nil {
		log.Fatal(err)
	}

	client := http.Client{Transport: t}

	url := "https://graph.facebook.com/oauth/access_token?client_id=xxxxxxx&redirect_uri=http://localhost:3000/auth/cb/fb2&client_secret=22222222&code=" + code + ""

	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

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

package main

import (
  "fmt"
  "io/ioutil"
  "log"
  "net/http"
  "net/url"
  "strings"

  "golang.org/x/oauth2"
  "golang.org/x/oauth2/facebook"
)

var (
  oauthConf = &oauth2.Config{
    ClientID:     "YOUR_CLIENT_ID",
    ClientSecret: "YOUR_CLIENT_SECRET",
    RedirectURL:  "YOUR_REDIRECT_URL_CALLBACK",
    Scopes:       []string{"public_profile"},
    Endpoint:     facebook.Endpoint,
  }
  oauthStateString = "thisshouldberandom"
)

const htmlIndex = `<html><body>
Logged in with <a href="/login">facebook</a>
</body></html>
`

func handleMain(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "text/html; charset=utf-8")
  w.WriteHeader(http.StatusOK)
  w.Write([]byte(htmlIndex))
}

func handleFacebookLogin(w http.ResponseWriter, r *http.Request) {
  Url, err := url.Parse(oauthConf.Endpoint.AuthURL)
  if err != nil {
    log.Fatal("Parse: ", err)
  }
  parameters := url.Values{}
  parameters.Add("client_id", oauthConf.ClientID)
  parameters.Add("scope", strings.Join(oauthConf.Scopes, " "))
  parameters.Add("redirect_uri", oauthConf.RedirectURL)
  parameters.Add("response_type", "code")
  parameters.Add("state", oauthStateString)
  Url.RawQuery = parameters.Encode()
  url := Url.String()
  http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}

func handleFacebookCallback(w http.ResponseWriter, r *http.Request) {
  state := r.FormValue("state")
  if state != oauthStateString {
    fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
    http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
    return
  }

  code := r.FormValue("code")

  token, err := oauthConf.Exchange(oauth2.NoContext, code)
  if err != nil {
    fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err)
    http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
    return
  }

  resp, err := http.Get("https://graph.facebook.com/me?access_token=" +
    url.QueryEscape(token.AccessToken))
  if err != nil {
    fmt.Printf("Get: %s\n", err)
    http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
    return
  }
  defer resp.Body.Close()

  response, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    fmt.Printf("ReadAll: %s\n", err)
    http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
    return
  }

  log.Printf("parseResponseBody: %s\n", string(response))

  http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}

func main() {
  http.HandleFunc("/", handleMain)
  http.HandleFunc("/login", handleFacebookLogin)
  http.HandleFunc("/oauth2callback", handleFacebookCallback)
  fmt.Print("Started running on http://localhost:9090\n")
  log.Fatal(http.ListenAndServe(":9090", nil))
}

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

英文:

Here is a simple example to get you started:

package main
import (
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;net/url&quot;
&quot;strings&quot;
&quot;golang.org/x/oauth2&quot;
&quot;golang.org/x/oauth2/facebook&quot;
)
var (
oauthConf = &amp;oauth2.Config{
ClientID:     &quot;YOUR_CLIENT_ID&quot;,
ClientSecret: &quot;YOUR_CLIENT_SECRET&quot;,
RedirectURL:  &quot;YOUR_REDIRECT_URL_CALLBACK&quot;,
Scopes:       []string{&quot;public_profile&quot;},
Endpoint:     facebook.Endpoint,
}
oauthStateString = &quot;thisshouldberandom&quot;
)
const htmlIndex = `&lt;html&gt;&lt;body&gt;
Logged in with &lt;a href=&quot;/login&quot;&gt;facebook&lt;/a&gt;
&lt;/body&gt;&lt;/html&gt;
`
func handleMain(w http.ResponseWriter, r *http.Request) {
w.Header().Set(&quot;Content-Type&quot;, &quot;text/html; charset=utf-8&quot;)
w.WriteHeader(http.StatusOK)
w.Write([]byte(htmlIndex))
}
func handleFacebookLogin(w http.ResponseWriter, r *http.Request) {
Url, err := url.Parse(oauthConf.Endpoint.AuthURL)
if err != nil {
log.Fatal(&quot;Parse: &quot;, err)
}
parameters := url.Values{}
parameters.Add(&quot;client_id&quot;, oauthConf.ClientID)
parameters.Add(&quot;scope&quot;, strings.Join(oauthConf.Scopes, &quot; &quot;))
parameters.Add(&quot;redirect_uri&quot;, oauthConf.RedirectURL)
parameters.Add(&quot;response_type&quot;, &quot;code&quot;)
parameters.Add(&quot;state&quot;, oauthStateString)
Url.RawQuery = parameters.Encode()
url := Url.String()
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
func handleFacebookCallback(w http.ResponseWriter, r *http.Request) {
state := r.FormValue(&quot;state&quot;)
if state != oauthStateString {
fmt.Printf(&quot;invalid oauth state, expected &#39;%s&#39;, got &#39;%s&#39;\n&quot;, oauthStateString, state)
http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
return
}
code := r.FormValue(&quot;code&quot;)
token, err := oauthConf.Exchange(oauth2.NoContext, code)
if err != nil {
fmt.Printf(&quot;oauthConf.Exchange() failed with &#39;%s&#39;\n&quot;, err)
http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
return
}
resp, err := http.Get(&quot;https://graph.facebook.com/me?access_token=&quot; +
url.QueryEscape(token.AccessToken))
if err != nil {
fmt.Printf(&quot;Get: %s\n&quot;, err)
http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
return
}
defer resp.Body.Close()
response, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf(&quot;ReadAll: %s\n&quot;, err)
http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
return
}
log.Printf(&quot;parseResponseBody: %s\n&quot;, string(response))
http.Redirect(w, r, &quot;/&quot;, http.StatusTemporaryRedirect)
}
func main() {
http.HandleFunc(&quot;/&quot;, handleMain)
http.HandleFunc(&quot;/login&quot;, handleFacebookLogin)
http.HandleFunc(&quot;/oauth2callback&quot;, handleFacebookCallback)
fmt.Print(&quot;Started running on http://localhost:9090\n&quot;)
log.Fatal(http.ListenAndServe(&quot;:9090&quot;, nil))
}

答案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 {

DialogOption := oauth2.SetAuthURLParam("auth_type", "rerequest")
// reauthorize - 总是需要权限
// rerequest - 用于被拒绝/撤销的权限
// reauthenticate - 总是要求用户确认密码
url := a.FacebookOauthConfig.AuthCodeURL(a.Config.FacebookClientState, DialogOption)
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:

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

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

// access resource server using token
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:

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

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:

确定