Skip to content

Commit 1da0cf6

Browse files
authored
feat: parse all id token claims into raw_claims (#2765)
All ID Token claims resulting from the Social Sign In flow are now available in `raw_claims` and can be used in the Social Sign In JsonNet Mapper. Closes #2528
1 parent 5af2c0a commit 1da0cf6

File tree

6 files changed

+66
-25
lines changed

6 files changed

+66
-25
lines changed

selfservice/strategy/oidc/provider.go

+24-23
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,28 @@ type TokenExchanger interface {
2222

2323
// ConvertibleBoolean is used as Apple casually sends the email_verified field as a string.
2424
type Claims struct {
25-
Issuer string `json:"iss,omitempty"`
26-
Subject string `json:"sub,omitempty"`
27-
Name string `json:"name,omitempty"`
28-
GivenName string `json:"given_name,omitempty"`
29-
FamilyName string `json:"family_name,omitempty"`
30-
LastName string `json:"last_name,omitempty"`
31-
MiddleName string `json:"middle_name,omitempty"`
32-
Nickname string `json:"nickname,omitempty"`
33-
PreferredUsername string `json:"preferred_username,omitempty"`
34-
Profile string `json:"profile,omitempty"`
35-
Picture string `json:"picture,omitempty"`
36-
Website string `json:"website,omitempty"`
37-
Email string `json:"email,omitempty"`
38-
EmailVerified x.ConvertibleBoolean `json:"email_verified,omitempty"`
39-
Gender string `json:"gender,omitempty"`
40-
Birthdate string `json:"birthdate,omitempty"`
41-
Zoneinfo string `json:"zoneinfo,omitempty"`
42-
Locale string `json:"locale,omitempty"`
43-
PhoneNumber string `json:"phone_number,omitempty"`
44-
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"`
45-
UpdatedAt int64 `json:"updated_at,omitempty"`
46-
HD string `json:"hd,omitempty"`
47-
Team string `json:"team,omitempty"`
25+
Issuer string `json:"iss,omitempty"`
26+
Subject string `json:"sub,omitempty"`
27+
Name string `json:"name,omitempty"`
28+
GivenName string `json:"given_name,omitempty"`
29+
FamilyName string `json:"family_name,omitempty"`
30+
LastName string `json:"last_name,omitempty"`
31+
MiddleName string `json:"middle_name,omitempty"`
32+
Nickname string `json:"nickname,omitempty"`
33+
PreferredUsername string `json:"preferred_username,omitempty"`
34+
Profile string `json:"profile,omitempty"`
35+
Picture string `json:"picture,omitempty"`
36+
Website string `json:"website,omitempty"`
37+
Email string `json:"email,omitempty"`
38+
EmailVerified x.ConvertibleBoolean `json:"email_verified,omitempty"`
39+
Gender string `json:"gender,omitempty"`
40+
Birthdate string `json:"birthdate,omitempty"`
41+
Zoneinfo string `json:"zoneinfo,omitempty"`
42+
Locale string `json:"locale,omitempty"`
43+
PhoneNumber string `json:"phone_number,omitempty"`
44+
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"`
45+
UpdatedAt int64 `json:"updated_at,omitempty"`
46+
HD string `json:"hd,omitempty"`
47+
Team string `json:"team,omitempty"`
48+
RawClaims map[string]interface{} `json:"raw_claims,omitempty"`
4849
}

selfservice/strategy/oidc/provider_generic_oidc.go

+6
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ func (g *ProviderGenericOIDC) verifyAndDecodeClaimsWithProvider(ctx context.Cont
9595
return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("%s", err))
9696
}
9797

98+
var rawClaims map[string]interface{}
99+
if err := token.Claims(&rawClaims); err != nil {
100+
return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("%s", err))
101+
}
102+
claims.RawClaims = rawClaims
103+
98104
return &claims, nil
99105
}
100106

selfservice/strategy/oidc/strategy_helper_test.go

