[java-idp-oidc] branch main updated: JOIDC-92 - Support for refresh token rotation
Henri Mikkonen
henri.mikkonen at iki.fi
Tue Jun 28 09:29:43 UTC 2022
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch main
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=7ea6ad22ced31ee4e94e8c3af4fc144f1eb22f56
The following commit(s) were added to refs/heads/main by this push:
new 7ea6ad22 JOIDC-92 - Support for refresh token rotation
7ea6ad22 is described below
commit 7ea6ad22ced31ee4e94e8c3af4fc144f1eb22f56
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Jun 28 12:28:37 2022 +0300
JOIDC-92 - Support for refresh token rotation
https://shibboleth.atlassian.net/browse/JOIDC-92
Exploit revocationLifetime from the revocation profile configuration whenever
revoking the full chain.
---
.../plugin/oidc/op/profile/impl/ValidateGrant.java | 55 ++++++++++++----------
.../oidc/op/profile/impl/ValidateGrantTest.java | 13 ++---
2 files changed, 38 insertions(+), 30 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java
index 38a83a8b..50d80b95 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java
@@ -46,7 +46,7 @@ import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.oidc.profile.config.logic.RefreshTokensEnabledPredicate;
-import net.shibboleth.oidc.profile.config.navigate.RefreshTokenLifetimeLookupFunction;
+import net.shibboleth.oidc.profile.config.navigate.RevocationLifetimeLookupFunction;
import net.shibboleth.oidc.profile.core.OidcEventIds;
import net.shibboleth.utilities.java.support.annotation.ParameterName;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
@@ -92,8 +92,8 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
/** Predicate used to indicate whether refresh tokens are enabled. */
@Nonnull private Predicate<ProfileRequestContext> refreshTokensEnabledPredicate;
- /** Strategy used to lookup the duration for lifetime of an entry in the token revocation cache. */
- @Nonnull private Function<ProfileRequestContext, Duration> tokenRevocationLifetimeLookupStrategy;
+ /** Lookup function to supply chain revocation lifetime. */
+ @Nonnull private Function<ProfileRequestContext,Duration> chainRevocationLifetimeLookupStrategy;
/** The RelyingPartyContext to operate on. */
@Nullable private RelyingPartyContext rpCtx;
@@ -107,7 +107,7 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
dataSealer = Constraint.isNotNull(sealer, "DataSealer cannot be null");
relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
refreshTokensEnabledPredicate = new RefreshTokensEnabledPredicate();
- tokenRevocationLifetimeLookupStrategy = new RefreshTokenLifetimeLookupFunction();
+ chainRevocationLifetimeLookupStrategy = new RevocationLifetimeLookupFunction();
}
/**
@@ -157,15 +157,13 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
}
/**
- * Set the strategy used to lookup the duration for lifetime of an entry in the token revocation cache.
+ * Set a lookup strategy for the chain revocation lifetime.
*
- * @param strategy The strategy to set.
+ * @param strategy What to set.
*/
- public void setTokenRevocationLifetimeLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, Duration> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- tokenRevocationLifetimeLookupStrategy = Constraint.isNotNull(strategy,
- "The lookup strategy cannot be null");
+ public void setChainRevocationLifetimeLookupStrategy(
+ @Nullable final Function<ProfileRequestContext,Duration> strategy) {
+ chainRevocationLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
}
/** {@inheritDoc} */
@@ -203,7 +201,7 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
final AuthorizationGrant grant = getTokenRequest().getAuthorizationGrant();
log.debug("{} Validating grant type: {}", getLogPrefix(),grant.getType());
-
+
TokenClaimsSet tokenClaimsSet = null;
if (GrantType.AUTHORIZATION_CODE.equals(grant.getType())) {
final AuthorizationCodeGrant codeGrant = (AuthorizationCodeGrant) grant;
@@ -215,8 +213,8 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
if (!replayCache.check(getClass().getName(), authzCodeClaimsSet.getID(),
authzCodeClaimsSet.getExp())) {
log.error("{} Replay detected of authz code {}", getLogPrefix(), authzCodeClaimsSet.getID());
- if (!revocationCache.revoke(RevocationCacheContexts.AUTHORIZATION_CODE,
- authzCodeClaimsSet.getID())) {
+ if (!revokeChain(authzCodeClaimsSet.getID(),
+ chainRevocationLifetimeLookupStrategy.apply(profileRequestContext))) {
log.warn("{} Fatal error, unable to save replayed code to revocation cache", getLogPrefix());
}
ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_GRANT);
@@ -259,16 +257,8 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
refreshTokenClaimsSet.getID())) {
log.error("{} The refresh token {} has been revoked. Revoking the full chain now.",
getLogPrefix(), refreshTokenClaimsSet.getID());
- final Duration lifetime =
- tokenRevocationLifetimeLookupStrategy.apply(profileRequestContext);
- if (lifetime == null) {
- log.error("{} Could not resolve the token revocation lifetime, full chain not revoked",
- getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
- return;
- }
- if (!revocationCache.revoke(RevocationCacheContexts.AUTHORIZATION_CODE, rootJtiToUse,
- lifetime)) {
+ if (!revokeChain(rootJtiToUse,
+ chainRevocationLifetimeLookupStrategy.apply(profileRequestContext))) {
log.error("{} Fatal error, unable to store revocation into the revocation cache",
getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
@@ -308,4 +298,21 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
}
// Checkstyle: CyclomaticComplexity|MethodLength|ReturnCount ON
+ /**
+ * Revokes the token chain with the given id, optionally with a given lifetime. If the given lifetime is null,
+ * the default lifetime set to the {@link RevocationCache} is used.
+ *
+ * @param id The identifier to be revoked in {@link RevocationCacheContexts#AUTHORIZATION_CODE} context.
+ * @param lifetime The lifetime for the revocation
+ * @return The result returned by the {@link RevocationCache}
+ */
+ protected boolean revokeChain(@Nonnull final String id, @Nullable final Duration lifetime) {
+ if (lifetime == null) {
+ log.warn("{} No profile-specific revocation lifetime could be resolved, using default value",
+ getLogPrefix());
+ return revocationCache.revoke(RevocationCacheContexts.AUTHORIZATION_CODE, id);
+ }
+ return revocationCache.revoke(RevocationCacheContexts.AUTHORIZATION_CODE, id, lifetime);
+ }
+
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java
index 6fd39c12..76e91a6e 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java
@@ -36,6 +36,7 @@ import java.time.Duration;
import java.time.Instant;
import java.util.function.Function;
+import org.mockito.Mockito;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.storage.ReplayCache;
import org.opensaml.storage.RevocationCache;
@@ -122,7 +123,7 @@ public class ValidateGrantTest extends BaseOIDCResponseActionTest {
profileRequestCtx.getInboundMessageContext().setMessage(req);
action = new ValidateGrant(getDataSealer());
if (revocationLifetimeLookup != null) {
- action.setTokenRevocationLifetimeLookupStrategy(revocationLifetimeLookup);
+ action.setChainRevocationLifetimeLookupStrategy(revocationLifetimeLookup);
}
action.setRevocationCache(revocationCache);
final ReplayCache replayCache = new ReplayCache();
@@ -277,12 +278,12 @@ public class ValidateGrantTest extends BaseOIDCResponseActionTest {
}
@Test
- public void testRefreshTokenRevokedLifetimeRequired() throws Exception {
- final RevocationCache revocationCache = new RevocationCache();
- revocationCache.setStorage(storageService);
+ public void testTokenRevocationViaRevokedTokenFailsReturnsInvalidProfileConfig() throws Exception {
+ RevocationCache revocationCache = Mockito.mock(RevocationCache.class);
+ Mockito.when(revocationCache.isRevoked(Mockito.matches(RevocationCacheContexts.AUTHORIZATION_CODE), Mockito.anyString())).thenReturn(false);
+ Mockito.when(revocationCache.isRevoked(Mockito.matches(RevocationCacheContexts.SINGLE_ACCESS_OR_REFRESH_TOKENS), Mockito.anyString())).thenReturn(true);
+ Mockito.when(revocationCache.revoke(Mockito.anyString(), Mockito.anyString())).thenReturn(false);
init(true, revocationCache, prc -> null);
- Assert.assertTrue(revocationCache.revoke(RevocationCacheContexts.SINGLE_ACCESS_OR_REFRESH_TOKENS,
- rfClaims.getID()));
final TokenRequest req = new TokenRequest(callback, new ClientID(clientId), rfGrant);
profileRequestCtx.getInboundMessageContext().setMessage(req);
ActionTestingSupport.assertEvent(action.execute(requestCtx), IdPEventIds.INVALID_PROFILE_CONFIG);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list