There are two general ways to register and manage OIDC "clients" (RPs), with metadata and dynamically. Metadata-based registration is analagous to the way CAS and SAML are supported in the IdP, through metadata resolution to files or online sources. It's not necessarily static since there are many ways of obtaining metadata (e.g., the process could be offloaded to some other system entirely but with output compatible with one of the supported formats).
A "native" JSON format is supported, as well as a profile of SAML metadata. The former is obviously more concise, but the latter is advantageous because it supports signing by third parties, and because of the myriad configuration features that rely on the use of SAML metadata, and its extensions, and for consistency with the rest of the supported protocols. There are also more advanced metadata loading options using the SAML format, such as the LocalDynamicMetadataProvider or DynamicHTTPMetadataProvider approaches.
For further information on configuring support for resolving this information, see OPClientResolution.
As of V3.1, not every feature actually requires registration and management of clients. While the support for this with SAML or CAS is not heavily used, the IdP has always allowed for so-called “unverified” or (in very old versions, “anonymous”) relying party configuration, whereby profiles may be enabled for systems that are not found in metadata. This support is currently not universal within the OP feature set, but is expected to expand going forward because of the fact that OIDC and OAuth typically rely on back-channel communication to the OP with authentication methods that don’t necessarily require metadata.
Because of this additional authentication requirement, it may be desirable to operate (within an enterprise at least) with a more default profile configuration that extends to any clients issued credentials by some general management approach, such as service accounts managed within an IDM solution. Issuing service accounts may be more lightweight for many organizations than capturing details for creation of client metadata.
With V3.1, the following features are meant to operate successfully in the absence of client metadata, provided the relevant profiles are enabled for unverified relying party use:
OAuth 2 Client Credentials grant support on the Token endpoint (OAUTH2.Token and OAUTH2.TokenAudience)
OAuth 2 Introspection (OAUTH2.Introspection)
OAuth 2 Revocation (OAUTH2.Revocation)
These profiles all support client authentication, and this can be used in lieu of actually registering metadata for clients if desired.
As of V4.0, all the previously mentioned profiles but also OIDC.SSO and OIDC.UserInfo can be used with unverified relying party configuration, but all the request messages must meet the unregistered client policy.
Each RP is defined with a JSON structure that is defined by the OpenID Connect Dynamic Client Registration 1.0 specification. That is, the format is just the format of the messages in that protocol. The required fields are:
Name | Type | Description |
---|---|---|
client_id | string | OIDC client identifier for the RP |
response_types | array of strings | Accepted client response types |
scope | string | Known scopes (space-delimited list in one string) |
redirect_uris | array of strings | Required redirect response locations for the RP |
Multiple RPs can be configured in a single file, and must be included in a JSON array.
An example of a JSON file with minimal settings:
{ "client_id": "demo_rp", "response_types": ["id_token"], "scope": "openid info profile email address phone", "redirect_uris": ["https://192.168.0.150/static"] } |
An example with multiple RPs:
[ { "client_id": "demo_rp", "response_types": ["id_token"], "scope": "openid info profile email address phone", "redirect_uris": ["https://192.168.0.150/static"] }, { "client_id": "demo_rp2", "response_types": ["id_token"], "scope": "openid info profile email address phone", "redirect_uris": ["https://192.168.0.150/static2"] } ] |
The alternative format supported is a profile of SAML metadata described in OAuthRPMetadataProfile. This profile makes heavier use of extensions than past adaptations of SAML metadata because of the comparative complexity of OAuth and OIDC relative to simpler protocols that have more similarity to SAML.
There are also additional considerations around the way keys are resolved. OIDC typically relies on untrusted leap-of-faith access to remote, unsigned JSON files to acquire frequently-rotated client secrets (i.e. shared/symmetric keys, not longer-lived keypairs), and the use of SAML metadata has to account for that difference. The techniques below are all auto-configured into the system.
Public keys can be resolved in two previously supported ways (<ds:KeyValue>
or <ds:X509Data>
elements) and two "novel" ways specific to OIDC:
You can embed a JSON Web Key Set, encoded into base64, inside a
The decoded data above is:
|
You can reference a JWKS (as in the other JWKS example) remotely via a URI. This is analagous to a rarely used (because of security, obviously) technique of remotely referencing certificates.
|
Client secrets, which are effectively either passwords or symmetric keys, are not something natively understood by SAML metadata, so extensions are defined to support them in one of the following ways.
You can embed a client secret directly inside a
|
Support also exists for indirectly referencing client secrets and resolving them at runtime separately. The metadata syntax is simply:
The reference string is just a label that has to correspond to some external source. Actually resolving secrets by reference requires one or more resolution beans be supplied in a list named shibboleth.oidc.ClientSecretValueResolvers, which does not exist by default. There are two built-in types of resolvers. Property-Based ResolutionClient secrets can be placed in a Java properties file (the keys are the labels used in the metadata reference and the values are the secrets). You can supply any number of such sources in the resolver list, generally in conf/global.xml. Each source is defined using a parent bean named shibboleth.oidc.PropertiesClientSecretValueResolver:
Attribute Resolver ResolutionA "generic" solution to resolving secrets is provided by means of leveraging the AttributeResolver to resolve the secret. The parent bean named shibboleth.oidc.ResolverServiceClientSecretValueResolver along with a set of attribute IDs will invoke the resolver and attempt to find a string value amongst them to return. During the resolution process, the "principal" variable is populated with the client secret reference label.
To simplify the configuration of the resolver, you may conditionalize connectors by attaching a |
If a secret value is prefixed by “{SHA2}”, then the supplied secret is hashed (with SHA-256) and base64-encoded before comparing it to the rest of the secret string. This is an unsalted hash so is not really suitable for exposing to offline attacks but is at least obsfuscated.
Name / Type | Description |
---|---|
shibboleth.oidc.ClientSecretValueResolvers List<net.shibboleth.oidc.metadata.ClientSecretValueResolver> | List of client secret resolvers to apply to any |
shibboleth.oidc.PropertiesClientSecretValueResolver net.shibboleth.oidc.metadata.impl.PropertiesClientSecretValueResolver | A resolver that looks for secrets in a Java properties file set via the |
shibboleth.oidc.ResolverServiceClientSecretValueResolver net.shibboleth.oidc.metadata.impl.ResolverServiceClientSecretValueResolver | A resolver that executes the AttributeResolver to resolve one or more client secrets via attributes set via the |