<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jérôme LELEU</title>
    <description>The latest articles on Forem by Jérôme LELEU (@jleleu).</description>
    <link>https://forem.com/jleleu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3857046%2F85b42614-37cd-4b19-b558-83ce9a555d0d.jpg</url>
      <title>Forem: Jérôme LELEU</title>
      <link>https://forem.com/jleleu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jleleu"/>
    <language>en</language>
    <item>
      <title>OpenID Federation with pac4j and Connect2id</title>
      <dc:creator>Jérôme LELEU</dc:creator>
      <pubDate>Thu, 02 Apr 2026 07:24:34 +0000</pubDate>
      <link>https://forem.com/jleleu/openid-federation-with-pac4j-and-connect2id-5f5c</link>
      <guid>https://forem.com/jleleu/openid-federation-with-pac4j-and-connect2id-5f5c</guid>
      <description>&lt;p&gt;Since version 6.4.0, &lt;a href="https://www.pac4j.org" rel="noopener noreferrer"&gt;pac4j&lt;/a&gt; supports the &lt;a href="https://openid.net/specs/openid-federation-1_0.html" rel="noopener noreferrer"&gt;OpenID (Connect) Federation specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The OpenID Connect (in short, OIDC) support in pac4j (&lt;code&gt;pac4j-oidc&lt;/code&gt; module) is strongly based on the Nimbus libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.nimbusds&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;oauth2-oidc-sdk&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.nimbusds&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;nimbus-jose-jwt&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are great Open Source libraries developed by the Connect2id team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://connect2id.com/products/nimbus-jose-jwt" rel="noopener noreferrer"&gt;https://connect2id.com/products/nimbus-jose-jwt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://connect2id.com/products/nimbus-oauth-openid-connect-sdk" rel="noopener noreferrer"&gt;https://connect2id.com/products/nimbus-oauth-openid-connect-sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And as they also developed an OIDC server, let's test the pac4j OpenID Federation support with it!&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Components
&lt;/h2&gt;

&lt;p&gt;In this article, we will focus on the setup of this particular configuration more than entering into the details and options of each component.&lt;/p&gt;

&lt;p&gt;We need 3 components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a client, which is called the Relying Party (RP) in OIDC, and we will use pac4j&lt;/li&gt;
&lt;li&gt;a server, which is called the OpenID Provider (OP) in OIDC, and we will use the Connect2id server&lt;/li&gt;
&lt;li&gt;a trust anchor (TA in short) and we will create a simulated one to simplify the whole installation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  a) RP = pac4j
&lt;/h3&gt;

&lt;p&gt;In the pac4j ecosystem, Spring Boot is the most popular web stack so let's use the &lt;code&gt;spring-webmvc-pac4j&lt;/code&gt; implementation in action in this simple demo: &lt;a href="https://github.com/pac4j/simple-spring-boot-pac4j-demos/tree/oidc/src/main/java/org/pac4j/demos" rel="noopener noreferrer"&gt;https://github.com/pac4j/simple-spring-boot-pac4j-demos/tree/oidc/src/main/java/org/pac4j/demos&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;SpringBootDemo&lt;/code&gt; class runs the Spring Boot demo&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;SecurityConfig&lt;/code&gt; class defines the security configuration (OIDC + URL protection)&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;Application&lt;/code&gt; class is a simple controller with two URLs: one public and the other one protected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need the following dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.pac4j&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-webmvc-pac4j&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;8.0.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.pac4j&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;pac4j-oidc&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;6.4.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  b) OP = Connect2id
&lt;/h3&gt;

&lt;p&gt;To make things easy, let's use the "Quick start" of the Connect2id server: &lt;a href="https://connect2id.com/products/server/docs/quick-start" rel="noopener noreferrer"&gt;https://connect2id.com/products/server/docs/quick-start&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Download the ZIP and unzip it. You will get a &lt;code&gt;connect2id-server-19.8&lt;/code&gt; directory or something similar.&lt;/p&gt;

