Skip to content

Commit 1ba7c66

Browse files
sayounaeneasr
andauthored
fix: add support for verified Graph API calls for facebook oidc provider (#2547)
Co-authored-by: aeneasr <[email protected]>
1 parent 659cf57 commit 1ba7c66

File tree

5 files changed

+119
-1
lines changed

5 files changed

+119
-1
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ require (
5858
github.com/hashicorp/golang-lru v0.5.4
5959
github.com/imdario/mergo v0.3.12
6060
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743
61+
github.com/jarcoal/httpmock v1.0.5
6162
github.com/jteeuwen/go-bindata v3.0.7+incompatible
6263
github.com/julienschmidt/httprouter v1.3.0
6364
github.com/knadh/koanf v1.4.0

go.sum

+1
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,7 @@ github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
11421142
github.com/jandelgado/gcov2lcov v1.0.4/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
11431143
github.com/jandelgado/gcov2lcov v1.0.5 h1:rkBt40h0CVK4oCb8Dps950gvfd1rYvQ8+cWa346lVU0=
11441144
github.com/jandelgado/gcov2lcov v1.0.5/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
1145+
github.com/jarcoal/httpmock v1.0.5 h1:cHtVEcTxRSX4J0je7mWPfc9BpDpqzXSJ5HbymZmyHck=
11451146
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
11461147
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
11471148
github.com/jcchavezs/porto v0.1.0/go.mod h1:fESH0gzDHiutHRdX2hv27ojnOVFco37hg1W6E9EZF4A=

selfservice/strategy/oidc/provider_facebook.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ package oidc
22

33
import (
44
"context"
5+
"crypto/hmac"
6+
"crypto/sha256"
7+
"encoding/hex"
58
"encoding/json"
9+
"fmt"
610
"net/url"
711

812
"github.com/hashicorp/go-retryablehttp"
@@ -34,6 +38,15 @@ func NewProviderFacebook(
3438
}
3539
}
3640

41+
func (g *ProviderFacebook) generateAppSecretProof(ctx context.Context, exchange *oauth2.Token) string {
42+
secret := g.config.ClientSecret
43+
data := exchange.AccessToken
44+
45+
h := hmac.New(sha256.New, []byte(secret))
46+
h.Write([]byte(data))
47+
return hex.EncodeToString(h.Sum(nil))
48+
}
49+
3750
func (g *ProviderFacebook) OAuth2(ctx context.Context) (*oauth2.Config, error) {
3851
p, err := g.provider(ctx)
3952
if err != nil {
@@ -52,8 +65,9 @@ func (g *ProviderFacebook) Claims(ctx context.Context, exchange *oauth2.Token, q
5265
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
5366
}
5467

68+
appSecretProof := g.generateAppSecretProof(ctx, exchange)
5569
client := g.reg.HTTPClient(ctx, httpx.ResilientClientWithClient(o.Client(ctx, exchange)))
56-
u, err := url.Parse("https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender")
70+
u, err := url.Parse(fmt.Sprintf("https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender&appsecret_proof=%s", appSecretProof))
5771
if err != nil {
5872
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
5973
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package oidc_test
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"net/url"
7+
"testing"
8+
"time"
9+
10+
"github.com/jarcoal/httpmock"
11+
12+
"github.com/ory/kratos/internal"
13+
"github.com/ory/kratos/selfservice/strategy/oidc"
14+
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
16+
"golang.org/x/oauth2"
17+
)
18+
19+
const fakeIDToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjk5OTk5OTk5OTksImF1ZCI6ImFiY2QiLCJpc3MiOiJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYWVuZWFzci9wcml2YXRlLW9pZGMvbWFzdGVyL3Rva2VuIn0.G9v8pJXJrEOgdJ5ecE6sIIcTH_p-RKkBaImfZY5DDVCl7h5GEis1n3GKKYbL_O3fj8Fu-WzI2mquI8S8BOVCQ6wN0XtrqJv22iX_nzeVHc4V_JWV1q7hg2gPpoFFcnF3KKtxZLvDOA8ujsDbAXmoBu0fEBdwCN56xLOOKQDzULyfijuAa8hrCwespZ9HaqcHzD3iHf_Utd4nHqlTM-6upWpKIMkplS_NGcxrfIRIWusZ0wob6ryy8jECD9QeZpdTGUozq-YM64lZfMOZzuLuqichH_PCMKFyB_tOZb6lDIiiSX4Irz7_YF-DP-LmfxgIW4934RqTCeFGGIP64h4xAA"
20+
21+
func TestProviderFacebook_Claims(t *testing.T) {
22+
httpmock.Activate()
23+
defer httpmock.DeactivateAndReset()
24+
25+
httpmock.RegisterResponder("GET", "https://graph.facebook.com/me",
26+
func(req *http.Request) (*http.Response, error) {
27+
if _, ok := req.URL.Query()["appsecret_proof"]; !ok {
28+
resp, err := httpmock.NewJsonResponse(400, map[string]interface{}{
29+
"error": map[string]interface{}{
30+
"message": "API calls from the server require an appsecret_proof argument",
31+
"type": "GraphMethodException",
32+
"code": 100,
33+
"fbtrace_id": "Ay8LR3n5BsHm809VYpJ3eDM",
34+
},
35+
})
36+
return resp, err
37+
}
38+
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
39+
"id": "123456789012345",
40+
"name": "John Doe",
41+
"first_name": "John",
42+
"last_name": "Doe",
43+
"email": "[email protected]",
44+
"birthday": "01/01/1990",
45+
})
46+
return resp, err
47+
},
48+
)
49+
50+
httpmock.RegisterResponder("GET", "https://www.facebook.com/.well-known/openid-configuration",
51+
func(req *http.Request) (*http.Response, error) {
52+
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
53+
"issuer": "https://www.facebook.com",
54+
})
55+
return resp, err
56+
},
57+
)
58+
59+
_, reg := internal.NewFastRegistryWithMocks(t)
60+
c := &oidc.Configuration{
61+
ID: "facebook",
62+
Provider: "facebook",
63+
ClientID: "abcd",
64+
ClientSecret: "secret",
65+
Mapper: "file://./stub/oidc.facebook.jsonnet",
66+
Scope: []string{"email"},
67+
}
68+
facebook := oidc.NewProviderFacebook(c, reg)
69+
70+
actual, err := facebook.Claims(
71+
context.Background(),
72+
(&oauth2.Token{AccessToken: "foo", Expiry: time.Now().Add(time.Hour)}).WithExtra(map[string]interface{}{"id_token": fakeIDToken}),
73+
url.Values{},
74+
)
75+
require.NoError(t, err)
76+
77+
assert.Equal(t, &oidc.Claims{
78+
Issuer: "https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender&appsecret_proof=773ba44693c7553d6ee20f61ea5d2757a9a4f4a44d2841ae4e95b52e4cd62db4",
79+
Subject: "123456789012345",
80+
Name: "John Doe",
81+
GivenName: "John",
82+
FamilyName: "Doe",
83+
Nickname: "John Doe",
84+
PreferredUsername: "John Doe",
85+
86+
EmailVerified: true,
87+
Birthdate: "01/01/1990",
88+
}, actual)
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
local claims = std.extVar('claims');
2+
3+
if std.length(claims.sub) == 0 then
4+
error 'claim sub not set'
5+
else
6+
{
7+
identity: {
8+
traits: {
9+
subject: claims.sub,
10+
[if "email" in claims then "email" else null]: claims.email,
11+
},
12+
},
13+
}

0 commit comments

Comments
 (0)