[java-identity-provider] branch master updated: IDP-1239 - Non-browser support for Duo authentication
Scott Cantor
cantor.2 at osu.edu
Wed Aug 8 19:30:28 EDT 2018
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=a0beaa8f4da2d55188f2e9b07104f4f92163e78f
The following commit(s) were added to refs/heads/master by this push:
new a0beaa8 IDP-1239 - Non-browser support for Duo authentication
a0beaa8 is described below
commit a0beaa8f4da2d55188f2e9b07104f4f92163e78f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Aug 8 19:30:25 2018 -0400
IDP-1239 - Non-browser support for Duo authentication
https://issues.shibboleth.net/jira/browse/IDP-1239
Add client address, device ID validation.
---
.../net/shibboleth/idp/authn/duo/DuoAuthAPI.java | 11 +++++++-
.../duo/context/DuoAuthenticationContext.java | 24 ++++++++++++++++++
.../idp/authn/duo/impl/DuoAuthAPIResponse.java | 21 ----------------
.../idp/authn/duo/impl/DuoAuthAuthenticator.java | 5 +++-
.../idp/authn/duo/impl/DuoAuthResponse.java | 20 ---------------
.../authn/duo/impl/DuoPreauthAuthenticator.java | 8 +++++-
.../idp/authn/duo/impl/DuoPreauthResponse.java | 20 ---------------
.../impl/ExtractDuoAuthenticationFromHeaders.java | 29 ++++++++++++++++++++++
.../idp/authn/duo/impl/ValidateDuoAuthAPI.java | 27 +++++++++++++++++---
9 files changed, 98 insertions(+), 67 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java
index 73ba8b6..eb22e80 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java
@@ -29,6 +29,12 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
public final class DuoAuthAPI {
/** Duo AuthAPI parameter name. */
+ @Nonnull @NotEmpty public static final String DUO_USERNAME = "username";
+
+ /** Duo AuthAPI parameter name. */
+ @Nonnull @NotEmpty public static final String DUO_IPADDR = "ipaddr";
+
+ /** Duo AuthAPI parameter name. */
@Nonnull @NotEmpty public static final String DUO_FACTOR = "factor";
/** Duo AuthAPI parameter name. */
@@ -36,7 +42,7 @@ public final class DuoAuthAPI {
/** Duo AuthAPI parameter name. */
@Nonnull @NotEmpty public static final String DUO_PASSCODE = "passcode";
-
+
/** Duo AuthAPI factor "auto" value. */
@Nonnull @NotEmpty public static final String DUO_FACTOR_AUTO = "auto";
@@ -76,6 +82,9 @@ public final class DuoAuthAPI {
/** Duo AuthAPI auth "bypass" result value. */
@Nonnull @NotEmpty public static final String DUO_AUTH_STATUS_BYPASS = "bypass";
+ /** Duo AuthAPI auth "locked_out" result value. */
+ @Nonnull @NotEmpty public static final String DUO_AUTH_STATUS_LOCKED = "locked_out";
+
/** Duo flow default header name for factor. */
@Nonnull @NotEmpty public static final String DUO_FACTOR_HEADER_NAME = "X-Shibboleth-Duo-Factor";
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java
index d968b58..696a82b 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java
@@ -38,6 +38,9 @@ public class DuoAuthenticationContext extends BaseContext {
/** Username. */
@Nullable private String username;
+ /** Client address. */
+ @Nullable private String clientAddress;
+
/** Factor. */
@Nullable private String duoFactor;
@@ -67,8 +70,29 @@ public class DuoAuthenticationContext extends BaseContext {
username = name;
return this;
}
+
+ /**
+ * Get the client address.
+ *
+ * @return address
+ */
+ @Nullable public String getClientAddress() {
+ return clientAddress;
+ }
/**
+ * Set the client address.
+ *
+ * @param address client address
+ *
+ * @return this context
+ */
+ @Nonnull public DuoAuthenticationContext setClientAddress(@Nullable final String address) {
+ clientAddress = address;
+ return this;
+ }
+
+ /**
* Get the device ID.
*
* @return the Duo device identifier
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAPIResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAPIResponse.java
index a053e5c..7f4195a 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAPIResponse.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAPIResponse.java
@@ -56,25 +56,4 @@ public abstract class DuoAuthAPIResponse {
return statusMessage;
}
- /**
- * Check if the result of the response is allow state.
- *
- * @return true if result of the response is allow state
- */
- public abstract boolean isAllow();
-
- /**
- * Check if the result of the response is deny state.
- *
- * @return true if result of the response is deny state
- */
- public abstract boolean isDeny();
-
- /**
- * Check if the status of the response is bypass state.
- *
- * @return true if status of the response is bypass state
- */
- public abstract boolean isBypass();
-
}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java
index 95c5f86..04523ca 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java
@@ -67,7 +67,10 @@ public class DuoAuthAuthenticator extends AbstractDuoAuthenticator {
final URI uri = new URIBuilder().setScheme("https").setHost(duoIntegration.getAPIHost())
.setPath("/auth/v2/auth").build();
final RequestBuilder rb =
- RequestBuilder.post().setUri(uri).addParameter("username", duoContext.getUsername());
+ RequestBuilder.post().setUri(uri).addParameter(DuoAuthAPI.DUO_USERNAME, duoContext.getUsername());
+ if (duoContext.getClientAddress() != null) {
+ rb.addParameter(DuoAuthAPI.DUO_IPADDR, duoContext.getClientAddress());
+ }
if (duoContext.getFactor() != null) {
rb.addParameter(DuoAuthAPI.DUO_FACTOR, duoContext.getFactor());
}
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthResponse.java
index d7a4510..8a14dce 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthResponse.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthResponse.java
@@ -23,8 +23,6 @@ import javax.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import net.shibboleth.idp.authn.duo.DuoAuthAPI;
-
/**
* Describes the results of an authentication attempt via the Duo AuthAPI, intended for use with a jackson
* {@link ObjectMapper}.
@@ -56,22 +54,4 @@ public class DuoAuthResponse extends DuoAuthAPIResponse {
return trustedDeviceToken;
}
- /** {@inheritDoc} */
- @Override
- public boolean isAllow() {
- return getResult().equals(DuoAuthAPI.DUO_AUTH_RESULT_ALLOW);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isDeny() {
- return getResult().equals(DuoAuthAPI.DUO_AUTH_RESULT_DENY);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isBypass() {
- return getStatus().equals(DuoAuthAPI.DUO_AUTH_STATUS_BYPASS);
- }
-
}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthAuthenticator.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthAuthenticator.java
index 29982b6..060d8d6 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthAuthenticator.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthAuthenticator.java
@@ -33,6 +33,7 @@ import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.utils.URIBuilder;
+import net.shibboleth.idp.authn.duo.DuoAuthAPI;
import net.shibboleth.idp.authn.duo.DuoIntegration;
import net.shibboleth.idp.authn.duo.context.DuoAuthenticationContext;
@@ -66,7 +67,12 @@ public class DuoPreauthAuthenticator extends AbstractDuoAuthenticator {
final URI uri = new URIBuilder().setScheme("https").setHost(duoIntegration.getAPIHost())
.setPath("/auth/v2/preauth").build();
final RequestBuilder rb =
- RequestBuilder.post().setUri(uri).addParameter("username", duoContext.getUsername());
+ RequestBuilder.post().setUri(uri).addParameter(DuoAuthAPI.DUO_USERNAME, duoContext.getUsername());
+
+ if (duoContext.getClientAddress() != null) {
+ rb.addParameter(DuoAuthAPI.DUO_IPADDR, duoContext.getClientAddress());
+ }
+
DuoSupport.signRequest(rb, duoIntegration);
final HttpUriRequest request = rb.build();
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthResponse.java
index 3c349f9..75b77bf 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthResponse.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthResponse.java
@@ -27,8 +27,6 @@ import javax.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import net.shibboleth.idp.authn.duo.DuoAuthAPI;
-
/**
* Describes the results of an pre-authentication attempt via the Duo AuthAPI.
*/
@@ -59,22 +57,4 @@ public class DuoPreauthResponse extends DuoAuthAPIResponse {
return enrollPortalURL;
}
- /** {@inheritDoc} */
- @Override
- public boolean isAllow() {
- return getResult().equals(DuoAuthAPI.DUO_AUTH_RESULT_ALLOW);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isDeny() {
- return getResult().equals(DuoAuthAPI.DUO_AUTH_RESULT_DENY);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isBypass() {
- return getResult().equals(DuoAuthAPI.DUO_PREAUTH_RESULT_ALLOW);
- }
-
}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java
index 7a91ec7..5ad90dd 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java
@@ -61,6 +61,9 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
/** Whether "auto" should be the default for factor and device. */
private boolean autoAuthenticationSupported;
+
+ /** Whether to trust, and extract, the client address. */
+ private boolean clientAddressTrusted;
/** Header name for factor. */
@Nonnull @NotEmpty private String factorHeaderName;
@@ -74,6 +77,7 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
/** Constructor. */
ExtractDuoAuthenticationFromHeaders() {
autoAuthenticationSupported = true;
+ clientAddressTrusted = true;
factorHeaderName = DuoAuthAPI.DUO_FACTOR_HEADER_NAME;
deviceHeaderName = DuoAuthAPI.DUO_DEVICE_HEADER_NAME;
@@ -117,6 +121,26 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
}
/**
+ * Get whether the client address should be trusted for use in API calls.
+ *
+ * @return whether client address should be trusted
+ */
+ public boolean isClientAddressTrusted() {
+ return clientAddressTrusted;
+ }
+
+ /**
+ * Set whether the client address should be trusted for use in API calls.
+ *
+ * @param flag flag to set
+ */
+ public void setClientAdddressTrusted(final boolean flag) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ clientAddressTrusted = flag;
+ }
+
+ /**
* Get whether "auto" is the default setting.
*
* @return whether "auto" is the default setting
@@ -154,6 +178,7 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
log.debug("{} Checking for Duo authentication headers", getLogPrefix());
final DuoAuthenticationContext duoCtx = new DuoAuthenticationContext();
+
extractHeaders(duoCtx);
if (duoCtx.getFactor() == null) {
@@ -198,6 +223,10 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
return;
}
+ if (clientAddressTrusted) {
+ context.setClientAddress(httpRequest.getRemoteAddr());
+ }
+
final String factor = httpRequest.getHeader(factorHeaderName);
if (factor != null && !factor.isEmpty()) {
context.setFactor(factor);
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoAuthAPI.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoAuthAPI.java
index 6aac899..ba47950 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoAuthAPI.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoAuthAPI.java
@@ -55,8 +55,10 @@ import com.duosecurity.duoweb.DuoWebException;
*
* @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
* @event {@link AuthnEventIds#AUTHN_EXCEPTION}
+ * @event {@link AuthnEventIds#ACCOUNT_LOCKED}
* @event {@link AuthnEventIds#ACCOUNT_WARNING}
* @event {@link AuthnEventIds#ACCOUNT_ERROR}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
* @event {@link AuthnEventIds#INVALID_CREDENTIALS}
* @pre
*
@@ -219,7 +221,7 @@ public class ValidateDuoAuthAPI extends AbstractValidationAction {
}
/** {@inheritDoc} */
- // CheckStyle: ReturnCount OFF
+ // CheckStyle: CyclomaticComplexity|MethodLength|ReturnCount OFF
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext) {
@@ -253,6 +255,25 @@ public class ValidateDuoAuthAPI extends AbstractValidationAction {
recordFailure();
return;
}
+
+ // Validate device ID specified against the enrolled set.
+ if (duoContext.getDeviceID() != null && !DuoAuthAPI.DUO_DEVICE_AUTO.equals(duoContext.getDeviceID())) {
+ boolean found = false;
+ for (final DuoDevice device : preAuthResponse.getDevices()) {
+ if (duoContext.getDeviceID().equals(device.getDevice())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ log.info("{} Request specified non-existent device ID ({}) for '{}': {}", getLogPrefix(),
+ duoContext.getDeviceID(), username, preAuthResponse.getStatusMessage());
+ handleError(profileRequestContext, authenticationContext, AuthnEventIds.INVALID_CREDENTIALS,
+ AuthnEventIds.INVALID_CREDENTIALS);
+ recordFailure();
+ return;
+ }
+ }
// Duo AuthAPI authentication
final DuoAuthResponse authenticationResponse = authAuthenticator.authenticate(duoContext, duoIntegration);
@@ -268,7 +289,7 @@ public class ValidateDuoAuthAPI extends AbstractValidationAction {
buildAuthenticationResult(profileRequestContext, authenticationContext);
} else if (DuoAuthAPI.DUO_AUTH_RESULT_DENY.equals(authResult)) {
log.info("{} Duo authentication failed for '{}'", getLogPrefix(), username);
- handleError(profileRequestContext, authenticationContext, AuthnEventIds.INVALID_CREDENTIALS,
+ handleError(profileRequestContext, authenticationContext, authenticationResponse.getStatus(),
AuthnEventIds.INVALID_CREDENTIALS);
recordFailure();
} else {
@@ -280,7 +301,7 @@ public class ValidateDuoAuthAPI extends AbstractValidationAction {
recordFailure();
}
}
- // CheckStyle: ReturnCount OFF
+ // CheckStyle: CyclomaticComplexity|MethodLength|ReturnCount OFF
/** {@inheritDoc} */
@Override protected Subject populateSubject(@Nonnull final Subject subject) {
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list