&lt;p&gt;In that directory, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run the server: &lt;code&gt;tomcat/bin/startup.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;stop the server: &lt;code&gt;tomcat/bin/shutdown.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;change its configuration in the &lt;code&gt;tomcat/webapps/c2id/WEB-INF/oidcProvider.properties&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;check the logs in the &lt;code&gt;tomcat/logs/c2id-server.log&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  c) TA is simulated
&lt;/h3&gt;

&lt;p&gt;The trust anchor is a core component in our installation: it plays the role of the certificate authority.&lt;/p&gt;

&lt;p&gt;We could use some specific existing software, but to make things easier, we will simulate it without entering into the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Configuration
&lt;/h2&gt;

&lt;p&gt;Now that we have our 3 components, let's configure them to work together via the OpenID Federation protocol.&lt;/p&gt;

&lt;p&gt;So far, when using the OpenID Connect protocol, you always need to define your client (&lt;code&gt;client_id&lt;/code&gt; + credentials + &lt;code&gt;redirect_uri&lt;/code&gt;) at the server level to be able to perform the authentication process.&lt;/p&gt;

&lt;p&gt;The OpenID Federation introduces the idea of trust and chain of trust between components to allow authenticating without registering upfront/at all.&lt;/p&gt;

&lt;p&gt;And each component (entry) in the federation exposes its metadata on the new &lt;code&gt;.well-known/openid-federation&lt;/code&gt; endpoint. A dedicated JWKS ensures security, similar to an X.509 certificate but without its complexity/difficulty.&lt;/p&gt;

&lt;h2&gt;
  
  
  a) pac4j
&lt;/h2&gt;