+27-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
type idTokenClaims struct {
3838
traits struct {
3939
website string
40+
groups []string
4041
}
4142
metadataPublic struct {
4243
picture string
@@ -46,6 +47,29 @@ type idTokenClaims struct {
4647
}
4748
}
4849

50+
func (token *idTokenClaims) MarshalJSON() ([]byte, error) {
51+
return json.Marshal(struct {
52+
IdToken struct {
53+
Website string `json:"website,omitempty"`
54+
Groups []string `json:"groups,omitempty"`
55+
Picture string `json:"picture,omitempty"`
56+
PhoneNumber string `json:"phone_number,omitempty"`
57+
} `json:"id_token"`
58+
}{
59+
IdToken: struct {
60+
Website string `json:"website,omitempty"`
61+
Groups []string `json:"groups,omitempty"`
62+
Picture string `json:"picture,omitempty"`
63+
PhoneNumber string `json:"phone_number,omitempty"`
64+
}{
65+
Website: token.traits.website,
66+
Groups: token.traits.groups,
67+
Picture: token.metadataPublic.picture,
68+
PhoneNumber: token.metadataAdmin.phoneNumber,
69+
},
70+
})
71+
}
72+
4973
func createClient(t *testing.T, remote string, redir, id string) {
5074
require.NoError(t, resilience.Retry(logrusx.New("", ""), time.Second*10, time.Minute*2, func() error {
5175
if req, err := http.NewRequest("DELETE", remote+"/clients/"+id, nil); err != nil {
@@ -137,8 +161,9 @@ func newHydraIntegration(t *testing.T, remote *string, subject *string, claims *
137161
require.NotEmpty(t, challenge)
138162

139163
var b bytes.Buffer
140-
var msg = `{"id_token":{"website":"` + claims.traits.website + `","picture":"` + *&claims.metadataPublic.picture + `","phone_number":"` + *&claims.metadataAdmin.phoneNumber + `"}}`
141-
require.NoError(t, json.NewEncoder(&b).Encode(&p{GrantScope: *scope, Session: json.RawMessage(msg)}))
164+
msg, err := json.Marshal(claims)
165+
require.NoError(t, err)
166+
require.NoError(t, json.NewEncoder(&b).Encode(&p{GrantScope: *scope, Session: msg}))
142167
href := urlx.MustJoin(*remote, "/oauth2/auth/requests/consent/accept") + "?consent_challenge=" + challenge
143168
do(w, r, href, &b)
144169
})

selfservice/strategy/oidc/strategy_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ func TestStrategy(t *testing.T) {
452452
scope = []string{"openid"}
453453
claims = idTokenClaims{}
454454
claims.traits.website = "https://www.ory.sh/kratos"
455+
claims.traits.groups = []string{"group1", "group2"}
455456
claims.metadataPublic.picture = "picture.png"
456457
claims.metadataAdmin.phoneNumber = "911"
457458

@@ -474,6 +475,7 @@ func TestStrategy(t *testing.T) {
474475
ai(t, res, body)
475476
assert.Equal(t, "https://www.ory.sh/kratos", gjson.GetBytes(body, "identity.traits.website").String(), "%s", body)
476477
assert.Equal(t, "valid-name", gjson.GetBytes(body, "identity.traits.name").String(), "%s", body)
478+
assert.Equal(t, "[\"group1\",\"group2\"]", gjson.GetBytes(body, "identity.traits.groups").String(), "%s", body)
477479
})
478480
})
479481

selfservice/strategy/oidc/stub/oidc.hydra.jsonnet

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ else
88
traits: {
99
subject: claims.sub,
1010
[if "website" in claims then "website" else null]: claims.website,
11+
[if "groups" in claims.raw_claims then "groups" else null]: claims.raw_claims.groups,
1112
},
1213
metadata_public: {
1314
[if "picture" in claims then "picture" else null]: claims.picture,

selfservice/strategy/oidc/stub/registration.schema.json

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
"website": {
2626
"type": "string",
2727
"format": "uri"
28+
},
29+
"groups": {
30+
"type": "array",
31+
"items": {
32+
"type": "string"
33+
}
2834
}
2935
},
3036
"required": [

0 commit comments

Comments
 (0)