英文:
golang Facebook authentication using golang.org/x/oauth2
问题
我正在尝试使用golang.org/x/oauth2
包编写一个简单的程序。但是我似乎无法用授权码交换访问令牌。以下错误有点误导人,因为它说授权码已被使用,但每次调用登录对话框时我都会看到一个新的代码。我对golang还不熟悉,可能犯了一个基本的错误,任何指针都将非常有帮助
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
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_ID
、YOUR_CLIENT_SECRET
和YOUR_REDIRECT_URL_CALLBACK
替换为你自己的值。该示例创建了一个基本的Web服务器,当用户访问主页时,显示一个链接用于通过Facebook登录。用户点击链接后,将被重定向到Facebook的登录页面,登录成功后会返回一个授权码,然后通过授权码获取访问令牌,最后使用访问令牌获取用户信息。
英文:
Here is a simple example to get you started:
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))
}
答案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 = &oauth2.Config{
ClientID: a.Config.FacebookClientID,
ClientSecret: a.Config.FacebookClientSecret,
RedirectURL: a.Config.FacebookRedirectURL,
Scopes: []string{"public_profile", "email"},
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("https://graph.facebook.com/me?fields=id,name,email&access_token=" + 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("auth_type", "rerequest")
// 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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论