Skip to content

Commit

Permalink
fix: add support for verified Graph API calls for facebook oidc provi…
Browse files Browse the repository at this point in the history
…der (#2547)

Co-authored-by: aeneasr <3372410+aeneasr@users.noreply.github.com>
  • Loading branch information
sayoun and aeneasr committed Jul 5, 2022
1 parent 659cf57 commit 1ba7c66
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 1 deletion.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ require (
github.com/hashicorp/golang-lru v0.5.4
github.com/imdario/mergo v0.3.12
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743
github.com/jarcoal/httpmock v1.0.5
github.com/jteeuwen/go-bindata v3.0.7+incompatible
github.com/julienschmidt/httprouter v1.3.0
github.com/knadh/koanf v1.4.0
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,7 @@ github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
github.com/jandelgado/gcov2lcov v1.0.4/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
github.com/jandelgado/gcov2lcov v1.0.5 h1:rkBt40h0CVK4oCb8Dps950gvfd1rYvQ8+cWa346lVU0=
github.com/jandelgado/gcov2lcov v1.0.5/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
github.com/jarcoal/httpmock v1.0.5 h1:cHtVEcTxRSX4J0je7mWPfc9BpDpqzXSJ5HbymZmyHck=
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jcchavezs/porto v0.1.0/go.mod h1:fESH0gzDHiutHRdX2hv27ojnOVFco37hg1W6E9EZF4A=
Expand Down
16 changes: 15 additions & 1 deletion selfservice/strategy/oidc/provider_facebook.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package oidc

import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/url"

"github.com/hashicorp/go-retryablehttp"
Expand Down Expand Up @@ -34,6 +38,15 @@ func NewProviderFacebook(
}
}

func (g *ProviderFacebook) generateAppSecretProof(ctx context.Context, exchange *oauth2.Token) string {
secret := g.config.ClientSecret
data := exchange.AccessToken

h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}

func (g *ProviderFacebook) OAuth2(ctx context.Context) (*oauth2.Config, error) {
p, err := g.provider(ctx)
if err != nil {
Expand All @@ -52,8 +65,9 @@ func (g *ProviderFacebook) Claims(ctx context.Context, exchange *oauth2.Token, q
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}

appSecretProof := g.generateAppSecretProof(ctx, exchange)
client := g.reg.HTTPClient(ctx, httpx.ResilientClientWithClient(o.Client(ctx, exchange)))
u, err := url.Parse("https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender")
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))
if err != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}
Expand Down
89 changes: 89 additions & 0 deletions selfservice/strategy/oidc/provider_facebook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package oidc_test

import (
"context"
"net/http"
"net/url"
"testing"
"time"

"github.com/jarcoal/httpmock"

"github.com/ory/kratos/internal"
"github.com/ory/kratos/selfservice/strategy/oidc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
)

const fakeIDToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjk5OTk5OTk5OTksImF1ZCI6ImFiY2QiLCJpc3MiOiJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYWVuZWFzci9wcml2YXRlLW9pZGMvbWFzdGVyL3Rva2VuIn0.G9v8pJXJrEOgdJ5ecE6sIIcTH_p-RKkBaImfZY5DDVCl7h5GEis1n3GKKYbL_O3fj8Fu-WzI2mquI8S8BOVCQ6wN0XtrqJv22iX_nzeVHc4V_JWV1q7hg2gPpoFFcnF3KKtxZLvDOA8ujsDbAXmoBu0fEBdwCN56xLOOKQDzULyfijuAa8hrCwespZ9HaqcHzD3iHf_Utd4nHqlTM-6upWpKIMkplS_NGcxrfIRIWusZ0wob6ryy8jECD9QeZpdTGUozq-YM64lZfMOZzuLuqichH_PCMKFyB_tOZb6lDIiiSX4Irz7_YF-DP-LmfxgIW4934RqTCeFGGIP64h4xAA"

func TestProviderFacebook_Claims(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

httpmock.RegisterResponder("GET", "https://graph.facebook.com/me",
func(req *http.Request) (*http.Response, error) {
if _, ok := req.URL.Query()["appsecret_proof"]; !ok {
resp, err := httpmock.NewJsonResponse(400, map[string]interface{}{
"error": map[string]interface{}{
"message": "API calls from the server require an appsecret_proof argument",
"type": "GraphMethodException",
"code": 100,
"fbtrace_id": "Ay8LR3n5BsHm809VYpJ3eDM",
},
})
return resp, err
}
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
"id": "123456789012345",
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"birthday": "01/01/1990",
})
return resp, err
},
)

httpmock.RegisterResponder("GET", "https://www.facebook.com/.well-known/openid-configuration",
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
"issuer": "https://www.facebook.com",
})
return resp, err
},
)

_, reg := internal.NewFastRegistryWithMocks(t)
c := &oidc.Configuration{
ID: "facebook",
Provider: "facebook",
ClientID: "abcd",
ClientSecret: "secret",
Mapper: "file://./stub/oidc.facebook.jsonnet",
Scope: []string{"email"},
}
facebook := oidc.NewProviderFacebook(c, reg)

actual, err := facebook.Claims(
context.Background(),
(&oauth2.Token{AccessToken: "foo", Expiry: time.Now().Add(time.Hour)}).WithExtra(map[string]interface{}{"id_token": fakeIDToken}),
url.Values{},
)
require.NoError(t, err)

assert.Equal(t, &oidc.Claims{
Issuer: "https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender&appsecret_proof=773ba44693c7553d6ee20f61ea5d2757a9a4f4a44d2841ae4e95b52e4cd62db4",
Subject: "123456789012345",
Name: "John Doe",
GivenName: "John",
FamilyName: "Doe",
Nickname: "John Doe",
PreferredUsername: "John Doe",
Email: "john.doe@example.com",
EmailVerified: true,
Birthdate: "01/01/1990",
}, actual)
}
13 changes: 13 additions & 0 deletions selfservice/strategy/oidc/stub/oidc.facebook.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
local claims = std.extVar('claims');

if std.length(claims.sub) == 0 then
error 'claim sub not set'
else
{
identity: {
traits: {
subject: claims.sub,
[if "email" in claims then "email" else null]: claims.email,
},
},
}

0 comments on commit 1ba7c66

Please sign in to comment.