OPClientCredentialsGrant

This feature requires V3.1+ of the plugin.

Overview

The plugin has support for the OAuth client_credentials grant type. The client issues a request directly to the OP’s token endpoint to request an access token for use with one or more resource servers (or “audiences”). This is sometimes referred to as “two-legged” OAuth when the resource server is also the token issuer, but is three-legged in this scenario because the token issuer is a separate authorization server. Both opaque and JWT-format access tokens are supported on a per-relying-party basis. When the JWT format is used, the token may include arbitrary claims derived from attributes resolved about the client.

As of V3.2, this plugin also supports the general approach outlined here for the standard OIDC code and implicit grant types, provided a resource server is identified in the original request. However, the plugin does not yet support pure OAuth requests without the “openid” scope included for those grant types.

While it is generally presumed in this use case that the clients are servers rather than end-users, the implementation does not actually constrain the identity of the client other than with respect to the configuration.

The implementation is intended to conform to a number of relevant RFCs:

These specifications lack significant interoperability for the handling of scope, audience, and sub claims, so this documentation will describe the particular decisions made in this implementation.

Enabling the Grant Type

By default, the OP only supports the grant types needed for the OIDC functionality (authorization_code and refresh). This feature relies on a third type, client_credentials. You will need to either enable that globally via the idp.oauth2.grantTypes property, or on a specific instance of the OAUTH2.Token profile configuration bean (which also needs to be added to at least one of the profile configuration collections in relying-party.xml in order to support this use case).

Relying Party Configuration

In the implementation of this profile, there are two relying parties involved in the issuance of the token, the client requesting the token and the “primary” audience for the token, a resource server intended to honor it.

While OAuth allows for multiple token audiences, the implementation requires that the first audience derived be considered the “primary” one and is the one treated as the token audience relying party when looking up metadata and determining the active profile configuration. Other derived audience values may be present in the token (e.g., to further constrain the use of a token within a resource server) but are ignored for the purposes of configuration.

At various points throughout the life of a request, settings are applied based on the client and the resource server, depending on which party is impacted by the configuration option. For example, the token lifetime and format are set based on the resource server’s applicable configuration, but control over client authentication types, and even allowing this grant type in the first place depends on the client. There are thus two separate profile configurations involved at the same time, OAUTH2.Token “early” in the flow and OAUTH2.TokenAudience “late”.

Notably, this implementation supports “unverified” clients and/or resource servers; that is, it is optional to require client metadata (and thus some form of static or dynamic registration) for either party. This deployment decision is represented by the choice to enable the OAUTH2.Token and OAUTH2.TokenAudience profile configuration beans within the shibboleth.UnverifiedRelyingParty bean in relying-party.xml. Not doing so will disallow the request if the client or “primary” resource server are not registered. This does not preclude tailoring specific behavior to registered clients or resource servers because when metadata is available, the active profile configuration must come from a different, matching “verified” relying party configuration.

Note that JWT token encryption is impossible in the case of unregistered resource servers, and thus the OAUTH2.TokenAudience’s encryptionOptional profile configuration setting has to be enabled or failure will result. Encryption is required by default (opposite the OIDC use case default) due to the possibility of claims exposure.

Token Content and Policies

Format

The access token format is determined by the OAUTH2.TokenAudience’s accessTokenType profile configuration setting. Absent/default/empty implies opaque tokens. A value of “JWT” switches the format to signed, and possibly encrypted, JWT. No other values are currently defined.

Scope

The determination of the access token “scope” claim relies on marrying together two inputs:

  • the (optional) scope requested by the client in its token request message

  • the results of applying a function to obtain the “allowed” scope for the request, by default the registered scope in the client’s metadata (if any)

Thus, by default the client may request any scope that is a subset of its registered metadata. The second input is implemented by a built-in bean named shibboleth.oidc.DefaultAllowedScopeStrategy. This bean also supports a default value returned in the event that no client metadata exists (i.e., unregistered clients), which is controlled by the idp.oauth2.defaultAllowedScope property.

If desired, you may define a bean of type Function<ProfileRequestContext,Scope> called shibboleth.oidc.AllowedScopeStrategy to produce an alternative result for the operation. The default bean can be used as a means of including the “default” value in the custom computation.

The behavior of this implementation is such that clients MUST request a scope if they wish the token to include one, otherwise it will be absent.

Audience

The determination of the access token “aud” claim relies on marrying together two inputs:

  • the (optional) resource value(s) requested by the client in its token request message

  • the results of applying a function to obtain the “allowed” audience for the request, by default the registered audience value(s) in the client’s metadata (if any)

There is no standard metadata representation for allowed audience, so this is an extension. In the case of JSON metadata, a claim called “audience” is used, while in SAML format, the <saml:Audience> element is used.

Thus, by default the client may request any audience that is a subset of its registered metadata. The second input is implemented by a built-in bean named shibboleth.oidc.DefaultAllowedAudienceStrategy. This bean also supports a default value returned in the event that no client metadata exists (i.e., unregistered clients), which is controlled by the idp.oauth2.defaultAllowedAudience property.

If desired, you may define a bean of type Function<ProfileRequestContext,List<String>> called shibboleth.oidc.AllowedAudienceStrategy to produce an alternative result for the operation. The default bean can be used as a means of including the “default” value in the custom computation.

The values derived are ordered, and this order is maintained, such that the first valid audience derived is the “primary” value and is treated as the relevant identity for subsequent behavior of the profile. As such, this value SHOULD be a logical resource server identity in most deployments rather than a particular resource server location that may be subject to change.

The OP will not issue a token without an “aud” claim, so if no valid/allowed audience can be derived, the request will be denied.

Sub and Attribute-Based Claims

In the case of JWT format access tokens, the “sub” claim is required, and additional claims about the subject MAY be included at the deployer’s option.

Because this grant type is oriented around services requesting tokens, there are no deliberate/unusual strategies designed to “mask” the client identity. However, there is a deliberate separation between the client_id making the request and the value of the “sub” claim, because the token also carries the “client_id” claim explicitly as well. The “sub” claim will be populated with the canonical principal name of the client after undergoing the authentication process. As a result, there is a way for deployers to indirect the value if desired by configuring alternative Subject Canonicalization approaches.

In addition, the flow will by default resolve attributes against the identity of the client (or more precisely the canonical principal name of the client). Obviously this is optional, but if you do attach directory information to the identities of client services, you may choose to resolve and release that data for inclusion in the token. It’s even plausible to store such data in the client’s metadata and extract it via a scripted data connector.

The custom claims included in the token must be both released by the attribute filtering step, and support encoding into JSON claims, as per ordinary OIDC usage. One special consideration for filtering is that both the identity of the client and the identity of the resource server are available as input to policy, as is the metadata for both parties if it exists. The client is treated as the “Requester”, for consistency with the OIDC use case, though pragmatically the client may not even be able to see the claims and is not allowed to parse the token. The primary audience/resource server is represented in the process as a “ProxiedRequester”, for which there are corresponding policy rules.

Signing and Encryption

Opaque access tokens (the default) are internally protected by the IdP’s secret key.

JWT format tokens are digitally signed, per the RFC, to allow the resource server to verify them. Some control over this process is covered by the https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376879249 topic. In addition, if metadata exists for the primary audience, and encryption algorithms and a public key are available, the token is also encrypted automatically. The encryptionOptional profile configuration setting exists, defaulting to false for safety, to allow tokens to be issued without encryption if the OP cannot locate the information required. A global property also exists to enable this if desired.

Note that encryption is only possible to a single resource server (audience) for a given token, due to RFC-imposed limitations on the JOSE serialization allowed for the access token.