Skip to content

Commit 45da11e

Browse files
feat: support different jwt scope claim strategies (#3531)
1 parent 6c298b2 commit 45da11e

File tree

8 files changed

+62
-2
lines changed

8 files changed

+62
-2
lines changed

.schema/config.schema.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,18 @@
781781
"description": "Defines access token type. jwt is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens",
782782
"enum": ["opaque", "jwt"],
783783
"default": "opaque"
784+
},
785+
"jwt": {
786+
"type": "object",
787+
"additionalProperties": false,
788+
"properties": {
789+
"scope_claim": {
790+
"type": "string",
791+
"description": "Defines how the scope claim is represented within a JWT access token",
792+
"enum": ["list", "string", "both"],
793+
"default": "list"
794+
}
795+
}
784796
}
785797
}
786798
},

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export PATH := .bin:${PATH}
55
export PWD := $(shell pwd)
66
export IMAGE_TAG := $(if $(IMAGE_TAG),$(IMAGE_TAG),latest)
77

8-
GOLANGCI_LINT_VERSION = 1.53.2
8+
GOLANGCI_LINT_VERSION = 1.53.3
99

1010
GO_DEPENDENCIES = github.com/ory/go-acc \
1111
github.com/golang/mock/mockgen \

cmd/cmd_list_clients.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func NewListClientsCmd() *cobra.Command {
3131
return err
3232
}
3333

34+
// nolint:bodyclose
3435
list, resp, err := m.OAuth2Api.ListOAuth2Clients(cmd.Context()).PageSize(int64(pageSize)).PageToken(pageToken).Execute()
3536
if err != nil {
3637
return cmdx.PrintOpenAPIError(cmd, err)

driver/config/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const (
7979
KeyAdminURL = "urls.self.admin"
8080
KeyIssuerURL = "urls.self.issuer"
8181
KeyAccessTokenStrategy = "strategies.access_token"
82+
KeyJWTScopeClaimStrategy = "strategies.jwt.scope_claim"
8283
KeyDBIgnoreUnknownTableColumns = "db.ignore_unknown_table_columns"
8384
KeySubjectIdentifierAlgorithmSalt = "oidc.subject_identifiers.pairwise.salt"
8485
KeyPublicAllowDynamicRegistration = "oidc.dynamic_client_registration.enabled"

driver/config/provider_fosite.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/pkg/errors"
1212

1313
"github.com/ory/fosite"
14+
"github.com/ory/fosite/token/jwt"
1415
"github.com/ory/hydra/v2/x"
1516
)
1617

@@ -89,6 +90,21 @@ func (p *DefaultProvider) GetScopeStrategy(ctx context.Context) fosite.ScopeStra
8990
return fosite.ExactScopeStrategy
9091
}
9192

93+
var _ fosite.JWTScopeFieldProvider = (*DefaultProvider)(nil)
94+
95+
func (p *DefaultProvider) GetJWTScopeField(ctx context.Context) jwt.JWTScopeFieldEnum {
96+
switch strings.ToLower(p.getProvider(ctx).String(KeyJWTScopeClaimStrategy)) {
97+
case "string":
98+
return jwt.JWTScopeFieldString
99+
case "both":
100+
return jwt.JWTScopeFieldBoth
101+
case "list":
102+
return jwt.JWTScopeFieldList
103+
default:
104+
return jwt.JWTScopeFieldUnset
105+
}
106+
}
107+
92108
func (p *DefaultProvider) GetUseLegacyErrorFormat(context.Context) bool {
93109
return false
94110
}

driver/config/provider_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"testing"
1515
"time"
1616

17+
"github.com/ory/fosite/token/jwt"
1718
"github.com/ory/x/configx"
1819
"github.com/ory/x/otelx"
1920

@@ -305,6 +306,7 @@ func TestViperProviderValidates(t *testing.T) {
305306
assert.False(t, c.GetScopeStrategy(ctx)([]string{"openid.*"}, "openid.email"), "should us fosite.ExactScopeStrategy")
306307
assert.Equal(t, AccessTokenDefaultStrategy, c.AccessTokenStrategy(ctx))
307308
assert.Equal(t, false, c.GrantAllClientCredentialsScopesPerDefault(ctx))
309+
assert.Equal(t, jwt.JWTScopeFieldList, c.GetJWTScopeField(ctx))
308310

309311
// ttl
310312
assert.Equal(t, 2*time.Hour, c.ConsentRequestMaxAge(ctx))
@@ -464,3 +466,19 @@ func TestJWTBearer(t *testing.T) {
464466
assert.Equal(t, true, p2.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
465467
assert.Equal(t, true, p2.GetGrantTypeJWTBearerIDOptional(ctx))
466468
}
469+
470+
func TestJWTScopeClaimStrategy(t *testing.T) {
471+
l := logrusx.New("", "")
472+
l.Logrus().SetOutput(io.Discard)
473+
p := MustNew(context.Background(), l)
474+
475+
ctx := context.Background()
476+
477+
assert.Equal(t, jwt.JWTScopeFieldList, p.GetJWTScopeField(ctx))
478+
p.MustSet(ctx, KeyJWTScopeClaimStrategy, "list")
479+
assert.Equal(t, jwt.JWTScopeFieldList, p.GetJWTScopeField(ctx))
480+
p.MustSet(ctx, KeyJWTScopeClaimStrategy, "string")
481+
assert.Equal(t, jwt.JWTScopeFieldString, p.GetJWTScopeField(ctx))
482+
p.MustSet(ctx, KeyJWTScopeClaimStrategy, "both")
483+
assert.Equal(t, jwt.JWTScopeFieldBoth, p.GetJWTScopeField(ctx))
484+
}

fositex/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func (c *Config) GetAccessTokenIssuer(ctx context.Context) string {
189189
}
190190

191191
func (c *Config) GetJWTScopeField(ctx context.Context) jwt.JWTScopeFieldEnum {
192-
return jwt.JWTScopeFieldList
192+
return c.deps.Config().GetJWTScopeField(ctx)
193193
}
194194

195195
func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) *template.Template {

spec/config.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,18 @@
781781
"description": "Defines access token type. jwt is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens",
782782
"enum": ["opaque", "jwt"],
783783
"default": "opaque"
784+
},
785+
"jwt": {
786+
"type": "object",
787+
"additionalProperties": false,
788+
"properties": {
789+
"scope_claim": {
790+
"type": "string",
791+
"description": "Defines how the scope claim is represented within a JWT access token",
792+
"enum": ["list", "string", "both"],
793+
"default": "list"
794+
}
795+
}
784796
}
785797
}
786798
},

0 commit comments

Comments
 (0)