OPFederationWIP
This page describes a feature that is still work in progress and should only be used for experimental testing at this point.
This page currently describes only the most common basic/common configuration options. The more advanced options will be added later.
Introduction
The OpenID Federation 1.0 (currently draft) specification defines components used to build multilateral federations and describes how to apply them on top of the OAuth2/OIDC stack.
The main Jira ticket to follow is https://shibboleth.atlassian.net/browse/JOIDC-222 . This is subject to change once a new Jira project is populated for the new OpenID Federation plugin to the OP plugin.
Source code access
The development is now being done on java-idp-plugin-oidc-op-oidfed repository.
The development was previously done on branch dev/JOIDC-222 in the java-idp-oidc repository, but the current approach is to provide the OpenID Federation features as a plugin/extension to the OP plugin.
See https://shibboleth.atlassian.net/wiki/spaces/DEV/pages/1137344525 for instructions on detailed instructions on how to access the source code.
Plugin (binary) snapshots
In order to deploy the current state of the work for federation feature, one needs to install (1) the latest snapshot of the OP plugin and (2) the latest snapshot of the OpenID Federation plugin for OP. The links to the latest snapshots can be found from the following endpoints:
OP:
https://build.shibboleth.net/maven/snapshots/net/shibboleth/idp/plugin/oidc/idp-plugin-oidc-op-distribution/4.4.0-SNAPSHOT/Federation plugin:
https://build.shibboleth.net/nexus-proxy/content/repositories/snapshots/net/shibboleth/idp/plugin/oidfed/idp-plugin-oidfed-op-dist/1.0.0-SNAPSHOT/
The tar.gz packages with the latest timestamps should be used.
The snapshot distribution package is signed with the Shibboleth Jenkins' key. To use that key as the truststore, download it from https://shibboleth.net/downloads/SHIBBOLETH_SNAPSHOT_PGP_KEYS.
Installation example
Make sure that the latest release versions of these plugins are installed (see https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376878882 ):
net.shibboleth.oidc.common (3.0.0 or later)
net.shibboleth.idp.plugin.oidc.config (3.0.0 or later)
cd /opt/shibboleth-idp
sh bin/plugin.sh -I net.shibboleth.oidc.common
sh bin/plugin.sh -I net.shibboleth.idp.plugin.oidc.config
wget https://shibboleth.net/downloads/SHIBBOLETH_SNAPSHOT_PGP_KEYS
sh bin/plugin.sh --noCheck --truststore SHIBBOLETH_SNAPSHOT_PGP_KEYS -i https://build.shibboleth.net/maven/snapshots/net/shibboleth/idp/plugin/oidc/idp-plugin-oidc-op-distribution/4.4.0-SNAPSHOT/idp-plugin-oidc-op-distribution-4.4.0-20250911.012902-9.tar.gz
sh bin/plugin.sh --noCheck --truststore SHIBBOLETH_SNAPSHOT_PGP_KEYS -i https://build.shibboleth.net/nexus-proxy/content/repositories/snapshots/net/shibboleth/idp/plugin/oidfed/idp-plugin-oidfed-op-dist/1.0.0-SNAPSHOT/idp-plugin-oidfed-op-dist-1.0.0-20250911.102501-2.tar.gzConfiguration
First configure the standard OIDC/OAuth2 feature as described here: https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376878976/OIDC+OP#Initial-Setup . Also note that ES384 and ES512 credentials are not included in the basic example, but the section https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376878976/OIDC+OP#Key-Generation-for-ES384-and-ES512 describes how to generate and enable them.
Regarding the publication of OP’s own configuration, follow the instructions for dynamic publication: https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376879256/OPDiscovery#Configuration . As documented on the page, this part contains editing of static/openid-configuration.json file and configuring servlet container to redirect /.well-known/openid-configuration requests into the oidc/configuration flow.
Federation configuration
Trust anchors
The locally trusted trust anchors need to be configured in conf/oidfed/oidfed-trust-anchors.json (NOTE! In earlier versions the file was located in conf-directory, not in its oidfed-subfolder). The file is a JSON file, containing a map of trust anchor identifiers to their locally trusted JWK sets. The contents below is just an example and not up-to-date keyset for the example trust anchor: fetch the current keyset by other means,
{
"https://ta.oidf-pilot.edugain.org": {
"keys": [
{
"alg": "ES512",
"crv": "P-521",
"kid": "vwOKov9Lo5z4cPqVpgcTsFtbaryfv8Lb_UhT2sMACuc",
"kty": "EC",
"use": "sig",
"x": "ARl3Tch9N0dYO7El-UBFss3_dsGvm1ehj4i_z7c26TXYJ9LxFYcqVDa1Dla5QUBpAp1bs2NOLFz_txwmFz4z9wJB",
"y": "Ac5wwr2RPrDE7R0mk691iC1k31KQRxlgacKsNAnEJ2auYdMn3p1kIKfxSHsALBmVyt-NTjzihvtL9JvnPjOLOgDY"
}
]
}
}Entity configuration
Generate a new key pair for the federation entity (ES512 in this example)
cd /opt/shibboleth-idp bin/jwtgen.sh -t EC -c P-521 -u sig -i defaultECFedSign | tail -n +2 > credentials/fed-signing-es521.jwk
Refer to the new key in
conf/oidc-credentials.xml... <bean id="shibboleth.oidfed.DefaultES521SigningCredential" parent="shibboleth.JWKCredential" p:resource="/opt/shibboleth-idp/credentials/fed-signing-es521.jwk" /> <util:list id="shibboleth.oidfed.SigningCredentials"> <ref bean="shibboleth.oidfed.DefaultES521SigningCredential" /> </util:list> ...
Define a signing algorithm that is compatible with the key (
conf/oidc.properties)idp.oidfed.entity.sigalg = ES512
Enable OIDFED.Configuration for shibboleth.UnverifiedRelyingParty in
conf/relying-party.xml... <bean id="shibboleth.UnverifiedRelyingParty" parent="RelyingParty"> <property name="profileConfigurations"> <list> <ref bean="OIDC.Keyset" /> <ref bean="OIDC.Configuration" /> <ref bean="OIDFED.Configuration" /> </list> </property> </bean> ...
Register your OP with a federation authority
This is obviously out of scope of the plugin itself, but in the end the authority should be included in the authority_hints in your entity configuration. In the example above, the OP is registered to
https://ia1.oidf-pilot.edugain.orgintermediate authority.idp.oidfed.entity.authorityHints = https://ia1.oidf-pilot.edugain.org
Finally, the servlet container needs to be configured to redirect /.well-known/openid-federation requests into the oidfed/entity-configuration flow.
After changes have been applied, the endpoint should be responding with the entity configuration.
curl -v https://op1.oidf-pilot.edugain.org/.well-known/openid-federation
* Request completely sent off
< HTTP/2 200
< alt-svc: h3=":443"; ma=2592000
< cache-control: no-store
< content-type: application/entity-statement+jwt;charset=UTF-8
< date: Wed, 25 Jun 2025 11:48:07 GMT
< set-cookie: __Host-JSESSIONID=6677368AD3E3F2C833A7FF6635D55A5B; Path=/; Secure; HttpOnly
< via: 1.1 Caddy
<
eyJraWQiOiJkZWZhdWx0RUNGZWRTaWduIiwidHlwIjoiZW50aXR5LXN0YXRlbWVudCtqd3QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiJodHRwczovL29wMS5vaWRmLXBpbG90LmVkdWdhaW4ub3JnIiwibWV0YWRhdGEiOnsib3BlbmlkX3Byb3ZpZGVyIjp7ImF1dGhvcml6YXRpb25fZW5kcG9pbnQiOiJodHRwczovL29wMS5vaWRmLXBpbG90LmVkdWdhaW4ub3JnL2lkcC9wcm9maWxlL29pZGMvYXV0aG9yaXplIiwidG9rZW5fZW5kcG9pbnQiOiJodHRwczovL29wMS5vaWRmLXBpbG90LmVkdWdhaW4ub3JnL2lkcC9wcm9maWxlL29pZGMvdG9rZW4iLCJyZWdpc3RyYXRpb25fZW5kcG9pbnQiOiJodHRwczovL29wMS5vaWRmLXBpbG90LmVkdWdhaW4ub3JnL2lkcC9wcm9maWxlL29pZGMvcmVnaXN0ZXIiLCJpbnRyb3NwZWN0aW9uX2VuZHBvaW50IjoiaHR0cHM6Ly9vcDEub2lkZi1waWxvdC5lZHVnYWluLm9yZy9pZHAvcHJvZmlsZS9vYXV0aDIvaW50cm9zcGVjdGlvbiIsInJldm9jYXRpb25fZW5kcG9pbnQiOiJodHRwczovL29wMS5vaWRmLXBpbG90LmVkdWdhaW4ub3JnL2lkcC9wcm9maWxlL29hdXRoMi9yZXZvY2F0aW9uIiwicHVzaGVkX2F1dGhvcml6YXRpb25fcmVxdWVzdF9lbmRwb2ludCI6Imh0dHBzOi8vb3AxLm9pZGYtcGlsb3QuZWR1Z2Fpbi5vcmcvaWRwL3Byb2ZpbGUvb2F1dGgyL3B1c2hlZC1hdXRob3JpemF0aW9uIiwiZmVkZXJhdGlvbl9yZWdpc3RyYXRpb25fZW5kcG9pbnQiOiJodHRwczovL29wMS5vaWRmLXBpbG90LmVkdWdhaW4ub3JnL2lkcC9wcm9maWxlL29pZGZlZC9yZWdpc3RlciIsImlzc3VlciI6Imh0dHBzOi8vb3AxLm9pZGYtcGlsb3QuZWR1Z2Fpbi5vcmciLCJqd2tzX3VyaSI6Imh0dHBzOi8vb3AxLm9pZGYtcGlsb3QuZWR1Z2Fpbi5vcmcvaWRwL3Byb2ZpbGUvb2lkYy9rZXlzZXQiLCJzY29wZXNfc3VwcG9ydGVkIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJlbWFpbCIsImFkZHJlc3MiLCJwaG9uZSIsIm9mZmxpbmVfYWNjZXNzIl0sInJlc3BvbnNlX3R5cGVzX3N1cHBvcnRlZCI6WyJjb2RlIiwiaWRfdG9rZW4iLCJpZF90b2tlbiB0b2tlbiIsImNvZGUgaWRfdG9rZW4iLCJjb2RlIHRva2VuIiwiY29kZSBpZF90b2tlbiB0b2tlbiJdLCJyZXNwb25zZV9tb2Rlc19zdXBwb3J0ZWQiOlsicXVlcnkiLCJmcmFnbWVudCIsImZvcm1fcG9zdCJdLCJncmFudF90eXBlc19zdXBwb3J0ZWQiOlsiYXV0aG9yaXphdGlvbl9jb2RlIiwiaW1wbGljaXQiLCJyZWZyZXNoX3Rva2VuIl0sInRva2VuX2VuZHBvaW50X2F1dGhfbWV0aG9kc19zdXBwb3J0ZWQiOlsiY2xpZW50X3NlY3JldF9iYXNpYyIsImNsaWVudF9zZWNyZXRfcG9zdCIsImNsaWVudF9zZWNyZXRfand0IiwicHJpdmF0ZV9rZXlfand0Il0sInJlcXVlc3Rfb2JqZWN0X3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsibm9uZSIsIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIkhTMjU2IiwiSFMzODQiLCJIUzUxMiIsIkVTMjU2IiwiRVMzODQiLCJFUzUxMiJdLCJyZXF1ZXN0X3BhcmFtZXRlcl9zdXBwb3J0ZWQiOnRydWUsInJlcXVlc3RfdXJpX3BhcmFtZXRlcl9zdXBwb3J0ZWQiOnRydWUsInJlcXVpcmVfcmVxdWVzdF91cmlfcmVnaXN0cmF0aW9uIjp0cnVlLCJkcG9wX3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsiUlMyNTYiLCJSUzM4NCIsIlJTNTEyIiwiRVMyNTYiLCJFUzM4NCIsIkVTNTEyIl0sImNsaWVudF9yZWdpc3RyYXRpb25fdHlwZXNfc3VwcG9ydGVkIjpbImF1dG9tYXRpYyIsImV4cGxpY2l0Il0sInN1YmplY3RfdHlwZXNfc3VwcG9ydGVkIjpbInB1YmxpYyIsInBhaXJ3aXNlIl0sInVzZXJpbmZvX2VuZHBvaW50IjoiaHR0cHM6Ly9vcDEub2lkZi1waWxvdC5lZHVnYWluLm9yZy9pZHAvcHJvZmlsZS9vaWRjL3VzZXJpbmZvIiwiZW5kX3Nlc3Npb25fZW5kcG9pbnQiOiJodHRwczovL29wMS5vaWRmLXBpbG90LmVkdWdhaW4ub3JnL2lkcC9wcm9maWxlL29pZGMvZW5kLXNlc3Npb24iLCJpZF90b2tlbl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIkhTMjU2IiwiSFMzODQiLCJIUzUxMiIsIkVTMjU2IiwiRVMzODQiLCJFUzUxMiIsIlBTMjU2IiwiUFMzODQiLCJQUzUxMiJdLCJpZF90b2tlbl9lbmNyeXB0aW9uX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIlJTQTFfNSIsIlJTQS1PQUVQIiwiUlNBLU9BRVAtMjU2IiwiUlNBLU9BRVAtMzg0IiwiUlNBLU9BRVAtNTEyIiwiQTEyOEtXIiwiQTE5MktXIiwiQTI1NktXIiwiQTEyOEdDTUtXIiwiQTE5MkdDTUtXIiwiQTI1NkdDTUtXIiwiRUNESC1FUyIsIkVDREgtRVMrQTEyOEtXIiwiRUNESC1FUytBMTkyS1ciLCJFQ0RILUVTK0EyNTZLVyJdLCJpZF90b2tlbl9lbmNyeXB0aW9uX2VuY192YWx1ZXNfc3VwcG9ydGVkIjpbIkExMjhDQkMtSFMyNTYiLCJBMTkyQ0JDLUhTMzg0IiwiQTI1NkNCQy1IUzUxMiIsIkExMjhHQ00iLCJBMTkyR0NNIiwiQTI1NkdDTSJdLCJ1c2VyaW5mb19zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIkhTMjU2IiwiSFMzODQiLCJIUzUxMiIsIkVTMjU2IiwiRVMzODQiLCJFUzUxMiIsIlBTMjU2IiwiUFMzODQiLCJQUzUxMiJdLCJ1c2VyaW5mb19lbmNyeXB0aW9uX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIlJTQTFfNSIsIlJTQS1PQUVQIiwiUlNBLU9BRVAtMjU2IiwiUlNBLU9BRVAtMzg0IiwiUlNBLU9BRVAtNTEyIiwiQTEyOEtXIiwiQTE5MktXIiwiQTI1NktXIiwiQTEyOEdDTUtXIiwiQTE5MkdDTUtXIiwiQTI1NkdDTUtXIiwiRUNESC1FUyIsIkVDREgtRVMrQTEyOEtXIiwiRUNESC1FUytBMTkyS1ciLCJFQ0RILUVTK0EyNTZLVyJdLCJ1c2VyaW5mb19lbmNyeXB0aW9uX2VuY192YWx1ZXNfc3VwcG9ydGVkIjpbIkExMjhDQkMtSFMyNTYiLCJBMTkyQ0JDLUhTMzg0IiwiQTI1NkNCQy1IUzUxMiIsIkExMjhHQ00iLCJBMTkyR0NNIiwiQTI1NkdDTSJdLCJkaXNwbGF5X3ZhbHVlc19zdXBwb3J0ZWQiOlsicGFnZSJdLCJjbGFpbXNfc3VwcG9ydGVkIjpbImF1ZCIsImlzcyIsInN1YiIsImlhdCIsImV4cCIsImFjciIsImF1dGhfdGl* Connection #0 to host op1.oidf-pilot.edugain.org left intact
tZSIsImVtYWlsIiwiZW1haWxfdmVyaWZpZWQiLCJhZGRyZXNzIiwicGhvbmUiLCJwaG9uZV9udW1iZXJfdmVyaWZpZWQiLCJuYW1lIiwiZmFtaWx5X25hbWUiLCJnaXZlbl9uYW1lIiwibWlkZGxlX25hbWUiLCJuaWNrbmFtZSIsInByZWZlcnJlZF91c2VybmFtZSIsInByb2ZpbGUiLCJwaWN0dXJlIiwid2Vic2l0ZSIsImdlbmRlciIsImJpcnRoZGF0ZSIsInpvbmVpbmZvIiwibG9jYWxlIiwidXBkYXRlZF9hdCJdLCJjbGFpbXNfcGFyYW1ldGVyX3N1cHBvcnRlZCI6dHJ1ZSwiZnJvbnRjaGFubmVsX2xvZ291dF9zdXBwb3J0ZWQiOnRydWUsImZyb250Y2hhbm5lbF9sb2dvdXRfc2Vzc2lvbl9zdXBwb3J0ZWQiOnRydWUsImJhY2tjaGFubmVsX2xvZ291dF9zdXBwb3J0ZWQiOnRydWUsImJhY2tjaGFubmVsX2xvZ291dF9zZXNzaW9uX3N1cHBvcnRlZCI6dHJ1ZX19LCJqd2tzIjp7ImtleXMiOlt7Imt0eSI6IkVDIiwidXNlIjoic2lnIiwiY3J2IjoiUC01MjEiLCJraWQiOiJkZWZhdWx0RUNGZWRTaWduIiwieCI6IkFNSURkazBXR0NaQ2xkZDlIMW82Z0FFYjJhSEE0bVY2bXJab05ZX1YtT1ZCZ1gtU0txMnZydWFXeW5nQjJyWVZGbVJyR3VjMW02V0N5cW9icXFmUTZRSkIiLCJ5IjoiQVZjNkVkUlVkLWk0T2NEdGtJZ3NDc2Jsc1ZOS1ZFYU9ZTWxfOU9iRlZFc0loYWFuY3AwY2FSeXN3bEItQ0RQWDhyaUN4NFNCdWxEOEg1WjhlbVl4WHlaaCJ9XX0sImlzcyI6Imh0dHBzOi8vb3AxLm9pZGYtcGlsb3QuZWR1Z2Fpbi5vcmciLCJhdXRob3JpdHlfaGludHMiOlsiaHR0cHM6Ly9pYTEub2lkZi1waWxvdC5lZHVnYWluLm9yZyJdLCJleHAiOjE3NTA5Mzg0ODcsImlhdCI6MTc1MDg1MjA4N30.ALrbgqQaeRI8OPEZURDjnKpaRsYVgiZMVTywlrB45OcHBxuNVfKffkAWHLaGHWNTHPaBldqXOuNLqdzMuIVIQ2zNAUuVAXOFpo6gyz9FVfGQmbAsRAQVvxLlihEbPfJg1HZxBEZ0aqVV65Ano5usPv4Qx66qiRnPq4ZHqaNg2GuNvSDjAutomatic registration
Enable OIDFED.AutomaticRegistration for shibboleth.DefaultRelyingParty in
conf/relying-party.xml... <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty"> <property name="profileConfigurations"> <list> <ref bean="OIDC.SSO" /> <ref bean="OIDC.UserInfo"/> <ref bean="OAUTH2.Token"/> <ref bean="OAUTH2.Revocation"/> <ref bean="OAUTH2.Introspection" /> <ref bean="OAUTH2.PAR" /> <bean parent="OIDFED.AutomaticRegistration" /> </list> </property> </bean> ...
Enable automatically registered RPs access per flow/endpoint (
conf/oidc.properties):idp.oidfed.authorize.automaticRegistrationCondition = shibboleth.Conditions.TRUE idp.oidfed.token.automaticRegistrationCondition = shibboleth.Conditions.TRUE idp.oidfed.userinfo.automaticRegistrationCondition = shibboleth.Conditions.TRUE
Trust Chain Resolution method
By default, all the valid trust chains for the incoming RP are resolved locally. The valid trust anchors are configured as described in sectionhttps://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/4500914216/OPFederationWIP#Trust-anchors.
The property idp.oidfed.trustchain.resolver.useResolverApiCondition may be used for defining a condition when a remote resolve entity API would be used instead of the local resolution. By default, its value is shibboleth.Conditions.FALSE. A value shibboleth.Conditions.TRUE would mean that remote resolution is always attempted.
Another property idp.oidfed.trustchain.resolver.fallbackToLocalCondition may be used for controlling whether or not to fall back into local resolution if the remote resolution fails. By default its value is shibboleth.Conditions.TRUE.
Remote Trust Chain resolution endpoints
If the condition for using remote resolver entity API returns true for the incoming client, by default the configuration in the conf/oidfed/oidfed-trustchain-resolver.xml -file is exploited.
...
<bean id="shibboleth.oidfed.DefaultTrustedRemoteResolverEntitiesLookupStrategy" parent="shibboleth.Functions.Constant">
<constructor-arg name="target">
<util:list value-type="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity">
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity"
c:entity="https://trust-anchor.federation.local"
c:anchors="https://trust-anchor.federation.local,https://another.federation.local" />
</util:list>
</constructor-arg>
</bean>
...The example above configures an entity https://trust-anchor.federation.local to be trusted for remote resolution. Its federation_resolve_endpointis automatically fetched and used as the endpoint together with the two trust_anchors parameters as specified by the c:anchors value. It could also contain a single value. Multiple values are separated by commas. Only the trust anchors that are locally trusted (see https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/4500914216/OPFederationWIP#Trust-anchors ) are used: others are filtered out.
Resolve Entity API
Enable OIDFED.ResolveEntity for shibboleth.DefaultRelyingParty in conf/relying-party.xml
...
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
<ref bean="OIDC.SSO" />
<ref bean="OIDC.UserInfo"/>
<ref bean="OAUTH2.Token"/>
<ref bean="OAUTH2.Revocation"/>
<ref bean="OAUTH2.Introspection" />
<ref bean="OAUTH2.PAR" />
<bean parent="OIDFED.AutomaticRegistration" />
<bean parent="OIDFED.ResolveEntity" />
</list>
</property>
</bean>
...Remarks:
Currently the Resolve Entity API doesn’t support client authentication
The Resolve Entity API exploits the same trust anchor configuration as automatic registration (via
conf/oidfed/oidfed-trust-anchors.json).