&lt;p&gt;As the Connect2id server runs by default on port 8080, we'll move our pac4j application to port 8081 via this configuration in the &lt;code&gt;application.properties&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8081&lt;/span&gt;
&lt;span class="py"&gt;app.base-url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We update the &lt;code&gt;SecurityConfig&lt;/code&gt; class to change the configuration for the federation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.pac4j.demos&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.nimbusds.jose.JWSAlgorithm&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.pac4j.core.config.Config&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.pac4j.oidc.client.OidcClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.pac4j.oidc.config.OidcConfiguration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.pac4j.oidc.config.method.PrivateKeyJwtClientAuthnMethodConfig&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.pac4j.oidc.federation.config.OidcTrustAnchorProperties&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.pac4j.springframework.config.Pac4jSecurityConfig&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.beans.factory.annotation.Value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Bean&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Configuration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.servlet.config.annotation.InterceptorRegistry&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityConfig&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Pac4jSecurityConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${app.base-url:http://localhost:8080}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;baseUri&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OidcConfiguration&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rpJwks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRpJwks&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rpJwks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJwksPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file:./metadata/rpjwks.jwks"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;rpJwks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setKid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"defaultjwks0426"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setClientAuthenticationMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ClientAuthenticationMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE_KEY_JWT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;privateKeyJwtConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrivateKeyJwtClientAuthnMethodConfig&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpJwks&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPrivateKeyJWTClientAuthnMethodConfig&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;privateKeyJwtConfig&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestObjectSigningAlgorithm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JWSAlgorithm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RS256&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;federation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFederation&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;federation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTargetOp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://127.0.0.1:8080/c2id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;trust&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OidcTrustAnchorProperties&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;trust&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setIssuer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:8081/trustanchor"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;trust&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJwksPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"classpath:trustanchor.jwks"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;federation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTrustAnchors&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trust&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;federation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getJwks&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setJwksPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file:./metadata/oidcfede.jwks"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;federation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getJwks&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setKid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mykeyoidcfede26"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;federation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContactName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C2ID Test RP (Localhost)"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;federation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContactEmails&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jerome@casinthecloud.com"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;federation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEntityId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:8081"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseUri&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/callback"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OidcClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addInterceptors&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;InterceptorRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;addSecurity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OidcClient"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;addPathPatterns&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/protected/**"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This new configuration needs explanations!&lt;/p&gt;

&lt;p&gt;Notice that we don't have any &lt;code&gt;dicsoveryURI&lt;/code&gt;, nor any &lt;code&gt;clientId&lt;/code&gt; or any credentials!&lt;/p&gt;

&lt;p&gt;We need two distinct and separate JWKS for our setup here: one for the federation (to sign our entity statement) and one for the &lt;code&gt;private_key_jwt&lt;/code&gt; client authentication (when calling the token endpoint). &lt;strong&gt;You should never use the same JWKS for both!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rpJwks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRpJwks&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;rpJwks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJwksPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file:./metadata/rpjwks.jwks"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;rpJwks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setKid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"defaultjwks0426"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setClientAuthenticationMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ClientAuthenticationMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE_KEY_JWT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;privateKeyJwtConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrivateKeyJwtClientAuthnMethodConfig&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpJwks&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPrivateKeyJWTClientAuthnMethodConfig&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;privateKeyJwtConfig&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;defines the JWKS used for the &lt;code&gt;private_key_jwt&lt;/code&gt; client authentication. This will create a key named &lt;code&gt;defaultjwks0426&lt;/code&gt; and save it as a JWKS in the &lt;code&gt;metadata/rpjwks.jwks&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;We use the Request Object signing configuration as the federation requires more security:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestObjectSigningAlgorithm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JWSAlgorithm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RS256&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JWKS used for signing here is the previous one defined in the &lt;code&gt;rpJwks&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;In federation, you must define your trust anchor(s) (the root authority). So we do that with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final var trust = new OidcTrustAnchorProperties();
trust.setIssuer("http://localhost:8081/trustanchor");
trust.setJwksPath("classpath:trustanchor.jwks");
federation.getTrustAnchors().add(trust);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It assumes the simulated TA is available at the &lt;code&gt;http://localhost:8081/trustanchor&lt;/code&gt; URL with a JWKS also in the classpath (this is in the entity statement, but we rely on the one downloaded in the classpath).&lt;/p&gt;

&lt;p&gt;The target OP (= the server we want to use for authentication) is of course the Connect2id and is defined by: &lt;code&gt;federation.setTargetOp("http://127.0.0.1:8080/c2id")&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, the entity identifier of our RP pac4j client is &lt;code&gt;http://localhost:8081&lt;/code&gt; (its own base URL) thanks to &lt;code&gt;federation.setEntityId("http://localhost:8081")&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As we don't have a &lt;code&gt;clientId&lt;/code&gt;, the &lt;code&gt;entityId&lt;/code&gt; is used to identify the RP.&lt;/p&gt;

&lt;p&gt;It also means we must expose a dedicated OpenID federation endpoint (in the &lt;code&gt;Application&lt;/code&gt; class):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/.well-known/openid-federation"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;produces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultEntityConfigurationGenerator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ResponseBody&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;oidcFederation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;HttpAction&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;oidcClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OidcClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClients&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;findClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OidcClient"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;oidcClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConfiguration&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getFederation&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getEntityConfigurationGenerator&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;generateEntityStatement&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  b) Connect2id
&lt;/h2&gt;

&lt;p&gt;The Connect2Id server is available on the &lt;code&gt;http://127.0.0.1:8080/c2id&lt;/code&gt; URL.&lt;/p&gt;

&lt;p&gt;To enable federation, in the &lt;code&gt;tomcat/webapps/c2id/WEB-INF/oidcProvider.properties&lt;/code&gt; file, you need to change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;### Federation ###
&lt;/span&gt;
&lt;span class="c"&gt;# Enables / disables OpenID Federation 1.0. Disabled by default.
&lt;/span&gt;&lt;span class="py"&gt;op.federation.enable&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# The configured trust anchors. Leave blank if none.
&lt;/span&gt;&lt;span class="py"&gt;op.federation.trustAnchors.1&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081/trustanchor&lt;/span&gt;

&lt;span class="c"&gt;# Trust anchors or intermediate entities that may issue an entity statement
# about this OpenID provider. Leave blank if none.
&lt;/span&gt;&lt;span class="py"&gt;op.federation.authorityHints.1&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081/trustanchor&lt;/span&gt;
&lt;span class="py"&gt;op.federation.authorityHints.2&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;
&lt;span class="py"&gt;op.federation.authorityHints.3&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;

