[java-idp-oidc] branch main updated: JOIDC-90 - Revocation of individual tokens

Henri Mikkonen henri.mikkonen at iki.fi
Tue Jun 7 11:57:40 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=e84914694a73b81d77d84f64d35398e6b5a88d91

The following commit(s) were added to refs/heads/main by this push:
     new e8491469 JOIDC-90 - Revocation of individual tokens
e8491469 is described below

commit e84914694a73b81d77d84f64d35398e6b5a88d91
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Jun 7 14:57:16 2022 +0300

    JOIDC-90 - Revocation of individual tokens
    
    https://shibboleth.atlassian.net/browse/JOIDC-90
    
    Improved flow tests.
---
 .../op/profile/flow/AbstractOidcApiFlowTest.java   | 32 +++++++++++++-
 .../oidc/op/profile/flow/RevocationFlowTest.java   | 49 ++++++++++++++++++++--
 .../src/test/resources/conf/relying-party.xml      |  1 +
 3 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java
index 7bec21a0..1b09f786 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcApiFlowTest.java
@@ -38,6 +38,7 @@ import com.nimbusds.jwt.SignedJWT;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
+import com.nimbusds.oauth2.sdk.token.RefreshToken;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
 
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
@@ -69,8 +70,15 @@ public class AbstractOidcApiFlowTest extends AbstractOidcFlowTest {
     protected BearerAccessToken buildToken(final String clientId, final String subject, final Scope scope,
             final ClaimsSet userInfoDeliverySet)
             throws URISyntaxException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+        return buildToken(clientId, subject, scope, userInfoDeliverySet, null, null);
+    }
+
+    protected BearerAccessToken buildToken(final String clientId, final String subject, final Scope scope,
+            final ClaimsSet userInfoDeliverySet, final String id, final String rootId)
+            throws URISyntaxException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+        final String jti = id == null ? idGenerator.generateIdentifier() : id;
         final TokenClaimsSet claims = new AccessTokenClaimsSet.Builder()
-                .setJWTID(idGenerator)
+                .setJWTID(jti)
                 .setClientID(new ClientID(clientId))
                 .setIssuer("https://op.example.org")
                 .setPrincipal("jdoe")
@@ -81,10 +89,32 @@ public class AbstractOidcApiFlowTest extends AbstractOidcFlowTest {
                 .setRedirectURI(new URI("https://example.org/cb"))
                 .setScope(scope)
                 .setDlClaimsUI(userInfoDeliverySet)
+                .setRootTokenIdentifier(rootId)
                 .build();
         return new BearerAccessToken(claims.serialize(BaseOIDCResponseActionTest.initializeDataSealer()));
     }
 
+    protected RefreshToken buildRefreshToken(final String clientId, final String subject, final Scope scope,
+            final ClaimsSet userInfoDeliverySet, final String id, final String rootId)
+            throws URISyntaxException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+        final String jti = id == null ? idGenerator.generateIdentifier() : id;
+        final TokenClaimsSet claims = new AccessTokenClaimsSet.Builder()
+                .setJWTID(jti)
+                .setClientID(new ClientID(clientId))
+                .setIssuer("https://op.example.org")
+                .setPrincipal("jdoe")
+                .setSubject(subject)
+                .setIssuedAt(Instant.now())
+                .setExpiresAt(Instant.now().plusSeconds(30))
+                .setAuthenticationTime(Instant.now())
+                .setRedirectURI(new URI("https://example.org/cb"))
+                .setScope(scope)
+                .setDlClaimsUI(userInfoDeliverySet)
+                .setRootTokenIdentifier(rootId)
+                .build();
+        return new RefreshToken(claims.serialize(BaseOIDCResponseActionTest.initializeDataSealer()));
+    }
+
     protected BearerAccessToken buildLegacyToken(final String clientId, final String subject, final Scope scope,
             final ClaimsSet userInfoDeliverySet, final String... consentedClaims)
             throws URISyntaxException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
index 8b2ce8e2..30aa859a 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
@@ -26,10 +26,12 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.opensaml.storage.RevocationCache;
 import org.opensaml.storage.StorageService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.Test;
 
@@ -43,6 +45,7 @@ import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
 import com.nimbusds.oauth2.sdk.id.ClientID;
 
 import net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.OAuth2RevocationSuccessResponse;
+import net.shibboleth.idp.plugin.oidc.op.storage.RevocationCacheContexts;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
 import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.utilities.java.support.collection.Pair;
