OpenID Connect / Federation (v6.6)
See also:
▸ OpenID Connect client for Java
▸ Basic OIDC configuration and clients
▸ Advanced OIDC configuration settings
Since v6.4.0, pac4j supports OpenID Federation v1.0. Your application is the relying party (RP): it publishes its own entity configuration and uses trust anchors to resolve the OpenID Provider (OP). This support has been tested with Connect2id v19.8.
The same pac4j-oidc module handles federation; there is no additional pac4j dependency:
Example (Maven dependency):
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-oidc</artifactId>
<version>${pac4j.version}</version>
</dependency>
Setting federation.targetOp enables federation for the OIDC client. It identifies the provider you want to resolve through the federation.
1) Federation endpoint
The federation endpoint publishes a signed entity configuration for your application. First, configure the keys used to sign it:
- either via a keystore (like for the SAML protocol):
oidcConfig.getFederation().getKeystore().setPath("file:./metadata/oidcfede.keystore");
oidcConfig.getFederation().getKeystore().setKeystorePassword("changeit");
oidcConfig.getFederation().getKeystore().setPrivateKeyPassword("changeit");
KeystoreProperties holds the other keystore settings, such as the type and key alias.
- or via a JWKS:
oidcConfig.getFederation().getJwks().setPath("file:./metadata/oidcfede.jwks");
oidcConfig.getFederation().getJwks().setKid("mykeyoidcfede26");
For a filesystem path, pac4j creates the keystore or JWKS if the file does not exist.
OidcFederationProperties controls both the RP entity statement generated by the federation endpoint and the federation-based OP resolution.
Available properties are:
keystore(KeystoreProperties): keystore-based signing material for the entity statement. By default, pac4j sets the certificate prefix tooidcfede-signing-cert, the certificate validity to 1 year, and uses a filesystem keystore generator.jwks(JwksProperties): JWKS-based signing material for the entity statement. If both JWKS and keystore are configured, JWKS is used first.entityConfigurationGenerator(EntityConfigurationGenerator): component used by the endpoint to generate the entity configuration. If not set,OidcClientassignsDefaultEntityConfigurationGenerator.validityInDays(int, default:90): validity duration of the generated entity statement (exp - iat).entityId(String, optional): entity identifier used asiss,sub, andaudin the generated statement. If not set, the callback URL is used.applicationType(String, default:web): value of theapplication_typeRP metadata claim (webornative).responseTypes(List<String>, default:["code"]): value of theresponse_typesRP metadata claim.grantTypes(List<String>, default:["authorization_code"]): value of thegrant_typesRP metadata claim.scopes(List<String>, default:["openid", "email", "profile"]): value of thescopeRP metadata claim (serialized as a space-separated string).clientRegistrationTypes(List<String>, default:["explicit", "automatic"]): RP registration modes accepted by pac4j for federation registration.clientName(String, optional): value of theclient_nameRP metadata claim.contacts(List<String>, default: empty list): value of thecontactsRP metadata claim when at least one contact is provided.trustAnchors(List<OidcTrustAnchorProperties>, default: empty list): trust anchors used to resolve trust chains (issuerand optionaljwksfor each anchor).targetOp(String): OP entity identifier to resolve via federation. When set, federation mode is used instead of discovery URI resolution.sendTrustChain(boolean, default:false): when enabled, sends the RP trust chain upfront in authorization requests.secretExportFile(String): the file where to save a received secret during an explicit registration.
At least one signing source must be configured with a resource/path (jwks or keystore) to generate the entity configuration.
Use EntityConfigurationGenerator to serve the entity configuration from your application’s federation endpoint:
Spring Boot example:
@RequestMapping(value = "/.well-known/openid-federation", produces = DefaultEntityConfigurationGenerator.CONTENT_TYPE)
@ResponseBody
public String oidcFederation() throws HttpAction {
val oidcClient = (OidcClient) config.getClients().findClient("OidcClient").get();
return oidcClient.getConfiguration().getFederation().getEntityConfigurationGenerator().generate();
}
or
@RequestMapping(value = "/.well-known/openid-federation")
@ResponseBody
public ResponseEntity<String> oidcFederation() throws HttpAction {
val oidcClient = (OidcClient) config.getClients().findClient("OidcClient").get();
val generator = oidcClient.getConfiguration().getFederation().getEntityConfigurationGenerator();
val entityStatement = generator.generateEntityStatement();
val contentType = generator.getContentType();
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.body(entityStatement);
}
2) Using trust anchors
In federation mode, leave discoveryURI unset. Give pac4j the target OP’s entity identifier and the trust anchors it should use to resolve that provider:
val federation = oidcConfig.getFederation();
federation.setTargetOp("http://localhost:8080/op");
val trust = new OidcTrustAnchorProperties();
trust.setIssuer("http://localhost:8081/ta");
trust.getJwks().setPath("http://localhost:8081/ta/jwks.json"); // optional
federation.getTrustAnchors().add(trust);
The federation metadata resolver performs a blocking load on first use, then refreshes metadata in the background when the trust chain expires.
3) Explicit / automatic client registration
If the RP is not yet registered and its clientId is left blank, pac4j supports both client registration modes and validates them against the OP metadata.
Priority is given to automatic mode if supported by the OP. In this case, the entity statement is sent via the client_assertion parameter in the authorization request URL.
Otherwise, if only explicit mode is supported and the federation_registration_endpoint exists, pac4j calls it to retrieve a client_id (and optionally a client_secret).
The client_id is displayed in the logs as follows: /!\ Explicit registration of the client 'http://rp' returned id: [XXX]. This information will not be repeated. You MUST manually add this value to your configuration before the next application startup!.
The client_secret is saved in the secretExportFile and must also be manually added to the configuration: /!\ The received secret has been saved into the file: YYY.