&lt;span class="c"&gt;# The enabled OpenID Federation 1.0 client registration types. The default
# value is none (for deployments that use manual client registration only).
#
# Supported OpenID Federation 1.0 client registration types:
#
#     * explicit
#     * automatic
#
&lt;/span&gt;&lt;span class="py"&gt;op.federation.clientRegistrationTypes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;automatic&lt;/span&gt;

&lt;span class="c"&gt;# The enabled methods for authenticating OpenID Federation 1.0 automatic client
# registration requests at the OAuth 2.0 authorisation endpoint.
#
# Supported methods:
#
#     * request_object
#
&lt;/span&gt;&lt;span class="py"&gt;op.federation.autoClientAuthMethods.ar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;request_object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters are quite obvious: &lt;code&gt;op.federation.enable=true&lt;/code&gt; to enable the federation.&lt;/p&gt;

&lt;p&gt;The trust anchor is defined twice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;op.federation.trustAnchors.1=http://localhost:8081/trustanchor
op.federation.authorityHints.1=http://localhost:8081/trustanchor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we use the automatic registration mode (the simplest way): &lt;code&gt;op.federation.clientRegistrationTypes=automatic&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Stop and start again the Connect2id server. You should see this welcome page on &lt;code&gt;http://127.0.0.1:8080/c2id&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.pac4j.org%2Fimg%2Fblog%2F2026_04%2Fc2id_welcome.png" class="article-body-image-wrapper"&gt;&lt;img alt="Connect2id welcome page" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.pac4j.org%2Fimg%2Fblog%2F2026_04%2Fc2id_welcome.png" width="792" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Meaning the federation is properly enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  c) Simulated TA
&lt;/h2&gt;

&lt;p&gt;For the simulated trust anchor, let's not enter into the details to limit the size of this article.&lt;/p&gt;

&lt;p&gt;We expose a few endpoints to simulate its behavior and the contract it must respect (in the &lt;code&gt;Application&lt;/code&gt; class):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/trustanchor/.well-known/openid-federation"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;produces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultEntityConfigurationGenerator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ResponseBody&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;trustAnchorWellKnown&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"eyJraWQiOiJ0YS1rZX...vBabK4L897BynKoyihoUHj4Q"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/trustanchor/fetch"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;produces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultEntityConfigurationGenerator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ResponseBody&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;trustAnchorFetch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// OP&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"eyJraWQiOiJ0YS1rZX...ldEVZd49QyZRnENUCP4hOMCWiQ"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// RP&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"eyJraWQiOiJ0YS1rZX...n94DBmi3m158dbEO2ZWyM_9YdWtxjerAl1Zh-bgzA6H17tRGlk-oYd-7g"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add the &lt;code&gt;trustanchor.jwks&lt;/code&gt; to the &lt;code&gt;src/main/resources&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This simulated TA depends on your Connect2id server keys so you won't be able to reproduce it locally until we move to a real trust anchor. We will do this in a future article.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Let's log in
&lt;/h2&gt;

&lt;p&gt;Now we run our pac4j Spring Boot RP and our Connect2id OP server.&lt;/p&gt;

&lt;p&gt;Let's add more logs on the pac4j side via: &lt;code&gt;logging.level.org.pac4j.oidc=DEBUG&lt;/code&gt; (in the &lt;code&gt;application.properties&lt;/code&gt; file).&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;http://localhost:8081/&lt;/code&gt; URL in your browser and click on the &lt;code&gt;Protected area&lt;/code&gt; link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.pac4j.org%2Fimg%2Fblog%2F2026_04%2Fpac4j_protected.png" class="article-body-image-wrapper"&gt;&lt;img alt="pac4j welcome page" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.pac4j.org%2Fimg%2Fblog%2F2026_04%2Fpac4j_protected.png" width="180" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ta-da! It works: the Connect2id login page is displayed and we can log in with the default &lt;code&gt;alice&lt;/code&gt;/ &lt;code&gt;secret&lt;/code&gt; user/password:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.pac4j.org%2Fimg%2Fblog%2F2026_04%2Fc2id_login.png" class="article-body-image-wrapper"&gt;&lt;img alt="Connect2id login page" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.pac4j.org%2Fimg%2Fblog%2F2026_04%2Fc2id_login.png" width="657" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pac4j logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEBUG o.p.o.m.OidcFederationOpMetadataResolver : Blocking load of the provider metadata via federation
 INFO o.p.o.m.chain.FederationChainResolver    : Resolving federation chain for RP: http://localhost:8081