@@ -57,7 +60,8 @@ import net.shibboleth.utilities.java.support.security.DataSealerException;
 public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest {
 
     public static final String FLOW_ID = "oauth2/revocation";
-    
+
+    String clientIdSingle = "mockClientIdRefreshTokenRotation";
 
     Scope scope = Scope.parse("openid profile email");
     
@@ -68,7 +72,11 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
     @Autowired
     @Qualifier("shibboleth.StorageService")
     StorageService storageService;
-    
+
+    @Autowired
+    @Qualifier("shibboleth.oidc.RevocationCache")
+    RevocationCache revocationCache;
+
     public RevocationFlowTest() {
         super(FLOW_ID);
     }
@@ -76,6 +84,7 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
     @AfterMethod
     public void tearDown() throws IOException {
         removeMetadata(storageService, clientId);
+        removeMetadata(storageService, clientIdSingle);
     }
 
     @Test
@@ -112,12 +121,46 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
     @Test
     public void testSuccess() throws IOException, NoSuchAlgorithmException, URISyntaxException,
         DataSealerException, ComponentInitializationException {
+        final String id = idGenerator.generateIdentifier();
+        final String rootId = idGenerator.generateIdentifier();
         setBasicAuth(clientId, clientSecret);
         storeMetadata(storageService, clientId, clientSecret, scope);
         setHttpFormRequest("POST", Collections.singletonMap("token", super.buildToken(clientId, "sub", 
-                Scope.parse("openid")).toJSONObject().getAsString("access_token")));
+                Scope.parse("openid"), null, id, rootId).toJSONObject().getAsString("access_token")));
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        parseSuccessResponse(result, OAuth2RevocationSuccessResponse.class);
+        Assert.assertFalse(revocationCache.isRevoked(RevocationCacheContexts.SINGLE_ACCESS_OR_REFRESH_TOKENS, id));
+        Assert.assertTrue(revocationCache.isRevoked(RevocationCacheContexts.AUTHORIZATION_CODE, rootId));
+    }
+
+    @Test
+    public void testSuccessSingleAccessToken() throws IOException, NoSuchAlgorithmException, URISyntaxException,
+        DataSealerException, ComponentInitializationException {
+        final String id = idGenerator.generateIdentifier();
+        final String rootId = idGenerator.generateIdentifier();
+        setBasicAuth(clientIdSingle, clientSecret);
+        storeMetadata(storageService, clientIdSingle, clientSecret, scope);
+        setHttpFormRequest("POST", Collections.singletonMap("token", super.buildToken(clientIdSingle, "sub", 
+                Scope.parse("openid"), null, id, rootId).toJSONObject().getAsString("access_token")));
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        parseSuccessResponse(result, OAuth2RevocationSuccessResponse.class);
+        Assert.assertTrue(revocationCache.isRevoked(RevocationCacheContexts.SINGLE_ACCESS_OR_REFRESH_TOKENS, id));
+        Assert.assertFalse(revocationCache.isRevoked(RevocationCacheContexts.AUTHORIZATION_CODE, rootId));
+    }
+
+    @Test
+    public void testSuccessSingleRefreshToken() throws IOException, NoSuchAlgorithmException, URISyntaxException,
+        DataSealerException, ComponentInitializationException {
+        final String id = idGenerator.generateIdentifier();
+        final String rootId = idGenerator.generateIdentifier();
+        setBasicAuth(clientIdSingle, clientSecret);
+        storeMetadata(storageService, clientIdSingle, clientSecret, scope);
+        setHttpFormRequest("POST", Collections.singletonMap("token", super.buildRefreshToken(clientIdSingle, "sub", 
+                Scope.parse("openid"), null, id, rootId).toJSONObject().getAsString("refresh_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         parseSuccessResponse(result, OAuth2RevocationSuccessResponse.class);
+        Assert.assertTrue(revocationCache.isRevoked(RevocationCacheContexts.SINGLE_ACCESS_OR_REFRESH_TOKENS, id));
+        Assert.assertFalse(revocationCache.isRevoked(RevocationCacheContexts.AUTHORIZATION_CODE, rootId));
     }
 
     @Test
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
index 7b4a256c..68e843f0 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
@@ -63,6 +63,7 @@
                  <list>
                      <ref bean="OIDC.SSO.MDDriven" />
                      <bean parent="OAUTH2.Token.MDDriven" p:enforceRefreshTokenRotation="true "/>
+                     <bean parent="OAUTH2.Revocation.MDDriven" p:revocationMethod="TOKEN" />
                  </list>
             </property>
         </bean>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list