This software is not yet released and this is preliminary documentaton subject to significant change. It should not be used in production or to protect important resources at this stage.
OIDCBasics
Configuration Files
The OIDC protocol plugin will add these four new files to the system:
conf/sp/oidc.properties
Global/default OIDC options and paths to keys.
conf/sp/oidc-credentials.xml
Bean definitions of OIDC JSON Web Keys (JWK) for signing and encrypting request and response tokens. For import into the conf/sp/agents.xml file
conf/sp/oidc-metadata-providers.xml
Bean definitions of metadata providers. By default, a dynamic HTTP Provider for fetching the well-known configuration of an OpenID Provider.
conf/attributes/oidc-claim-rules.xml
OIDC transcoding rules that translate OIDC claims into internal IdPAttributes.
Enabling OIDC
There are a few steps to globally outfit your Agents with OIDC support:
Install the protocol plugin (net.shibboleth.plugin.shibd.oidc).
Import or generate JWK keys for OIDC use. Failure to perform this step while adding the import in the next step will prevent the SP plugin’s AgentResolverService from initializing itself.
Add an import statement to the end of conf/sp/agents.xml to import all defined OIDC credentials into the configuration (but only after the previous step is complete). Noting that a single JWK can contain both private and public key material.
... <import resource="oidc-credentials.xml" /> </beans>Add an import statment to the end of conf/attributes/default-rules.xml to import the OIDC transcoding rules:
... <import resource="oidc-claim-rules.xml" /> </beans>
The system automatically does a couple of things for you when detecting the presence of the OIDC plugin:
Enables all the supported OIDC profiles (with default behavior) for all Agents/Applications. For the moment, this is just OIDC.SSO.
Defines the necessary OIDC web flow operations on behalf of the Agent to support initiating sessions and handling all the available OIDC authentication flows. For the moment, only the Authorization Code Flow is supported.
Eventually, you can control that at a much deeper level if you need to, but initially (and possibly for a long while), there is no need.
Client Registration
In vanilla (without OpenID Federation) OIDC, trust between an RP and OP is established through client registration, in which the RP becomes a registered OAuth 2.0/OIDC client with the OP. Registration occurs either statically (out-of-band) or dynamically.
Static registration involves the RP operator manually providing the OP with information about the RP, such as redirect URIs, client name, and public keys. In return, the OP generates the information the RP needs to interact with it, including the client ID and client secret.
Dynamic client registration, on the other hand, is a programmatic, runtime process that allows an RP to self-register with an OP. After registration, the RP again receives the necessary information to interact with the OP.
Currently, the plugin does not support dynamic client registration, so the RP must be manually registered with any OP you wish to use.
ClientIDs
During the client registration process, the OP issues a client identifier (client_id) and, for confidential clients, a client secret. The client_id uniquely identifies the relying party within the OP—functionally similar to how the entityID identifies a SP in SAML. Within the hub, the client_id is treated equivalently to a SAML entityID.
Issuer
The SP’s OIDC/OAuth identity (client_id) is set with an “issuer” property. You can set this globally with the sp.issuer property in conf/sp/sp.properties. If you need to get more complex and support multiple non-overlapping Agents in the Hub, you would set thep:issuer property in each Agent bean in conf/sp/agents.xml:
...
<bean p:id="app.example.org" parent="shibboleth.sp.Agent" p:issuer="client_id" />
...Different from the SAML case, the OP is the authority for client_ids (and hence the issuer used above); you do not generate them yourself.
Authenticating Authority
In a unilateral OIDC deployment (the only kind supported until OID Federation support is added), you’ll know the specific OP you intend to use and can directly configure the OP’s issuer identifier (issuer) for an Agent. For global default assignment as in an enterprise scenario, you can set this with the sp.defaultAuthority property in conf/sp/sp.properties. If you need to override the value for a particular Agent, you would set thep:authenticatingAuthority property in the Agent bean in conf/sp/agents.xml:
...
<bean p:id="app.example.org" parent="shibboleth.sp.Agent"
p:authenticatingAuthority="https://op.example.org" />
...Client Secret and JSON Web Keys
In its simplest configuration, the RP only requires a client secret, along with the client ID, to successfully interact with an OP and receive ID tokens. After obtaining the client_secret, as described in the Client Registration section, you can globally define it using the sp.oidc.defaultClientSecret property located in conf/sp/oidc.properties. Additionally, make sure to uncomment the default client secret credential in `conf/sp/oidc-credentials.xml`.
<!-- Uncomment this bean
<bean id="shibboleth.oidc.DefaultCredential" parent="shibboleth.oidc.ClientSecretCredential"
p:secret="%{sp.oidc.defaultClientSecret:#{null}}"/>
-->This might be adequate for a single Agent setup relying on a single OP, but in more complicated situations involving multiple OPs, you’ll NEED to remove the default from the properties file, comment out the default secret credential, and attach different credentials to different OPs using a relying party override (see the DefiningRelyingPartyConfiguration topic for more information). An example is shown below:
We are developing a better method for managing client secrets.
Even with a single OP, the RP may require additional, typically asymmetric, keys for:
Signing Request Objects used to pass authentication request parameters to the OP.
Decrypting ID Tokens returned by the OP.
Decrypting UserInfo responses returned by the OP.
Authenticating itself to the OP’s Token endpoint if you are using something other than the default client‑secret–based methods (such as
client_secret_basicor symmetric‑key mechanisms likeclient_secret_jwt). For example, using asymmetric keys to sign client authentication assertions usingprivate_key_jwtclient authentication.
The plugin supports keys in the JWK format. To configure keys, the default properties in conf/sp/oidc.properties assume your keys live in these files, though you can certainly adjust them if desired. (Note that the use of an sp subfolder is assumed.)
sp.oidc.signing.rs.key = %{idp.home}/credentials/sp/sp-signing-rs.jwk
sp.oidc.signing.es.key = %{idp.home}/credentials/sp/sp-signing-es.jwk
sp.oidc.encryption.rsa.key = %{idp.home}/credentials/sp/sp-encryption-rsa.jwkYou’ll need to uncomment the signing and encryption credential lists in conf/sp/oidc-credentials.xml, and the beans they reference:
<util:list id="shibboleth.oidc.SigningCredentials">
<ref bean="shibboleth.oidc.DefaultRSSigningCredential" />
<ref bean="shibboleth.oidc.DefaultESSigningCredential" />
</util:list>
...
<util:list id="shibboleth.oidc.EncryptionCredentials">
<ref bean="shibboleth.oidc.DefaultRSAEncryptionCredential" />
</util:list>If you do enable asymmetric Request Object signature signing and or ID Token / UserInfo response encryption, the RP will need to publicly publish signing and encryption public keys. This can either be achieved by registering them with the OP directly, or published in a Key Set Document. The Key Set Document endpoint is not yet supported in the alpha.
Generating New Keys
As with the IdP’s OpenID Provider plugin, keys can be generated using the provided wrappers, in bin/jwtgen.sh and bin/jwtgen.bat:
$ cd /opt/shibboleth-idp
$ bin/jwtgen-sp.sh -t RSA -s 2048 -u sig -i defaultRSASign | tail -n +2 > credentials/sp/sp-signing-rs.jwk
$ bin/jwtgen-sp.sh -t EC -c P-256 -u sig -i defaultECSign | tail -n +2 > credentials/sp/sp-signing-es.jwk
$ bin/jwtgen-sp.sh -t RSA -s 2048 -u enc -i defaultRSAEnc | tail -n +2 > credentials/sp/sp-encryption-rsa.jwkOIDC Metadata
Without OpenID Federation support, the mechanics of metadata handling and trust establishment in OpenID Connect differ from SAML. In OIDC, the OP publishes its own metadata and signing keys via discovery endpoints, which the RP retrieves (typically at runtime). The RP registers itself with the OP, as detailed in ClientRegistration, creating client metadata directly at the OP.
Obtaining OpenID Provider Metadata
The metadata for the OpenID Provider (OP) is retrieved dynamically using an HTTP metadata resolver. This resolver determines the Issuer location based on the Issuer Identifier. By default, it does this by appending the well-known path /.well-known/openid-configuration to the normalized Issuer Identifier (as per OpenID Connect Discovery). A default provider comes out of the box and should work without requiring any further modification.
If you do need to modify its behaviour, the configuration for the default metadata resolver can be found in /conf/sp/oidc-metadata-providers.xml. Note that the resolver configuration here differs from that for normal SAML Metadata resolvers. The maxCacheDuration defines how long the metadata remains valid before it expires and is re-fetched. Unlike SAML Metadata, the OIDC Provider metadata does not explicitly define cacheDuration or validUntil settings. An example config is given below:
Attributes
The primary function of this collection of software is to get data about a logged in user into an application. The Hub is responsible for producing the data from a potentially wide combination of sources and then packaging it in a simple form for the Agent to consume.
With OIDC, the primary source of that data will be:
The ID Token, which provides authenticated user identity and some standard claims
The UserInfo response, which carries additional profile information
The system automatically merges the user's identity and profile claims to generate a final set of claims for the authenticated end-user. At this stage, validation claims, such as the ID Token's audience and issuer, are remove--this process can be customized if necessary.
All data that the Hub extracts and passes to the Agent will be decoded into IdPAttribute objects with one or more values (various subtypes of IdPAttributeValue). The Agent will transform all supported types of values into strings when it receives them; the Hub maintains the fidelity of more complex types because future Agent implementations might have more advanced features for consuming the information.
The OIDC-specific transcoding rules (oidc-claim-rules.xml) include mappings for the most common OIDC claims. Currently, the ‘sub’ (identifier for the End-User at the Issuer) claim is always mapped directly to an IdPAttribute, even if no specific transcoder is defined for it.
Filtering/Acceptance
Assuming you can decode the data successfully, an additional required step is to “accept” them via the AttributeFilterConfiguration. This enables control over which data a particular authority can supply during SSO.
The IdP’s configuration (which is used by the Hub) does not default to accepting anything for security reasons. In lieu of fully tackling the filtering layer while initially testing, you can set the property sp.oidc.acceptUnfilteredAttributes in conf/sp/oidc.properties to essentially bypass use of the filter and accept “anything unspecified” that is decoded and has no explicit rule defined.
TBD example filter.
Further Reading
TBD.