DEBUG o.p.o.m.chain.FederationChainResolver    : Loaded 1 trust anchor(s)
DEBUG o.p.o.m.chain.FederationChainResolver    : OP target issuer: http://127.0.0.1:8080/c2id
DEBUG o.p.o.m.chain.FederationChainResolver    : OP chain: com.nimbusds.openid.connect.sdk.federation.trust.TrustChain@14c16574
DEBUG o.p.o.m.chain.FederationChainResolver    : rawMetadataJson: {"request_parameter_supported":true,"pushed_authorization_...
DEBUG o.p.o.m.chain.FederationChainResolver    : combinedPolicy: {}
DEBUG o.p.o.m.chain.FederationChainResolver    : resolvedJson: {"request_parameter_supported":true,"pushed_authorization_...
DEBUG o.p.o.m.r.FederationClientRegister       : ClientID is not defined
DEBUG o.p.o.m.r.FederationClientRegister       : Automatic registration by OP (supported by RP) -&amp;gt;
setting clientId as entityId for further operation
DEBUG o.p.o.r.OidcRedirectionActionBuilder     : Algorithm used for request object signing: RS256
DEBUG o.p.o.r.OidcRedirectionActionBuilder     : Request Object claim names: [iss, aud, iat, exp, jti, scope,
response_type, redirect_uri, state, code_challenge_method, client_id, code_challenge, response_mode]
DEBUG o.p.o.r.OidcRedirectionActionBuilder     : Authz parameter names: [response_type, request, client_id, scope]
DEBUG o.p.o.r.OidcRedirectionActionBuilder     : Authentication request URL: http://127.0.0.1:8080/c2id-login?response_type=code
&amp;amp;request=eyJraW...KLjkrVw&amp;amp;client_id=http%3A%2F%2Flocalhost%3A8081&amp;amp;scope=openid%20profile%20email
 INFO .f.e.DefaultEntityConfigurationGenerator : Generating entity configuration for: http://localhost:8081
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Connect2id logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO FED-REG - [OP8025] Resolved 1 trust chains (automatic client):
INFO FED-REG - [OP8026] Trust chain [1] anchor (automatic client): http://localhost:8081/trustanchor
INFO FED-REG - [OP8026] Trust chain [1][1] entity (automatic client): http://localhost:8081
INFO FED-REG - [OP8026] Trust chain [1][1] statement (automatic client): {"sub":"http:\/\/localhost:8081","metadata":{"openid_relying_party":...
INFO FED-REG - [OP8026] Trust chain [1][2] entity (automatic client): http://localhost:8081
INFO FED-REG - [OP8026] Trust chain [1][2] statement (automatic client): {"sub":"http:\/\/localhost:8081","metadata":{"openid_relying_party":...
INFO FED-REG - [OP8013] Selected trust chain for entity http://localhost:8081 (automatic client) with anchor
http://localhost:8081/trustanchor and exp 2026-06-25T20:52:26.000+0200
INFO FED-REG - [OP8018] Received automatic registration request from http://localhost:8081 with authorities [http://localhost:8081/trustanchor]
INFO FED-REG - [OP8051] Effective metadata RP policy for entity http://localhost:8081: {}
INFO FED-REG - [OP8014] Registered entity http://localhost:8081 as automatic client with client_id=http://localhost:8081 exp=1782413546
INFO AUTHZ-SESSION - [OP2101] Created new auth session: sid=0r61vms5vYcodERRslQNMT0QqFRQsjw2Jr33YL4de5s client_id=http://localhost:8081 scope=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without going too deep in the logs, we can see that both sides are checking federation endpoints and entity statements to establish the trust chain.&lt;/p&gt;

&lt;p&gt;So here is the &lt;strong&gt;magic of federation&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The pac4j RP and the Connect2id OP only know and rely on the trust anchor, they don't know each other.&lt;br&gt;
But nonetheless the RP can perform the authentication process on the OP!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>security</category>
      <category>java</category>
    </item>
  </channel>
</rss>
