4040import java .util .UUID ;
4141import java .util .concurrent .CompletableFuture ;
4242import java .util .concurrent .CompletionStage ;
43+ import java .util .concurrent .ForkJoinPool ;
44+ import java .util .concurrent .ForkJoinTask ;
4345import java .util .concurrent .atomic .AtomicReference ;
4446import java .util .function .Supplier ;
4547
5456import org .jose4j .jwt .consumer .JwtConsumerBuilder ;
5557import org .jose4j .keys .resolvers .HttpsJwksVerificationKeyResolver ;
5658import org .jose4j .keys .resolvers .VerificationKeyResolver ;
59+ import org .slf4j .Logger ;
60+ import org .slf4j .LoggerFactory ;
5761
5862/**
5963 * A session implementation for use with OpenID Connect ID Tokens.
6064 *
6165 */
6266public final class OpenIdSession implements Session {
6367
68+ private static final Logger LOGGER = LoggerFactory .getLogger (OpenIdSession .class );
69+
6470 public static final URI ID_TOKEN = URI .create ("http://openid.net/specs/openid-connect-core-1_0.html#IDToken" );
6571
6672 private final String id ;
6773 private final Set <String > schemes ;
6874 private final Supplier <CompletionStage <Credential >> authenticator ;
6975 private final AtomicReference <Credential > credential = new AtomicReference <>();
76+ private final ForkJoinPool executor = new ForkJoinPool (1 );
7077 private final DPoP dpop ;
7178
7279 private OpenIdSession (final String id , final DPoP dpop ,
@@ -182,15 +189,11 @@ public Set<String> supportedSchemes() {
182189 @ Override
183190 public Optional <Credential > getCredential (final URI name , final URI uri ) {
184191 if (ID_TOKEN .equals (name )) {
185- final Credential c = credential .get ();
186- if (!hasExpired (c )) {
187- return Optional .of (c );
188- }
189- final Credential freshCredential = authenticator .get ().toCompletableFuture ().join ();
190- if (!hasExpired (freshCredential )) {
191- credential .set (freshCredential );
192- return Optional .of (freshCredential );
192+ final Credential cred = credential .get ();
193+ if (!hasExpired (cred )) {
194+ return Optional .of (cred );
193195 }
196+ return Optional .ofNullable (executor .invoke (ForkJoinTask .adapt (this ::synchronizedFetch )));
194197 }
195198 return Optional .empty ();
196199 }
@@ -222,7 +225,7 @@ public Optional<Credential> fromCache(final Request request) {
222225 @ Override
223226 public CompletionStage <Optional <Credential >> authenticate (final Request request ,
224227 final Set <String > algorithms ) {
225- return authenticator . get (). thenApply ( Optional :: ofNullable );
228+ return CompletableFuture . completedFuture ( getCredential ( ID_TOKEN , null ) );
226229 }
227230
228231 boolean hasExpired (final Credential credential ) {
@@ -232,6 +235,22 @@ boolean hasExpired(final Credential credential) {
232235 return true ;
233236 }
234237
238+ private synchronized Credential synchronizedFetch () {
239+ // Check again inside the synchronized method
240+ final Credential cred = credential .get ();
241+ if (!hasExpired (cred )) {
242+ return cred ;
243+ }
244+
245+ // Fetch the refreshed credentials
246+ final Credential refreshed = authenticator .get ().toCompletableFuture ().join ();
247+ if (!hasExpired (refreshed )) {
248+ credential .set (refreshed );
249+ return refreshed ;
250+ }
251+ return null ;
252+ }
253+
235254 static String getSessionIdentifier (final JwtClaims claims ) {
236255 final String webid = claims .getClaimValueAsString ("webid" );
237256 if (webid != null ) {
0 commit comments