[java-idp-plugin-webauthn] branch main updated: Fix message selection in WebAuthn error lookup function
Phil Smart
philip.smart at jisc.ac.uk
Fri Nov 29 17:21:57 UTC 2024
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-webauthn.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-webauthn.git;a=commit;h=9c958a1753fae897d1dc295bc32e7e3278a20c0d
The following commit(s) were added to refs/heads/main by this push:
new 9c958a1 Fix message selection in WebAuthn error lookup function
9c958a1 is described below
commit 9c958a1753fae897d1dc295bc32e7e3278a20c0d
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Nov 29 17:21:54 2024 +0000
Fix message selection in WebAuthn error lookup function
- Add customizable cleanup hook
- Add classified messages map
---
.../WebAuthnErrorMessageLookupFunction.java | 50 +++++-----------------
.../webauthn/impl/ValidateWebAuthnAssertion.java | 29 ++++++++++++-
.../idp/flows/authn/WebAuthn/webauthn-beans.xml | 6 ++-
.../idp/flows/authn/WebAuthn/webauthn-flow.xml | 9 +++-
.../authn/webauthn/conf/authn/webauthn-config.xml | 11 +++++
.../idp/plugin/authn/webauthn/messages.properties | 5 ++-
6 files changed, 65 insertions(+), 45 deletions(-)
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/WebAuthnErrorMessageLookupFunction.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/WebAuthnErrorMessageLookupFunction.java
index cd17a8e..8a7d504 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/WebAuthnErrorMessageLookupFunction.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/WebAuthnErrorMessageLookupFunction.java
@@ -14,22 +14,17 @@
package net.shibboleth.idp.plugin.authn.webauthn.context.navigate;
-import java.util.function.Function;
-
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
import org.opensaml.profile.context.ProfileRequestContext;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.context.support.MessageSourceAccessor;
+import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.context.AuthenticationErrorContext;
-import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
-import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationErrorContext;
-import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.StringSupport;
/**
@@ -44,27 +39,6 @@ public class WebAuthnErrorMessageLookupFunction extends ApplicationObjectSupport
/** Message ID to use for generic messages. */
private String genericMessageID;
- /** Lookup strategy to locate the WebAuthn registration information context. */
- @Nonnull
- private Function<ProfileRequestContext,WebAuthnRegistrationErrorContext> webauthnErrorContextLookupStrategy;
-
- /** Constructor.*/
- public WebAuthnErrorMessageLookupFunction() {
- webauthnErrorContextLookupStrategy = new ChildContextLookup<>(WebAuthnRegistrationErrorContext.class)
- .compose(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
- }
-
- /**
- * Set the strategy used to lookup the {@link WebAuthnRegistrationErrorContext}.
- *
- * @param strategy The strategy to set.
- */
- public void setWebauthnErrorContextLookupStrategy(
- final Function<ProfileRequestContext, WebAuthnRegistrationErrorContext> strategy) {
- webauthnErrorContextLookupStrategy = Constraint.isNotNull(strategy,
- "WebAuthnRegistrationErrorContextLookupStrategy can not be null");
- }
-
/**
* Sets whether non-message-based error messages should be exposed or turned into a more
* generic value.
@@ -103,24 +77,20 @@ public class WebAuthnErrorMessageLookupFunction extends ApplicationObjectSupport
* Get classified message.
*
* @param messageSource Spring message source
- * @param classifiedMessage classified message
+ * @param classifiedError classified error event
*
- * @return mapped message, or null if an empty string was produced.
+ * @return mapped message, or null
*/
@Nullable private String getClassifiedMessage(@Nonnull final MessageSourceAccessor messageSource,
- @Nonnull final String classifiedMessage) {
+ @Nonnull final String classifiedError) {
- String message = messageSource.getMessage(classifiedMessage, "");
- if (message.isEmpty()) {
- message = messageSource.getMessage( genericMessageID != null ? genericMessageID :
- "idp.webauthn.message", "WebAuthn Failure: "
- + classifiedMessage);
- }
- if (message.isEmpty()) {
- return null;
+ if (!AuthnEventIds.RESELECT_FLOW.equals(classifiedError)) {
+ final String eventKey = messageSource.getMessage(classifiedError,
+ genericMessageID != null ? genericMessageID : "authn");
+ return messageSource.getMessage(eventKey + ".message", "Login Failure: " + classifiedError);
}
- return message;
-
+
+ return null;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java
index 9a09c96..bced460 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java
@@ -14,6 +14,7 @@
package net.shibboleth.idp.plugin.authn.webauthn.impl;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -215,7 +216,8 @@ public class ValidateWebAuthnAssertion extends AbstractAuditingValidationAction
} catch (final AssertionFailureException e) {
log.warn("{} Error validating authenticator assertion for '{}'",
getLogPrefix(),context.getUsername() != null ? context.getUsername() : "unknown username", e);
- handleError(profileRequestContext, authenticationContext, e, AuthnEventIds.INVALID_CREDENTIALS);
+ handleError(profileRequestContext, authenticationContext, AuthnEventIds.INVALID_CREDENTIALS,
+ AuthnEventIds.INVALID_CREDENTIALS);
recordFailure(profileRequestContext);
return;
}
@@ -279,5 +281,30 @@ public class ValidateWebAuthnAssertion extends AbstractAuditingValidationAction
return subject;
}
}
+
+ /**
+ * A default cleanup hook that removes the {@link WebAuthnAuthenticationContext} from the tree.
+ */
+ public static class WebAuthnCleanupHook implements Consumer<ProfileRequestContext> {
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(final ProfileRequestContext input) {
+
+ final AuthenticationContext authnCtx =
+ input != null ? input.getSubcontext(AuthenticationContext.class) : null;
+ if (authnCtx == null) {
+ return;
+ }
+
+ final WebAuthnAuthenticationContext webAuthnCtx = authnCtx.getSubcontext(WebAuthnAuthenticationContext.class);
+ if (webAuthnCtx == null) {
+ return;
+ }
+ authnCtx.removeSubcontext(webAuthnCtx);
+
+ }
+
+ }
}
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
index 410e153..155aabe 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
@@ -160,7 +160,11 @@
p:credentialRepository="#{getObject('shibboleth.authn.WebAuthn.CredentialRepository') ?: getObject('shibboleth.authn.WebAuthn.DefaultCredentialRepository')}"
p:updateSignatureCountPredicate="#{getObject('shibboleth.authn.WebAuthn.UpdateSignatureCountPredicate') ?: %{idp.authn.webauthn.updateSignatureCount:true}}"
p:populateAuditContextAction="#{%{idp.authn.webauthn.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.WebAuthn.PopulateAuditContext') : null}"
- p:writeAuditLogAction="#{%{idp.authn.webauthn.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}"/>
+ p:writeAuditLogAction="#{%{idp.authn.webauthn.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}"
+ p:cleanupHook="#{getObject('shibboleth.authn.WebAuthn.RemoveAfterValidation') == true ? getObject('DefaultCleanupHook') : null}"
+ p:classifiedMessages="#{getObject('shibboleth.authn.WebAuthn.ClassifiedMessageMap')}"/>
+
+ <bean id="DefaultCleanupHook" class="net.shibboleth.idp.plugin.authn.webauthn.impl.ValidateWebAuthnAssertion.WebAuthnCleanupHook"/>
<!-- Error beans -->
<alias alias="WebAuthnErrorFunction" name="%{idp.authn.webauthn.errorMessageFunction:DefaultWebAuthnErrorFunction}" />
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml
index 330dd56..d5b6d37 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml
@@ -105,7 +105,10 @@
<transition on="proceed" to="GenerateAuthenticationCeremonyOptions" />
</action-state>
- <action-state id="GenerateAuthenticationCeremonyOptions">
+ <!--
+ Now the authentication options have been set, add them to the WebAuthn request options for the browser API.
+ -->
+ <action-state id="GenerateAuthenticationCeremonyOptions">
<evaluate expression="GenerateServerChallenge"/>
<evaluate expression="CreatePublicKeyCredentialRequestOptions"/>
<evaluate expression="'proceed'" />
@@ -146,7 +149,9 @@
<transition on="proceed" to="proceed" />
<!-- Import here we backtrack to a suitable action. For example, regenerate the challenge and options but
we do not need a new username input etc. -->
- <transition on="CredentialPolicyRejection" to="GenerateAuthenticationCeremonyOptions"/>
+ <transition on="CredentialPolicyRejection" to="GenerateAuthenticationCeremonyOptions"/>
+ <!-- The default invalid credentials transition would be InvalidCredentials, configuring this event instead allows control to be returned to the view -->
+ <transition on="InvalidWebAuthnCredential" to="GenerateAuthenticationCeremonyOptions"/>
</action-state>
<bean-import resource="webauthn-beans.xml" />
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
index 637576e..08c2bf4 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
@@ -20,5 +20,16 @@
p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators:null}"/>
</util:list>
+
+ <!-- Uncomment this map if you want control returned to the WebAuthn view if a credential fails to validate -->
+ <!--
+ <util:map id="shibboleth.authn.WebAuthn.ClassifiedMessageMap">
+ <entry key="InvalidWebAuthnCredential">
+ <list>
+ <value>InvalidCredentials</value>
+ </list>
+ </entry>
+ </util:map>
+ -->
</beans>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/messages.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/messages.properties
index bac27e9..a7fb47d 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/messages.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/messages.properties
@@ -65,4 +65,7 @@ ValidRegistration = Key was registered successfully
KeyRemoved = Key was removed successfully
# Messages to report back to the user during authentication
-CredentialPolicyRejection = Credential was rejected by policy
\ No newline at end of file
+InvalidWebAuthnCredential = authn
+CredentialPolicyRejection = webauthn-policy
+
+webauthn-policy.message = Credential was rejected by policy
\ 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