Skip to content

Commit

Permalink
feat: oidc provider claims config option (#753)
Browse files Browse the repository at this point in the history
Closes #735

Co-authored-by: zepatrik <patrik@ory.sh>
  • Loading branch information
NickUfer and zepatrik committed Oct 13, 2020
1 parent 6ccffa8 commit bf94a40
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 10 deletions.
79 changes: 79 additions & 0 deletions .schema/config.schema.json
Expand Up @@ -56,6 +56,82 @@
"hook"
]
},
"OIDCClaims": {
"title": "OpenID Connect claims",
"description": "The OpenID Connect claims and optionally their properties which should be included in the id_token or returned from the UserInfo Endpoint.",
"type": "object",
"examples": [
{
"id_token": {
"email": null,
"email_verified": null
}
},
{
"userinfo": {
"given_name": {
"essential": true
},
"nickname": null,
"email": {
"essential": true
},
"email_verified": {
"essential": true
},
"picture": null,
"http://example.info/claims/groups": null
},
"id_token": {
"auth_time": {
"essential": true
},
"acr": {
"values": [
"urn:mace:incommon:iap:silver"
]
}
}
}
],
"patternProperties": {
"^userinfo$|^id_token$": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
".*": {
"oneOf": [
{
"const": null,
"description": "Indicates that this Claim is being requested in the default manner."
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"essential": {
"description": "Indicates whether the Claim being requested is an Essential Claim.",
"type": "boolean"
},
"value": {
"description": "Requests that the Claim be returned with a particular value.",
"$comment": "There seem to be no constrains on value"
},
"values": {
"description": "Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.",
"type": "array",
"items": {
"$comment": "There seem to be no constrains on individual items"
}
}
}
}
]
}
}
}
}
},
"selfServiceOIDCProvider": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -138,6 +214,9 @@
"8eaef023-2b34-4da1-9baa-8bc8c9d6a490",
"contoso.onmicrosoft.com"
]
},
"requested_claims": {
"$ref": "#/definitions/OIDCClaims"
}
},
"additionalProperties": false,
Expand Down
23 changes: 23 additions & 0 deletions docs/docs/concepts/credentials/openid-connect-oidc-oauth2.mdx
Expand Up @@ -85,6 +85,29 @@ selfservice:
# Should only be used when the OAuth2 / OpenID Connect server is not supporting OpenID Connect Discovery and when
# `provider` is set to `generic`.
token_url: http://openid-connect-provider/oauth2/token

# requested_claims json object that specifies claims and optionally their properties which should
# be included in the id_token or returned from the UserInfo Endpoint.
#
# More information: https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
requested_claims:
userinfo:
given_name:
essential: true
nickname: null
email:
essential: true
email_verified:
essential: true
picture: null
http://example/info/claims/groups: null
id_token:
auth_time:
essential: true
acr:
values: ['urn:mace:incommon:iap:silver']
sub:
value: 248289761001
```

:::info
Expand Down
7 changes: 7 additions & 0 deletions selfservice/strategy/oidc/provider_config.go
@@ -1,6 +1,7 @@
package oidc

import (
"encoding/json"
"net/url"
"strings"

Expand Down Expand Up @@ -53,6 +54,12 @@ type Configuration struct {
//
// It can be either a URL (file://, http(s)://, base64://) or an inline JSONNet code snippet.
Mapper string `json:"mapper_url"`

// RequestedClaims string encoded json object that specifies claims and optionally their properties which should be
// included in the id_token or returned from the UserInfo Endpoint.
//
// More information: https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
RequestedClaims json.RawMessage `json:"requested_claims"`
}

func (p Configuration) Redir(public *url.URL) string {
Expand Down
12 changes: 8 additions & 4 deletions selfservice/strategy/oidc/provider_generic_oidc.go
Expand Up @@ -73,12 +73,16 @@ func (g *ProviderGenericOIDC) OAuth2(ctx context.Context) (*oauth2.Config, error
}

func (g *ProviderGenericOIDC) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {
var options []oauth2.AuthCodeOption

if isForced(r) {
return []oauth2.AuthCodeOption{
oauth2.SetAuthURLParam("prompt", "login"),
}
options = append(options, oauth2.SetAuthURLParam("prompt", "login"))
}
return []oauth2.AuthCodeOption{}
if len(g.config.RequestedClaims) != 0 {
options = append(options, oauth2.SetAuthURLParam("claims", string(g.config.RequestedClaims)))
}

return options
}

func (g *ProviderGenericOIDC) verifyAndDecodeClaimsWithProvider(ctx context.Context, provider *gooidc.Provider, raw string) (*Claims, error) {
Expand Down
35 changes: 29 additions & 6 deletions selfservice/strategy/oidc/provider_generic_test.go
Expand Up @@ -2,6 +2,7 @@ package oidc

import (
"context"
"encoding/json"
"net/url"
"testing"

Expand All @@ -12,16 +13,31 @@ import (
"github.com/ory/kratos/x"
)

func makeOIDCClaims() json.RawMessage {
claims, _ := json.Marshal(map[string]interface{}{
"id_token": map[string]interface{}{
"email": map[string]bool{
"essential": true,
},
"email_verified": map[string]bool{
"essential": true,
},
},
})
return claims
}

func makeAuthCodeURL(t *testing.T, r *login.Flow) string {
public, err := url.Parse("https://ory.sh")
require.NoError(t, err)
p := NewProviderGenericOIDC(&Configuration{
Provider: "generic",
ID: "valid",
ClientID: "client",
ClientSecret: "secret",
IssuerURL: "https://accounts.google.com",
Mapper: "file://./stub/hydra.schema.json",
Provider: "generic",
ID: "valid",
ClientID: "client",
ClientSecret: "secret",
IssuerURL: "https://accounts.google.com",
Mapper: "file://./stub/hydra.schema.json",
RequestedClaims: makeOIDCClaims(),
}, public)
c, err := p.OAuth2(context.TODO())
require.NoError(t, err)
Expand All @@ -43,4 +59,11 @@ func TestProviderGenericOIDC_AddAuthCodeURLOptions(t *testing.T) {
}
assert.NotContains(t, makeAuthCodeURL(t, r), "prompt=login")
})

t.Run("case=expect requested claims to be set", func(t *testing.T) {
r := &login.Flow{
ID: x.NewUUID(),
}
assert.Contains(t, makeAuthCodeURL(t, r), "claims="+url.QueryEscape(string(makeOIDCClaims())))
})
}
@@ -0,0 +1,18 @@
userinfo:
given_name:
essential: true
someInvalidProperty: 'invalid' # Invalid property
nickname: null
email:
essential: InvalidValueForEssential
email_verified:
essentials: true # key typo
picture: null
http://example/info/claims/groups: null
id_token:
auth_time:
essential: true
acr:
values: ['urn:mace:incommon:iap:silver']
sub:
value: '248289761001'
@@ -0,0 +1,18 @@
userinfo:
given_name:
essential: true
nickname: null
email:
essential: true
email_verified:
essential: true
picture: null
http://example/info/claims/groups: null
id_token:
auth_time:
essential: true
acr:
values: ['urn:mace:incommon:iap:silver']
sub:
essential: true
value: 248289761001
Expand Up @@ -9,3 +9,4 @@ mapper_url: https://example.com
scope:
- foo
- bar
requested_claims: "#/definitions/OIDCClaims"
Expand Up @@ -10,3 +10,4 @@ scope:
- foo
- bar
tenant: org
requested_claims: "#/definitions/OIDCClaims"

0 comments on commit bf94a40

Please sign in to comment.