[java-idp-plugin-duo] branch master updated: Add test for forced authn with previous auth_time.
Phil Smart
philip.smart at jisc.ac.uk
Mon Aug 10 10:18:43 UTC 2020
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch master
in repository java-idp-plugin-duo.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-duo.git;a=commit;h=cd053659831f4482bde55722d75a9f89ba044a49
The following commit(s) were added to refs/heads/master by this push:
new cd05365 Add test for forced authn with previous auth_time.
cd05365 is described below
commit cd053659831f4482bde55722d75a9f89ba044a49
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Aug 10 11:18:37 2020 +0100
Add test for forced authn with previous auth_time.
---
.../impl/ValidateDuoTokenAuthenticationTime.java | 2 +-
.../authn/duo/impl/ValidateDuoTokenSubject.java | 3 +-
.../plugin/authn/duo/impl/DuoAuthnFlowTest.java | 143 +++++++++++++++++++++
.../plugin/authn/mock/MockDuoOIDCClient_OK.java | 3 +-
...ava => MockDuoOIDCClient_OK_OLD_AUTH_TIME.java} | 7 +-
.../authn/mock/MockDuoOIDCClient_UNKNOWN.java | 3 +-
6 files changed, 154 insertions(+), 7 deletions(-)
diff --git a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenAuthenticationTime.java b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenAuthenticationTime.java
index ff62ef1..7be6089 100644
--- a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenAuthenticationTime.java
+++ b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenAuthenticationTime.java
@@ -151,7 +151,7 @@ public class ValidateDuoTokenAuthenticationTime extends AbstractDuoAuthenticatio
// Check time of authentication has not expired
if (expiration.isBefore(now)) {
log.warn(
- "{} Authentication has expired: auth_time was '{}', expired at: '{}', current time: '{}'",
+ "{} Authentication required (forced) but has expired: auth_time was '{}', expired at: '{}', current time: '{}'",
getLogPrefix(), authTime, expiration, now);
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.AUTHN_EXCEPTION);
}
diff --git a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenSubject.java b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenSubject.java
index 1fd0cd5..b3648a6 100644
--- a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenSubject.java
+++ b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ValidateDuoTokenSubject.java
@@ -33,8 +33,7 @@ import net.shibboleth.idp.authn.context.AuthenticationContext;
/**
* An action that verifies the Subject (sub) claim in the Duo token matches that user which
- * is currently authenticating. This validation is suggested by the Duo Modern MFA developer
- * documentation.
+ * is currently authenticating.
*
* @pre <pre>
* ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java
index 9dd7584..d116228 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java
@@ -18,12 +18,14 @@
package net.shibboleth.idp.plugin.authn.duo.impl;
+import java.security.Principal;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import org.junit.Test;
+import org.opensaml.profile.context.EventContext;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,13 +37,18 @@ import org.springframework.webflow.execution.FlowExecution;
import net.shbboleth.idp.plugin.authn.duo.DefaultDuoOIDCIntegration;
import net.shbboleth.idp.plugin.authn.duo.DuoClientException;
import net.shbboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext;
+import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
+import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClientFactory_FAIL_Client;
import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClientFactory_OK_Client;
import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClient_OK;
+import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClient_OK_OLD_AUTH_TIME;
import net.shibboleth.idp.plugin.authn.mock.MockFlowBuilder;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
@@ -330,7 +337,143 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
assertEquals(prc.getSubcontext(SubjectCanonicalizationContext.class).getPrincipalName(),"jdoe");
}
+
+ /** Test the Duo flow from the external authorization request to the end of the flow with a requested
+ * principal set.
+ *
+ * @throws DuoClientException if the client can not be created.*/
+ @Test
+ public void testDuoAuthnFlowFromAuthorizationCallbackWithRPC() throws DuoClientException {
+
+ setFlowPath(FLOW);
+ setFlowModelResources(flowResources);
+ setSubflows(subflows);
+ setClientFactory(new MockDuoOIDCClientFactory_OK_Client());
+
+
+ final Map<String,String> mockProperties = Map.of(
+ "idp.duo.oidc.config.user","classpath:/conf/authn/duo-oidc-authn-config.xml");
+
+ setMockProperties(mockProperties);
+ final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+ .createFlowExecution(getFlowDefinition());
+ final ProfileRequestContext prc = buildProfileRequestContext(false,false);
+ //add a DuoContext
+ final DuoOIDCAuthenticationContext duoContext = new DuoOIDCAuthenticationContext();
+ final String nonce = DuoSupport.generateNonce(32);
+ duoContext.setAuthorizationCode("adummycode");
+ duoContext.setRequestState(nonce);
+ duoContext.setResponseState(nonce);
+ duoContext.setUsername("jdoe");
+
+ final DefaultDuoOIDCIntegration integ = new DefaultDuoOIDCIntegration();
+ integ.setClientId("DIU6GEFWG5LIUBVV2M3P");
+ integ.setAPIHost("api-c9f24c5a.duosecurity.com");
+ integ.setSecretKey("rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh");
+ integ.setRedirectURI("http://localhost/authorization-callback");
+
+ duoContext.setIntegration(integ);
+
+ //add the mock client as was not added by the populate stage
+ duoContext.setClient(new MockDuoOIDCClient_OK(duoContext.getIntegration()));
+
+ prc.getSubcontext(AuthenticationContext.class).addSubcontext(duoContext);
+
+ //add a requested principal context.
+ RequestedPrincipalContext rpc = new RequestedPrincipalContext();
+
+ List<Principal> requestedPrincipals = List.of(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"));
+ rpc.setRequestedPrincipals(requestedPrincipals);
+ rpc.setOperator("exact");
+ prc.getSubcontext(AuthenticationContext.class).addSubcontext(rpc);
+
+ //override the authflowdescriptor
+ AuthenticationFlowDescriptor afd = new AuthenticationFlowDescriptor();
+ afd.setId("authn/DuoOIDC");
+ afd.setSupportedPrincipals(List.of(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa")));
+ prc.getSubcontext(AuthenticationContext.class).setAttemptedFlow(afd);
+
+ flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+ updateFlowExecution(flowExecution);
+
+ //set start view and ending event to transition on.
+ externalContext.setEventId("proceed");
+ setCurrentState("Duo2FAAuthorizationRequest");
+ resumeFlow(externalContext);
+
+ //assert success conditions
+ assertFlowExecutionEnded();
+ assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+ assertNotNull(prc.getSubcontext(AuthenticationContext.class).
+ getSubcontext(DuoOIDCAuthenticationContext.class));
+ final DuoOIDCAuthenticationContext contextFromPrc = prc.getSubcontext(AuthenticationContext.class).
+ getSubcontext(DuoOIDCAuthenticationContext.class);
+ assertNotNull(contextFromPrc.getAuthToken());
+ assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class));
+ assertEquals(prc.getSubcontext(SubjectCanonicalizationContext.class).getPrincipalName(),"jdoe");
+
+ }
+
+ /**
+ * Test the Duo flow from the external authorization request. This should fail, as forced authn is
+ * requested by the auth_time is from a previous authentication (to far in the past).
+ *
+ * @throws DuoClientException if the client can not be created.*/
+ @Test
+ public void testDuoAuthnFlowFromAuthorizationCallbackForceAuthnFailure() throws DuoClientException {
+
+ setFlowPath(FLOW);
+ setFlowModelResources(flowResources);
+ setSubflows(subflows);
+ setClientFactory(new MockDuoOIDCClientFactory_OK_Client());
+
+
+ final Map<String,String> mockProperties = Map.of(
+ "idp.duo.oidc.config.user","classpath:/conf/authn/duo-oidc-authn-config.xml");
+
+ setMockProperties(mockProperties);
+
+ final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+ .createFlowExecution(getFlowDefinition());
+ //true for forced authn
+ final ProfileRequestContext prc = buildProfileRequestContext(true,false);
+ //add a DuoContext
+ final DuoOIDCAuthenticationContext duoContext = new DuoOIDCAuthenticationContext();
+ final String nonce = DuoSupport.generateNonce(32);
+ duoContext.setAuthorizationCode("adummycode");
+ duoContext.setRequestState(nonce);
+ duoContext.setResponseState(nonce);
+ duoContext.setUsername("jdoe");
+
+ final DefaultDuoOIDCIntegration integ = new DefaultDuoOIDCIntegration();
+ integ.setClientId("DIU6GEFWG5LIUBVV2M3P");
+ integ.setAPIHost("api-c9f24c5a.duosecurity.com");
+ integ.setSecretKey("rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh");
+ integ.setRedirectURI("http://localhost/authorization-callback");
+
+ duoContext.setIntegration(integ);
+
+ //add the mock client as was not added by the populate stage
+ duoContext.setClient(new MockDuoOIDCClient_OK_OLD_AUTH_TIME(duoContext.getIntegration()));
+
+ prc.getSubcontext(AuthenticationContext.class).addSubcontext(duoContext);
+ flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+ updateFlowExecution(flowExecution);
+
+ //set start view and ending event to transition on.
+ externalContext.setEventId("proceed");
+ setCurrentState("Duo2FAAuthorizationRequest");
+ resumeFlow(externalContext);
+
+ //assert success conditions
+ assertFlowExecutionEnded();
+ assertNotNull(prc.getSubcontext(EventContext.class));
+ assertNotNull(prc.getSubcontext(EventContext.class).getEvent());
+ assertTrue(prc.getSubcontext(EventContext.class).getEvent() instanceof String);
+ assertEquals(AuthnEventIds.AUTHN_EXCEPTION, prc.getSubcontext(EventContext.class).getEvent());
+
+ }
}
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK.java
index c78efc2..b9595ae 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK.java
@@ -97,7 +97,8 @@ public class MockDuoOIDCClient_OK implements DuoOIDCClient{
withFactor("duo_push")
.withUsername("jdoe")
.withUserKey("XXXXXXX")
- .build())
+ .build()).
+ withAuthTime(Math.toIntExact(Instant.now().getEpochSecond()))
.build();
}
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK_OLD_AUTH_TIME.java
similarity index 90%
copy from idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK.java
copy to idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK_OLD_AUTH_TIME.java
index c78efc2..f51e310 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_OK_OLD_AUTH_TIME.java
@@ -37,8 +37,10 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterI
/**
* Mock a Duo client which is available and returns a valid response.
+ * The token has an *old* auth_time to test a situation where a previous authentication
+ * result was used - which would prevent a forced authn from succeeding.
*/
-public class MockDuoOIDCClient_OK implements DuoOIDCClient{
+public class MockDuoOIDCClient_OK_OLD_AUTH_TIME implements DuoOIDCClient{
@Nonnull private static final String STATUS = "OK";
@@ -47,7 +49,7 @@ public class MockDuoOIDCClient_OK implements DuoOIDCClient{
@NonnullAfterInit private DuoOIDCIntegration integration;
- public MockDuoOIDCClient_OK(final @Nonnull DuoOIDCIntegration integ) throws DuoClientException {
+ public MockDuoOIDCClient_OK_OLD_AUTH_TIME(final @Nonnull DuoOIDCIntegration integ) throws DuoClientException {
integration = integ;
}
@@ -98,6 +100,7 @@ public class MockDuoOIDCClient_OK implements DuoOIDCClient{
.withUsername("jdoe")
.withUserKey("XXXXXXX")
.build())
+ .withAuthTime(Math.toIntExact(Instant.now().minus(10,ChronoUnit.MINUTES).getEpochSecond()))
.build();
}
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_UNKNOWN.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_UNKNOWN.java
index 445a68a..917b7ee 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_UNKNOWN.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/mock/MockDuoOIDCClient_UNKNOWN.java
@@ -93,7 +93,8 @@ public class MockDuoOIDCClient_UNKNOWN implements DuoOIDCClient{
withApplication(DuoApplication.builder()
.withKey("DIU6GEFXXXXXXX")
.withName("Test").build()).
- withFactor("duo_push").withUsername("jdoe").withUserKey("XXXXXXX").build()).build();
+ withFactor("duo_push").withUsername("jdoe").withUserKey("XXXXXXX").build()).
+ withAuthTime(Math.toIntExact(Instant.now().getEpochSecond())).build();
}
}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list