Skip to content

Commit

Permalink
feat(oidc): jwt response for introspection (#5840)
Browse files Browse the repository at this point in the history
This implements the standard for JWT encoded and signed responses from the introspection endpoint. This has been implemented as per the IETF draft https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-introspection-response and as it is a draft (it is also an expired draft) so it should be noted that this implementation may be removed or changed without any regard for breaking changes. While this factor points in the direction of this never receiving ratification the IANA has accepted registration of the metadata parameters for this specification which points to the fact that it probably will.

Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
  • Loading branch information
james-d-elliott committed Aug 27, 2023
1 parent 8d196b7 commit 34b7a47
Show file tree
Hide file tree
Showing 31 changed files with 2,059 additions and 225 deletions.
50 changes: 50 additions & 0 deletions api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1341,10 +1341,22 @@ paths:
- $ref: '#/components/schemas/openid.spec.AccessResponse'
"401":
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
"403":
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
"500":
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
security:
- openid: []
/api/oidc/revocation:
Expand All @@ -1368,10 +1380,22 @@ paths:
description: OK
"401":
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
"403":
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
"500":
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
security:
- openid: []
/api/oidc/introspection:
Expand All @@ -1397,12 +1421,31 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.IntrospectionResponse'
application/token-introspection+jwt:
schema:
description: |
The RFC7519 encoded JWT with the nested 'token_introspection' claim which contains the same structure
as the openid.spec.IntrospectionResponse described in this OpenAPI 3.0 specification.
type: string
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20vIiwiYXVkIjoiaHR0cHM6Ly9hcHAuZXhhbXBsZS5jb20vcmVzb3VyY2UiLCJpYXQiOjE1MTQ3OTc4OTIsInRva2VuX2ludHJvc3BlY3Rpb24iOnsiYWN0aXZlIjp0cnVlLCJleHAiOjE1MTQ3OTc5NDIsImNsaWVudF9pZCI6ImV4YW1wbGUiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIiwiaWF0IjoxNTE0Nzk3ODIyLCJzdWIiOiI5Y2JhMzJhMC02M2EyLTQyZDUtODNlYi1kZmM3NTc4OGEyMjIiLCJhdWQiOiJodHRwczovL2FwcC5leGFtcGxlLmNvbS9yZXNvdXJjZSIsInVzZXJuYW1lIjoiam9obiJ9fQ.9rN-G3uaj28Geiktfvknl-G6EnZxOGJjpXcemvsllYA'
"401":
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
"403":
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
"500":
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/openid.spec.ErrorResponseGeneric'
security:
- openid: []
/api/oidc/userinfo:
Expand Down Expand Up @@ -3230,6 +3273,7 @@ components:
- 'unauthorized_client'
- 'unsupported_grant_type'
- 'unsupported_response_type'
- 'unsupported_token_type'
- 'request_not_supported'
- 'request_uri_not_supported'
- 'registration_not_supported'
Expand All @@ -3254,6 +3298,12 @@ components:
conform to the URI-reference syntax and thus MUST NOT include characters outside the set %x21 / %x23-5B /
%x5D-7E.
type: string
error_hint:
description: An additional hint about the cause of the error.
type: string
error_debug:
description: Additional debug information about the cause of the error.
type: string
state:
description: >
OAuth 2.0 state value. REQUIRED if the Authorization Request included the state parameter. Set to the value
Expand Down
24 changes: 21 additions & 3 deletions config.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1412,15 +1412,33 @@ notifier:
# token_endpoint_auth_signing_alg: 'RS256'

## The signing algorithm which must be used for request objects. A client JWK with a matching algorithm must be
## included if configured.
## available if configured.
# request_object_signing_alg: 'RS256'

## The signing algorithm used for ID Tokens. Am issuer JWK with a matching algorithm must be included.
## The signing algorithm used for ID Tokens. An issuer JWK with a matching algorithm must be available.
## Has no effect if id_token_signing_key_id is configured.
# id_token_signing_alg: 'RS256'

## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
## The signing key id used for ID Tokens. An issuer JWK with a matching key id must be available when
## configured.
# id_token_signing_key_id: ''

## The signing algorithm used for User Info responses. An issuer JWK with a matching algorithm must be
## available. Has no effect if userinfo_signing_key_id is configured.
# userinfo_signing_alg: 'none'

## The signing key id used for User Info responses. An issuer JWK with a matching key id must be available when
## configured.
# userinfo_signing_key_id: ''

## The signing algorithm used for Introspection responses. An issuer JWK with a matching algorithm must be
## available. Has no effect if introspection_signed_response_key_id is configured.
# introspection_signed_response_alg: 'none'

## The signing key id used for Introspection responses. An issuer JWK with a matching key id must be available
## when configured.
# introspection_signed_response_key_id: ''

## Trusted public keys configuration for request object signing for things such as private_key_jwt
# public_keys:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,31 +290,36 @@ relying party supports it.

{{< confkey type="string" default="RS256" required="no" >}}

_**Note:** This value is completely ignored if the [id_token_signing_key_id](#idtokensigningkeyid) is defined._

The algorithm used to sign the ID Tokens in the token responses.

See the response object section of the
[integration guide](../../../integration/openid-connect/introduction.md#response-object) for more information including
the algorithm column for supported values. In addition to the values listed we also support `none` as a value for this
endpoint.
the algorithm column for supported values. This value can not be set to `none`.

The algorithm chosen must have a key configured in the [issuer_private_keys](provider.md#issuerprivatekeys) section to
be considered valid.

This option has no effect if the [id_token_signing_key_id](#idtokensigningkid) is specified as the algorithm is
automatically assumed by the configured key.
See the response object section of the [integration guide](../../../integration/openid-connect/introduction.md#response-object)
for more information including the algorithm column for supported values.

### id_token_signing_key_id

{{< confkey type="string" required="no" >}}

The key id of the JWK used to sign the ID Tokens in the token responses. This option takes precedence over
[id_token_signing_alg](#idtokensigningalg). The value of this must one of those provided or calculated in the
[issuer_private_keys](provider.md#issuerprivatekeys).
_**Note:** This value automatically configures the [id_token_signing_alg](#idtokensigningalg) value with the algorithm
of the specified key._

The key id of the JWK used to sign the ID Tokens in the token responses. The value of this must one of those provided or
calculated in the [issuer_private_keys](provider.md#issuerprivatekeys).

### userinfo_signing_alg

{{< confkey type="string" default="none" required="no" >}}

_**Note:** This value is completely ignored if the [userinfo_signing_key_id](#userinfosigningkeyid) is defined._

The algorithm used to sign the userinfo endpoint responses.

See the response object section of the [integration guide](../../../integration/openid-connect/introduction.md#response-object)
Expand All @@ -324,17 +329,45 @@ support `none` as a value for this endpoint.
The algorithm chosen must have a key configured in the [issuer_private_keys](provider.md#issuerprivatekeys) section to
be considered valid.

This option has no effect if the [userinfo_signing_key_id](#userinfosigningkeyid) is specified as the algorithm is
automatically assumed by the configured key.

### userinfo_signing_key_id

{{< confkey type="string" required="no" >}}

The key id of the JWK used to sign the userinfo endpoint responses in the token responses. This option takes precedence
over [userinfo_signing_alg](#userinfosigningalg). The value of this must one of those provided or calculated in the
_**Note:** This value automatically configures the [userinfo_signing_alg](#userinfosigningalg) value with the algorithm
of the specified key._

The key id of the JWK used to sign the userinfo endpoint responses in the token responses. The value of this must one of
those provided or calculated in the
[issuer_private_keys](provider.md#issuerprivatekeys).

### introspection_signed_response_alg

{{< confkey type="string" default="none" required="no" >}}

_**Note:** This value is completely ignored if the
[introspection_signed_response_key_id](#introspectionsignedresponsekeyid) is defined._

The algorithm used to sign the Introspection response. By default it is set to `none` which results in the response
not being signed and the encoding being JSON.

If configured to any other value the response is a JWT in the `application/token-introspection+jwt` format.

See the response object section of the [integration guide](../../../integration/openid-connect/introduction.md#response-object)
for more information including the algorithm column for supported values.

The algorithm chosen must have a key configured in the [issuer_private_keys](provider.md#issuerprivatekeys) section to
be considered valid.

### introspection_signed_response_key_id

{{< confkey type="string" required="no" >}}

_**Note:** This value automatically configures the [introspection_signed_response_alg](#introspectionsignedresponsealg)
value with the algorithm of the specified key._

The key id of the JWK used to sign the Introspection responses. The value of this must one of those provided or
calculated in the [issuer_private_keys](provider.md#issuerprivatekeys).

### request_object_signing_alg

{{< confkey type="string" default="RSA256" required="no" >}}
Expand Down
1 change: 1 addition & 0 deletions docs/content/en/roadmap/active/openid-connect.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Feature List:
* Policies can be mapped to individual clients and reused
* Match criteria is only subjects as this is the only effective thing that is constant during the life of an
authorization
* [JWT Response for OAuth Token Inspection](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-introspection-response)

### Beta 7

Expand Down
24 changes: 21 additions & 3 deletions internal/configuration/config.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1412,15 +1412,33 @@ notifier:
# token_endpoint_auth_signing_alg: 'RS256'

## The signing algorithm which must be used for request objects. A client JWK with a matching algorithm must be
## included if configured.
## available if configured.
# request_object_signing_alg: 'RS256'

## The signing algorithm used for ID Tokens. Am issuer JWK with a matching algorithm must be included.
## The signing algorithm used for ID Tokens. An issuer JWK with a matching algorithm must be available.
## Has no effect if id_token_signing_key_id is configured.
# id_token_signing_alg: 'RS256'

## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
## The signing key id used for ID Tokens. An issuer JWK with a matching key id must be available when
## configured.
# id_token_signing_key_id: ''

## The signing algorithm used for User Info responses. An issuer JWK with a matching algorithm must be
## available. Has no effect if userinfo_signing_key_id is configured.
# userinfo_signing_alg: 'none'

## The signing key id used for User Info responses. An issuer JWK with a matching key id must be available when
## configured.
# userinfo_signing_key_id: ''

## The signing algorithm used for Introspection responses. An issuer JWK with a matching algorithm must be
## available. Has no effect if introspection_signed_response_key_id is configured.
# introspection_signed_response_alg: 'none'

## The signing key id used for Introspection responses. An issuer JWK with a matching key id must be available
## when configured.
# introspection_signed_response_key_id: ''

## Trusted public keys configuration for request object signing for things such as private_key_jwt
# public_keys:

Expand Down
31 changes: 17 additions & 14 deletions internal/configuration/schema/identity_providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,14 @@ type OpenIDConnectClient struct {

PKCEChallengeMethod string `koanf:"pkce_challenge_method"`

IDTokenSigningAlg string `koanf:"id_token_signing_alg"`
IDTokenSigningKeyID string `koanf:"id_token_signing_key_id"`
UserinfoSigningAlg string `koanf:"userinfo_signing_alg"`
UserinfoSigningKeyID string `koanf:"userinfo_signing_key_id"`
RequestObjectSigningAlg string `koanf:"request_object_signing_alg"`
TokenEndpointAuthSigningAlg string `koanf:"token_endpoint_auth_signing_alg"`
IDTokenSigningAlg string `koanf:"id_token_signing_alg"`
IDTokenSigningKeyID string `koanf:"id_token_signing_key_id"`
UserinfoSigningAlg string `koanf:"userinfo_signing_alg"`
UserinfoSigningKeyID string `koanf:"userinfo_signing_key_id"`
IntrospectionSignedResponseAlg string `koanf:"introspection_signed_response_alg"`
IntrospectionSignedResponseKeyID string `koanf:"introspection_signed_response_key_id"`
RequestObjectSigningAlg string `koanf:"request_object_signing_alg"`
TokenEndpointAuthSigningAlg string `koanf:"token_endpoint_auth_signing_alg"`

TokenEndpointAuthMethod string `koanf:"token_endpoint_auth_method"`

Expand Down Expand Up @@ -168,12 +170,13 @@ var defaultOIDCClientConsentPreConfiguredDuration = time.Hour * 24 * 7

// DefaultOpenIDConnectClientConfiguration contains defaults for OIDC Clients.
var DefaultOpenIDConnectClientConfiguration = OpenIDConnectClient{
AuthorizationPolicy: policyTwoFactor,
Scopes: []string{"openid", "groups", "profile", "email"},
ResponseTypes: []string{"code"},
ResponseModes: []string{"form_post"},
IDTokenSigningAlg: "RS256",
UserinfoSigningAlg: "none",
ConsentMode: "auto",
ConsentPreConfiguredDuration: &defaultOIDCClientConsentPreConfiguredDuration,
AuthorizationPolicy: policyTwoFactor,
Scopes: []string{"openid", "groups", "profile", "email"},
ResponseTypes: []string{"code"},
ResponseModes: []string{"form_post"},
IDTokenSigningAlg: "RS256",
UserinfoSigningAlg: "none",
IntrospectionSignedResponseAlg: "none",
ConsentMode: "auto",
ConsentPreConfiguredDuration: &defaultOIDCClientConsentPreConfiguredDuration,
}
2 changes: 2 additions & 0 deletions internal/configuration/schema/keys.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion internal/configuration/validator/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ const (
errFmtOIDCClientInvalidSecretNotPlainText = "identity_providers: oidc: clients: client '%s': option 'secret' must be plaintext with option 'token_endpoint_auth_method' with a value of 'client_secret_jwt'"
errFmtOIDCClientPublicInvalidSecret = "identity_providers: oidc: clients: client '%s': option 'secret' is " +
"required to be empty when option 'public' is true"
errFmtOIDCClientPublicInvalidSecretClientAuthMethod = "identity_providers: oidc: clients: client '%s': option 'secret' is " +
"required to be empty when option 'token_endpoint_auth_method' is configured as '%s'"
errFmtOIDCClientRedirectURICantBeParsed = "identity_providers: oidc: clients: client '%s': option 'redirect_uris' has an " +
"invalid value: redirect uri '%s' could not be parsed: %v"
errFmtOIDCClientRedirectURIPublic = "identity_providers: oidc: clients: client '%s': option 'redirect_uris' has the " +
Expand All @@ -217,7 +219,7 @@ const (
errFmtOIDCClientInvalidTokenEndpointAuthSigAlg = "identity_providers: oidc: clients: client '%s': option " +
"'token_endpoint_auth_signing_alg' must be one of %s when option 'token_endpoint_auth_method' is configured to '%s'"
errFmtOIDCClientInvalidTokenEndpointAuthSigAlgReg = "identity_providers: oidc: clients: client '%s': option " +
"'token_endpoint_auth_signing_alg' must be one of registered public key algorithm values %s when option 'token_endpoint_auth_method' is configured to '%s'"
"'token_endpoint_auth_signing_alg' must be one of the registered public key algorithm values %s when option 'token_endpoint_auth_method' is configured to '%s'"
errFmtOIDCClientInvalidTokenEndpointAuthSigAlgMissingPrivateKeyJWT = "identity_providers: oidc: clients: client '%s': option " +
"'token_endpoint_auth_signing_alg' is required when option 'token_endpoint_auth_method' is configured to 'private_key_jwt'"
errFmtOIDCClientInvalidPublicKeysPrivateKeyJWT = "identity_providers: oidc: clients: client '%s': option " +
Expand Down Expand Up @@ -465,6 +467,7 @@ const (
attrOIDCRedirectURIs = "redirect_uris"
attrOIDCTokenAuthMethod = "token_endpoint_auth_method"
attrOIDCUsrSigAlg = "userinfo_signing_alg"
attrOIDCIntrospectionSigAlg = "introspection_signed_response_alg"
attrOIDCUsrSigKID = "userinfo_signing_key_id"
attrOIDCIDTokenSigAlg = "id_token_signing_alg"
attrOIDCIDTokenSigKID = "id_token_signing_key_id"
Expand Down

0 comments on commit 34b7a47

Please sign in to comment.