Skip to content

Commit

Permalink
feat: support different jwt scope claim strategies (#3531)
Browse files Browse the repository at this point in the history
  • Loading branch information
brett-patterson authored Jul 5, 2023
1 parent 6c298b2 commit 45da11e
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 2 deletions.
12 changes: 12 additions & 0 deletions .schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,18 @@
"description": "Defines access token type. jwt is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens",
"enum": ["opaque", "jwt"],
"default": "opaque"
},
"jwt": {
"type": "object",
"additionalProperties": false,
"properties": {
"scope_claim": {
"type": "string",
"description": "Defines how the scope claim is represented within a JWT access token",
"enum": ["list", "string", "both"],
"default": "list"
}
}
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export PATH := .bin:${PATH}
export PWD := $(shell pwd)
export IMAGE_TAG := $(if $(IMAGE_TAG),$(IMAGE_TAG),latest)

GOLANGCI_LINT_VERSION = 1.53.2
GOLANGCI_LINT_VERSION = 1.53.3

GO_DEPENDENCIES = github.com/ory/go-acc \
github.com/golang/mock/mockgen \
Expand Down
1 change: 1 addition & 0 deletions cmd/cmd_list_clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func NewListClientsCmd() *cobra.Command {
return err
}

// nolint:bodyclose
list, resp, err := m.OAuth2Api.ListOAuth2Clients(cmd.Context()).PageSize(int64(pageSize)).PageToken(pageToken).Execute()
if err != nil {
return cmdx.PrintOpenAPIError(cmd, err)
Expand Down
1 change: 1 addition & 0 deletions driver/config/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const (
KeyAdminURL = "urls.self.admin"
KeyIssuerURL = "urls.self.issuer"
KeyAccessTokenStrategy = "strategies.access_token"
KeyJWTScopeClaimStrategy = "strategies.jwt.scope_claim"
KeyDBIgnoreUnknownTableColumns = "db.ignore_unknown_table_columns"
KeySubjectIdentifierAlgorithmSalt = "oidc.subject_identifiers.pairwise.salt"
KeyPublicAllowDynamicRegistration = "oidc.dynamic_client_registration.enabled"
Expand Down
16 changes: 16 additions & 0 deletions driver/config/provider_fosite.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"

"github.com/ory/fosite"
"github.com/ory/fosite/token/jwt"
"github.com/ory/hydra/v2/x"
)

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

var _ fosite.JWTScopeFieldProvider = (*DefaultProvider)(nil)

func (p *DefaultProvider) GetJWTScopeField(ctx context.Context) jwt.JWTScopeFieldEnum {
switch strings.ToLower(p.getProvider(ctx).String(KeyJWTScopeClaimStrategy)) {
case "string":
return jwt.JWTScopeFieldString
case "both":
return jwt.JWTScopeFieldBoth
case "list":
return jwt.JWTScopeFieldList
default:
return jwt.JWTScopeFieldUnset
}
}

func (p *DefaultProvider) GetUseLegacyErrorFormat(context.Context) bool {
return false
}
18 changes: 18 additions & 0 deletions driver/config/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"testing"
"time"

"github.com/ory/fosite/token/jwt"
"github.com/ory/x/configx"
"github.com/ory/x/otelx"

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

// ttl
assert.Equal(t, 2*time.Hour, c.ConsentRequestMaxAge(ctx))
Expand Down Expand Up @@ -464,3 +466,19 @@ func TestJWTBearer(t *testing.T) {
assert.Equal(t, true, p2.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
assert.Equal(t, true, p2.GetGrantTypeJWTBearerIDOptional(ctx))
}

func TestJWTScopeClaimStrategy(t *testing.T) {
l := logrusx.New("", "")
l.Logrus().SetOutput(io.Discard)
p := MustNew(context.Background(), l)

ctx := context.Background()

assert.Equal(t, jwt.JWTScopeFieldList, p.GetJWTScopeField(ctx))
p.MustSet(ctx, KeyJWTScopeClaimStrategy, "list")
assert.Equal(t, jwt.JWTScopeFieldList, p.GetJWTScopeField(ctx))
p.MustSet(ctx, KeyJWTScopeClaimStrategy, "string")
assert.Equal(t, jwt.JWTScopeFieldString, p.GetJWTScopeField(ctx))
p.MustSet(ctx, KeyJWTScopeClaimStrategy, "both")
assert.Equal(t, jwt.JWTScopeFieldBoth, p.GetJWTScopeField(ctx))
}
2 changes: 1 addition & 1 deletion fositex/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (c *Config) GetAccessTokenIssuer(ctx context.Context) string {
}

func (c *Config) GetJWTScopeField(ctx context.Context) jwt.JWTScopeFieldEnum {
return jwt.JWTScopeFieldList
return c.deps.Config().GetJWTScopeField(ctx)
}

func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) *template.Template {
Expand Down
12 changes: 12 additions & 0 deletions spec/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,18 @@
"description": "Defines access token type. jwt is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens",
"enum": ["opaque", "jwt"],
"default": "opaque"
},
"jwt": {
"type": "object",
"additionalProperties": false,
"properties": {
"scope_claim": {
"type": "string",
"description": "Defines how the scope claim is represented within a JWT access token",
"enum": ["list", "string", "both"],
"default": "list"
}
}
}
}
},
Expand Down

0 comments on commit 45da11e

Please sign in to comment.