fix: facebook auth

This commit is contained in:
Krispire Admin
2026-06-02 16:42:22 +05:30
parent b895ceaccf
commit ac95a857bd
+56 -7
View File
@@ -17,7 +17,9 @@ import (
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strings"
"golang.org/x/oauth2"
@@ -150,6 +152,11 @@ type facebook struct{ id, secret string }
func (f *facebook) Name() string { return "facebook" }
// Bump in lockstep with whatever version your Meta app is configured for
// (Settings → Basic → API Version). Stay on a single supported version so
// /me responses match the documented schema.
const facebookGraphVersion = "v25.0"
func (f *facebook) Config(redirectURL string) *oauth2.Config {
return &oauth2.Config{
ClientID: f.id,
@@ -157,8 +164,8 @@ func (f *facebook) Config(redirectURL string) *oauth2.Config {
RedirectURL: redirectURL,
Scopes: []string{"email", "public_profile"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://www.facebook.com/v18.0/dialog/oauth",
TokenURL: "https://graph.facebook.com/v18.0/oauth/access_token",
AuthURL: "https://www.facebook.com/" + facebookGraphVersion + "/dialog/oauth",
TokenURL: "https://graph.facebook.com/" + facebookGraphVersion + "/oauth/access_token",
},
}
}
@@ -187,8 +194,18 @@ type facebookMe struct {
}
func (f *facebook) FetchProfile(ctx context.Context, tok *oauth2.Token) (*Profile, error) {
const url = "https://graph.facebook.com/v18.0/me?fields=id,email,name,picture.type(large)"
body, err := httpGet(ctx, tok, url)
// Facebook Graph API: pass the token as ?access_token=... (their
// canonical mode) rather than via Bearer header. Bearer works most
// of the time but is documented as a secondary mode and has been
// reported to silently drop fields in some Graph versions.
params := url.Values{
"fields": {"id,email,name,picture.type(large)"},
"access_token": {tok.AccessToken},
}
reqURL := "https://graph.facebook.com/" + facebookGraphVersion +
"/me?" + params.Encode()
body, err := plainGet(ctx, reqURL)
if err != nil {
return nil, fmt.Errorf("facebook /me: %w", err)
}
@@ -199,6 +216,12 @@ func (f *facebook) FetchProfile(ctx context.Context, tok *oauth2.Token) (*Profil
if u.ID == "" {
return nil, fmt.Errorf("facebook /me: empty id")
}
// If email permission was granted but FB still didn't return one,
// log the raw response so the operator can diagnose (commonly the
// user's FB account email is unverified — Graph API omits it silently).
if u.Email == "" {
log.Printf("oauth/facebook: /me returned no email; raw=%s", truncate(body, 512))
}
return &Profile{
Provider: "facebook",
ProviderUserID: u.ID,
@@ -209,9 +232,35 @@ func (f *facebook) FetchProfile(ctx context.Context, tok *oauth2.Token) (*Profil
}, nil
}
// httpGet performs a token-authenticated GET and returns the response body.
// The provider tokens include their auth header already (Bearer for Google,
// query-param for Facebook), so we let oauth2.Client handle it.
func truncate(b []byte, n int) string {
if len(b) <= n {
return string(b)
}
return string(b[:n]) + "…"
}
// plainGet performs an unauthenticated GET (the caller is expected to have
// already embedded any access token in the URL). Used by Facebook because
// Graph API treats `?access_token=` as the canonical auth mode.
func plainGet(ctx context.Context, url string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(body))
}
return io.ReadAll(resp.Body)
}
// httpGet performs a Bearer-authenticated GET — used by Google. Facebook
// uses plainGet with the token in a query param instead.
func httpGet(ctx context.Context, tok *oauth2.Token, url string) ([]byte, error) {
cli := oauth2.NewClient(ctx, oauth2.StaticTokenSource(tok))
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)