Skip to content

Commit 91e77cf

Browse files
authored
JCL-349: Better concurrency support for OpenID session refresh (#456)
1 parent 26fcbc8 commit 91e77cf

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

openid/src/main/java/com/inrupt/client/openid/OpenIdSession.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import java.util.UUID;
4141
import java.util.concurrent.CompletableFuture;
4242
import java.util.concurrent.CompletionStage;
43+
import java.util.concurrent.ForkJoinPool;
44+
import java.util.concurrent.ForkJoinTask;
4345
import java.util.concurrent.atomic.AtomicReference;
4446
import java.util.function.Supplier;
4547

@@ -54,19 +56,24 @@
5456
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
5557
import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver;
5658
import 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
*/
6266
public 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

Comments
 (0)