[java-idp-plugin-webauthn] branch main updated: Externalise registration messages
Phil Smart
philip.smart at jisc.ac.uk
Fri Jun 7 14:49:37 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=8ca5c5ee2633c091260fd395e442eed9a376939d
The following commit(s) were added to refs/heads/main by this push:
new 8ca5c5e Externalise registration messages
8ca5c5e is described below
commit 8ca5c5ee2633c091260fd395e442eed9a376939d
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jun 7 15:49:33 2024 +0100
Externalise registration messages
- Use new error and info message contexts to store information for the
views to display e.g. key registered, key removed etc.
- Add functions to retrieve that information using the
MessageSourceAccessor (similar to the normal login view of the password
flow).
- Add new info and error functions to views
---
.../context/WebAuthnRegistrationErrorContext.java | 86 ++++++++++++++
.../WebAuthnRegistrationInformationContext.java | 86 ++++++++++++++
.../RegistrationErrorMessageLookupFunction.java | 122 ++++++++++++++++++++
.../RegistrationInfoMessageLookupFunction.java | 124 +++++++++++++++++++++
.../ValidateAuthenticatorAttestationResponse.java | 5 +
.../webauthn-management-beans.xml | 28 ++++-
.../webauthn-management-flow.xml | 5 +-
.../webauthn-registration-beans.xml | 9 ++
.../webauthn-registration-flow.xml | 13 +--
.../authn/webauthn/conf/authn/webauthn.properties | 8 ++
.../idp/plugin/authn/webauthn/messages.properties | 7 +-
.../plugin/authn/webauthn/views/webauthn-end.vm | 2 -
.../webauthn/views/webauthn-management-search.vm | 2 -
.../authn/webauthn/views/webauthn-management.vm | 15 ++-
.../authn/webauthn/views/webauthn-register.vm | 20 ++--
15 files changed, 501 insertions(+), 31 deletions(-)
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationErrorContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationErrorContext.java
new file mode 100644
index 0000000..fbd1ca3
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationErrorContext.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.webauthn.context;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationErrorContext;
+import net.shibboleth.shared.annotation.constraint.Live;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/**
+ * A WebAuthn version of {@link AuthenticationErrorContext} that holds information about WebAuthn registration events.
+ * For example, if a new credential registration is invalid.
+ */
+public class WebAuthnRegistrationErrorContext extends BaseContext {
+
+ /** Error conditions detected through classified error messages. */
+ @Nonnull private final Collection<String> classifiedErrors;
+
+ /** Constructor. */
+ public WebAuthnRegistrationErrorContext() {
+ classifiedErrors = new LinkedHashSet<>();
+ }
+
+ /**
+ * Get a mutable collection of error "tokens" associated with the context.
+ *
+ * @return mutable collection of error strings
+ */
+ @Nonnull @Live public Collection<String> getClassifiedErrors() {
+ return classifiedErrors;
+ }
+
+ /**
+ * Check for the presence of a particular error condition in the context.
+ *
+ * @param error the condition to check for
+ * @return true iff the context contains the error condition specified
+ */
+ public boolean isClassifiedError(@Nonnull @NotEmpty final String error) {
+ return classifiedErrors.contains(error);
+ }
+
+ /**
+ * Adds a classified error to the context, ensuring that it will be returned
+ * from {@link #getLastClassifiedError()} until another is added.
+ *
+ * @param error error to add
+ *
+ * @return this context
+ */
+ @Nonnull public WebAuthnRegistrationErrorContext addClassifiedError(@Nonnull @NotEmpty final String error) {
+ // This is done to preserve ordering so that the error is the "last one added".
+ classifiedErrors.remove(error);
+ classifiedErrors.add(error);
+ return this;
+ }
+
+ /**
+ * Gets the last classified error added, or null if none.
+ *
+ * @return last error added or null
+ */
+ @Nullable public String getLastClassifiedError() {
+ return classifiedErrors.stream().reduce((first, second) -> second).orElse(null);
+ }
+
+}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationInformationContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationInformationContext.java
new file mode 100644
index 0000000..43cf9e5
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationInformationContext.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.webauthn.context;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationWarningContext;
+import net.shibboleth.shared.annotation.constraint.Live;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/**
+ * A WebAuthn version of {@link AuthenticationWarningContext} that holds information about WebAuthn registration events.
+ * For example, if a new credential has been added or an old credential has been removed.
+ */
+public class WebAuthnRegistrationInformationContext extends BaseContext {
+
+ /** Event conditions detected through classified messages. */
+ @Nonnull private final Collection<String> classifiedMessages;
+
+ /** Constructor. */
+ public WebAuthnRegistrationInformationContext() {
+ classifiedMessages = new LinkedHashSet<>();
+ }
+
+ /**
+ * Get a mutable collection of message "tokens" associated with the context.
+ *
+ * @return mutable collection of message strings
+ */
+ @Nonnull @Live public Collection<String> getClassifiedMessages() {
+ return classifiedMessages;
+ }
+
+ /**
+ * Check for the presence of a particular message condition in the context.
+ *
+ * @param msg the condition to check for
+ * @return true iff the context contains the msg condition specified
+ */
+ public boolean isClassifiedMessage(@Nonnull @NotEmpty final String msg) {
+ return classifiedMessages.contains(msg);
+ }
+
+ /**
+ * Adds a classified message to the context, ensuring that it will be returned
+ * from {@link #getClassifiedMessages()} until another is added.
+ *
+ * @param msg message to add
+ *
+ * @return this context
+ */
+ @Nonnull public WebAuthnRegistrationInformationContext addClassifiedMessage(@Nonnull @NotEmpty final String msg) {
+ // This is done to preserve ordering so that the message is the "last one added".
+ classifiedMessages.remove(msg);
+ classifiedMessages.add(msg);
+ return this;
+ }
+
+ /**
+ * Gets the last classified message added, or null if none.
+ *
+ * @return last message added or null
+ */
+ @Nullable public String getLastClassifiedMessage() {
+ return classifiedMessages.stream().reduce((first, second) -> second).orElse(null);
+ }
+
+}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/RegistrationErrorMessageLookupFunction.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/RegistrationErrorMessageLookupFunction.java
new file mode 100644
index 0000000..e67a6d6
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/RegistrationErrorMessageLookupFunction.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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.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;
+
+/**
+ * A function that examines the state of a request and produces an appropriate message for WebAuthn flow views.
+ *
+ * <p>NOTE: The result of this function is <strong>NOT</strong> HTML-encoded in any way and must
+ * be encoded for safety if used.</p>
+ */
+public class RegistrationErrorMessageLookupFunction extends ApplicationObjectSupport
+ implements ContextDataLookupFunction<ProfileRequestContext,String>{
+
+ /** 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;
+
+
+ public RegistrationErrorMessageLookupFunction() {
+ 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.
+ *
+ * @param id message ID
+ */
+ public void setGenericMessageID(@Nullable final String id) {
+ genericMessageID = StringSupport.trimOrNull(id);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String apply(@Nullable final ProfileRequestContext input) {
+
+ final MessageSourceAccessor messageSource = getMessageSourceAccessor();
+ if (messageSource == null) {
+ return null;
+ }
+
+ final WebAuthnRegistrationErrorContext regErrorCtx = webauthnErrorContextLookupStrategy.apply(input);
+ if (regErrorCtx == null) {
+ return null;
+ }
+
+ final String classifiedError = regErrorCtx.getLastClassifiedError();
+ if (classifiedError != null && !classifiedError.isEmpty()) {
+ return getClassifiedMessage(messageSource, classifiedError);
+ }
+ return null;
+ }
+
+ /**
+ * Get classified message.
+ *
+ * @param messageSource Spring message source
+ * @param classifiedMessage classified message
+ *
+ * @return mapped message, or null if an empty string was produced.
+ */
+ @Nullable private String getClassifiedMessage(@Nonnull final MessageSourceAccessor messageSource,
+ @Nonnull final String classifiedMessage) {
+
+ String message = messageSource.getMessage(classifiedMessage, "");
+ if (message.isEmpty()) {
+ message = messageSource.getMessage( genericMessageID != null ? genericMessageID :
+ "idp.webauthn.register.message", "Registration result: "
+ + classifiedMessage);
+ }
+ if (message.isEmpty()) {
+ return null;
+ }
+ return message;
+
+ }
+
+
+}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/RegistrationInfoMessageLookupFunction.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/RegistrationInfoMessageLookupFunction.java
new file mode 100644
index 0000000..d6edd16
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/RegistrationInfoMessageLookupFunction.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.webauthn.context.navigate;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+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.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationInformationContext;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * A function that examines the state of a request and produces an appropriate message for WebAuthn flow views.
+ *
+ * <p>NOTE: The result of this function is <strong>NOT</strong> HTML-encoded in any way and must
+ * be encoded for safety if used.</p>
+ */
+ at NotThreadSafe
+public class RegistrationInfoMessageLookupFunction extends ApplicationObjectSupport
+ implements ContextDataLookupFunction<ProfileRequestContext,String>{
+
+ /** Message ID to use for generic messages. */
+ private String genericMessageID;
+
+ /** Lookup strategy to locate the WebAuthn registration information context. */
+ @Nonnull
+ private Function<ProfileRequestContext,WebAuthnRegistrationInformationContext> webauthnInfoContextLookupStrategy;
+
+ /** Constructor.*/
+ public RegistrationInfoMessageLookupFunction() {
+ webauthnInfoContextLookupStrategy = new ChildContextLookup<>(WebAuthnRegistrationInformationContext.class)
+ .compose(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
+ }
+
+ /**
+ * Set the strategy used to lookup the {@link WebAuthnRegistrationInformationContext}.
+ *
+ * @param strategy The strategy to set.
+ */
+ public void setWebauthnInfoContextLookupStrategy(
+ final Function<ProfileRequestContext, WebAuthnRegistrationInformationContext> strategy) {
+ webauthnInfoContextLookupStrategy = Constraint.isNotNull(strategy,
+ "WebauthnInfoContextLookupStrategy can not be null");
+ }
+
+ /**
+ * Sets whether non-message-based error messages should be exposed or turned into a more
+ * generic value.
+ *
+ * @param id message ID
+ */
+ public void setGenericMessageID(@Nullable final String id) {
+ genericMessageID = StringSupport.trimOrNull(id);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String apply(@Nullable final ProfileRequestContext input) {
+
+ final MessageSourceAccessor messageSource = getMessageSourceAccessor();
+ if (messageSource == null) {
+ return null;
+ }
+
+ final WebAuthnRegistrationInformationContext regInfoCtx = webauthnInfoContextLookupStrategy.apply(input);
+ if (regInfoCtx == null) {
+ return null;
+ }
+
+ final String classifiedError = regInfoCtx.getLastClassifiedMessage();
+ if (classifiedError != null && !classifiedError.isEmpty()) {
+ return getClassifiedMessage(messageSource, classifiedError);
+ }
+ return null;
+ }
+
+ /**
+ * Get classified message.
+ *
+ * @param messageSource Spring message source
+ * @param classifiedMessage classified message
+ *
+ * @return mapped message, or null if an empty string was produced.
+ */
+ @Nullable private String getClassifiedMessage(@Nonnull final MessageSourceAccessor messageSource,
+ @Nonnull final String classifiedMessage) {
+
+ String message = messageSource.getMessage(classifiedMessage, "");
+ if (message.isEmpty()) {
+ message = messageSource.getMessage( genericMessageID != null ? genericMessageID :
+ "idp.webauthn.register.message", "Registration result: "
+ + classifiedMessage);
+ }
+ if (message.isEmpty()) {
+ return null;
+ }
+ return message;
+
+ }
+
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
index 651817f..2c5476f 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
@@ -32,6 +32,7 @@ import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import net.shibboleth.idp.plugin.authn.webauthn.admin.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationErrorContext;
import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -71,6 +72,8 @@ public class ValidateAuthenticatorAttestationResponse extends AbstractWebAuthnRe
log.error("{} PublicKeyCredential containing the authenticator attestation response was null",
getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, WebAuthnRegistrationEventIds.INVALID_REGISTRATION);
+ context.ensureSubcontext(WebAuthnRegistrationErrorContext.class)
+ .addClassifiedError(WebAuthnRegistrationEventIds.INVALID_REGISTRATION);
return false;
}
@@ -78,6 +81,8 @@ public class ValidateAuthenticatorAttestationResponse extends AbstractWebAuthnRe
if (pkCredCreationOptions == null) {
log.error("{} PublicKeyCredential creation options was null", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, WebAuthnRegistrationEventIds.INVALID_REGISTRATION);
+ context.ensureSubcontext(WebAuthnRegistrationErrorContext.class)
+ . addClassifiedError(WebAuthnRegistrationEventIds.INVALID_REGISTRATION);
return false;
}
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
index c0d6969..42c252d 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
@@ -17,8 +17,22 @@
<bean id="shibboleth.ChildLookup.WebAuthnManagementContext"
class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext) }" />
-
-
+
+ <bean id="shibboleth.ChildLookup.WebAuthnAdminInfoContext" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationInformationContext) }" />
+ </constructor-arg>
+ <constructor-arg name="f" ref="shibboleth.ChildLookup.WebAuthnManagementContext"/>
+ </bean>
+
+ <bean id="shibboleth.ChildLookup.WebAuthnAdminErrorContext" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationErrorContext) }" />
+ </constructor-arg>
+ <constructor-arg name="f" ref="shibboleth.ChildLookup.WebAuthnManagementContext"/>
+ </bean>
<!-- Abstract parent beans -->
@@ -57,6 +71,16 @@
<bean id="DeletePublicKeyCredential" parent="AbstractWebAuthnManagementAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AdminDeletePublicKeyCredential" />
+ <!-- Default functions to produce messages for the management view. -->
+ <bean id="DefaultAdminInfoMessageFunction" class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.RegistrationInfoMessageLookupFunction" lazy-init="true"
+ p:genericMessageID="%{idp.authn.webauthn.admin.management.genericMessageID:}" scope="prototype" p:webauthnInfoContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAdminInfoContext"/>
+
+ <bean id="DefaultAdminErrorMessageFunction" class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.RegistrationErrorMessageLookupFunction" lazy-init="true"
+ p:genericMessageID="%{idp.authn.webauthn.admin.management.genericMessageID:}" scope="prototype" p:webauthnErrorContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAdminErrorContext"/>
+
+ <alias alias="AdminInfoMessageFunction" name="%{idp.authn.webauthn.admin.management.infoMessageFunction:DefaultAdminInfoMessageFunction}" />
+ <alias alias="AdminErrorMessageFunction" name="%{idp.authn.webauthn.admin.management.errorMessageFunction:DefaultAdminErrorMessageFunction}" />
+
</beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-flow.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-flow.xml
index 33af679..c13fd7d 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-flow.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-flow.xml
@@ -72,6 +72,8 @@
<evaluate expression="T(net.shibboleth.idp.plugin.authn.webauthn.impl.WebAuthnEncoder)" result="viewScope.webAuthnEncoder"/>
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('AdminInfoMessageFunction')" result="viewScope.adminInfoMessageFunction" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('AdminErrorMessageFunction')" result="viewScope.adminErrorMessageFunction" />
</on-render>
<transition on="again" to="UsernameSearchView" />
@@ -86,8 +88,7 @@
<evaluate expression="'proceed'" />
<transition on="proceed" to="LookupCredentials">
- <!-- TODO externalise message bundle-->
- <set name="flashScope.managementOutcomes" value="'Key was removed successfully'"/>
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext)).ensureSubcontext(T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationInformationContext)).addClassifiedMessage('KeyRemoved')"/>
</transition>
</action-state>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
index 2e91dbb..7465374 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
@@ -127,6 +127,15 @@
<bean id="StorePublicKeyCredential" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.StorePublicKeyCredential"
p:credentialRepository-ref="shibboleth.authn.webauthn.DefaultCredentialRepository" />
+
+ <!-- Default functions to produce messages for the registration view. -->
+ <bean id="DefaultRegistrationInfoMessageFunction" class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.RegistrationInfoMessageLookupFunction" lazy-init="true"
+ p:genericMessageID="%{idp.authn.webauthn.registration.genericMessageID:}" />
+ <bean id="DefaultRegistrationErrorMessageFunction" class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.RegistrationErrorMessageLookupFunction" lazy-init="true"
+ p:genericMessageID="%{idp.authn.webauthn.registration.genericMessageID:}" />
+
+ <alias alias="RegistrationInfoMessageFunction" name="%{idp.authn.webauthn.registration.infoMessageFunction:DefaultRegistrationInfoMessageFunction}" />
+ <alias alias="RegistrationErrorMessageFunction" name="%{idp.authn.webauthn.registration.errorMessageFunction:DefaultRegistrationErrorMessageFunction}" />
</beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
index b308e87..a52038c 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
@@ -82,6 +82,8 @@
<evaluate expression="T(net.shibboleth.idp.plugin.authn.webauthn.impl.WebAuthnEncoder)" result="viewScope.webAuthnEncoder"/>
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('RegistrationInfoMessageFunction')" result="viewScope.registrationInfoMessageFunction" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('RegistrationErrorMessageFunction')" result="viewScope.registrationErrorMessageFunction" />
</on-render>
<transition on="finish" to="RegistrationComplete" />
@@ -96,13 +98,9 @@
<evaluate expression="StorePublicKeyCredential"/>
<evaluate expression="'proceed'" />
- <transition on="InvalidRegistration" to="GeneratePublicKeyCredentialCreationOptions">
- <!-- TODO externalise message bundle-->
- <set name="flashScope.registrationErrorOutcomes" value="'Key registration unsuccessful'"/>
- </transition>
+ <transition on="InvalidRegistration" to="GeneratePublicKeyCredentialCreationOptions"/>
<transition on="proceed" to="GeneratePublicKeyCredentialCreationOptions">
- <!-- TODO externalise message bundle-->
- <set name="flashScope.registrationOutcomes" value="'Key was registered successfully'"/>
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext)).ensureSubcontext(T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationInformationContext)).addClassifiedMessage('ValidRegistration')"/>
</transition>
</action-state>
@@ -112,8 +110,7 @@
<evaluate expression="'proceed'" />
<transition on="proceed" to="GeneratePublicKeyCredentialCreationOptions">
- <!-- TODO externalise message bundle-->
- <set name="flashScope.registrationOutcomes" value="'Key was removed successfully'"/>
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext)).ensureSubcontext(T(net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationInformationContext)).addClassifiedMessage('KeyRemoved')"/>
</transition>
</action-state>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
index 1684641..55e4e53 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
@@ -96,6 +96,10 @@ idp.authn.webauthn.supportedPrincipals = \
#idp.authn.webauthn.registration.username.lowercase = false
#idp.authn.webauthn.registration.username.trim = false
+#idp.authn.webauthn.registration.genericMessageID =
+#idp.authn.webauthn.registration.infoMessageFunction = DefaultRegistrationInfoMessageFunction
+#idp.authn.webauthn.registration.errorMessageFunction = DefaultRegistrationErrorMessageFunction
+
#### Administrator properties
@@ -107,6 +111,10 @@ idp.authn.webauthn.supportedPrincipals = \
#idp.authn.webauthn.admin.management.defaultAuthenticationMethods = saml2/http://example.org/ac/classes/mfa
#idp.authn.webauthn.admin.management.authenticated = true
+#idp.authn.webauthn.admin.management.genericMessageID =
+#idp.authn.webauthn.admin.management.infoMessageFunction = DefaultAdminInfoMessageFunction
+#idp.authn.webauthn.admin.management.errorMessageFunction = DefaultAdminErrorMessageFunction
+
#### Authentication properties
# Which type of flow is supported? Usernameless or passwordless
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 cc9fd41..d8df1a2 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
@@ -45,4 +45,9 @@ idp.webauthn.ended = Your session has ended
# When debug information has been enabled
idp.webauthn.debug.title = Debugging
idp.webauthn.debug.request = Request options
-idp.webauthn.register.debug.registration = Registration options
\ No newline at end of file
+idp.webauthn.register.debug.registration = Registration options
+
+# Messages to report back to the user during registration
+InvalidRegistration = Key registration unsuccessful
+ValidRegistration = Key was registered successfully
+KeyRemoved = Key was removed successfully
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
index 2f864c7..e374d58 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
@@ -7,9 +7,7 @@
## flowExecutionKey - the SWF execution key (this is built into the flowExecutionUrl)
## profileRequestContext - root of context tree
## authenticationContext - context with authentication request information
-## authenticationErrorContext - context with login error state
## webauthnContext = web authentication context
-## authenticationWarningContext - context with login warning state
## rpUIContext - the context with SP UI information from the metadata
## encoder - HTMLEncoder class
## webAuthnEncoder - WebAuthnEncoder class
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm
index 33aa561..3b554c3 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm
@@ -7,8 +7,6 @@
## flowExecutionKey - the SWF execution key (this is built into the flowExecutionUrl)
## profileRequestContext - root of context tree
## webAuthnManContext = WebAuthn management context
-## authenticationWarningContext - context with login warning state
-## rpUIContext - the context with SP UI information from the metadata
## encoder - HTMLEncoder class
## webAuthnEncoder - WebAuthnEncoder class
## request - HttpServletRequest
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm
index 4dd1f5a..fcf1eb3 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm
@@ -8,13 +8,14 @@
## profileRequestContext - root of context tree
## webAuthnManContext = WebAuthn management context
## authenticationWarningContext - context with login warning state
-## rpUIContext - the context with SP UI information from the metadata
## encoder - HTMLEncoder class
## webAuthnEncoder - WebAuthnEncoder class
## request - HttpServletRequest
## response - HttpServletResponse
## environment - Spring Environment object for property resolution
## custom - arbitrary object injected by deployer
+## adminInfoMessageFunction - function to return info admin messages
+## adminErrorMessageFunction - function to return error admin messages
## Add CSP directives
#set ($areYouSure = "return confirm('#springMessageText('idp.webauthn.register.credential.remove.confirm', 'Are you sure')');")
@@ -41,11 +42,13 @@ $response.addHeader("Content-Security-Policy", "default-src 'none'; style-src 's
<section>
<div id="supportedDiv">
<div class="centre">
- #if ($managementOutcomes)
- <p id="reg-success-outcome" class="output-message output--success">$managementOutcomes</p>
- #end
- #if ($managementErrorOutcomes)
- <p id="reg-error-outcome" class="output-message output--error">$managementErrorOutcomes</p>
+ #set ($infoMessage = $adminInfoMessageFunction.apply($profileRequestContext))
+ #if ($infoMessage)
+ <p id="reg-success-outcome" class="output-message output--success">$encoder.encodeForHTML($infoMessage)</p>
+ #end
+ #set ($errorMessage = $adminErrorMessageFunction.apply($profileRequestContext))
+ #if ($errorMessage)
+ <p id="reg-error-outcome" class="output-message output--error">$encoder.encodeForHTML($errorMessage)</p>
#end
<div class="hidden output-message output--error" id="error_div">
<p id="error_message"></p>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
index b96a8f0..81319d9 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
@@ -7,14 +7,14 @@
## flowExecutionKey - the SWF execution key (this is built into the flowExecutionUrl)
## profileRequestContext - root of context tree
## webauthnRegContext = WebAuthn registration context
-## authenticationWarningContext - context with login warning state
-## rpUIContext - the context with SP UI information from the metadata
## encoder - HTMLEncoder class
## webAuthnEncoder - WebAuthnEncoder class
## request - HttpServletRequest
## response - HttpServletResponse
## environment - Spring Environment object for property resolution
## custom - arbitrary object injected by deployer
+## registrationInfoMessageFunction - function to produce information message for form
+## registrationErrorMessageFunction - function to produce error message for form
##
#set ($debug = $environment.getProperty("idp.authn.webauthn.ui.debug", "false"))
@@ -106,13 +106,17 @@ $response.addHeader("Content-Security-Policy", "default-src 'none'; style-src 's
</header>
<section>
<div id="supportedDiv">
- <div class="centre">
- #if ($registrationOutcomes)
- <p id="reg-success-outcome" class="output-message output--success">$registrationOutcomes</p>
+ <div class="centre">
+ #set ($infoMessage = $registrationInfoMessageFunction.apply($profileRequestContext))
+ #if ($infoMessage)
+ <p id="reg-success-outcome" class="output-message output--success">$encoder.encodeForHTML($infoMessage)</p>
#end
- #if ($registrationErrorOutcomes)
- <p id="reg-error-outcome" class="output-message output--error">$registrationErrorOutcomes</p>
- #end
+
+ #set ($errorMessage = $registrationErrorMessageFunction.apply($profileRequestContext))
+ #if ($errorMessage)
+ <p id="reg-error-outcome" class="output-message output--error">$encoder.encodeForHTML($errorMessage)</p>
+ #end
+
<div class="hidden output-message output--error" id="error_div">
<p id="error_message"></p>
</div>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list