[java-idp-plugin-webauthn] branch main updated: Add key deletion functionality
Phil Smart
philip.smart at jisc.ac.uk
Thu Dec 14 16:34:06 UTC 2023
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=07f558d566e3a53195536abc9422a325afc38230
The following commit(s) were added to refs/heads/main by this push:
new 07f558d Add key deletion functionality
07f558d is described below
commit 07f558d566e3a53195536abc9422a325afc38230
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Dec 14 16:34:03 2023 +0000
Add key deletion functionality
---
.../context/WebAuthnRegistrationContext.java | 24 ++++-
.../webauthn/storage/CredentialRegistration.java | 11 ++
.../StorageServiceCredentialRepository.java | 16 ++-
.../admin/impl/DeletePublicKeyCredential.java | 79 ++++++++++++++
...xtractKeyRemovalInformationFromFormRequest.java | 114 +++++++++++++++++++++
.../webauthn-registration-beans.xml | 10 +-
.../webauthn-registration-flow.xml | 29 ++++--
.../authn/webauthn/views/webauthn-register.vm | 20 ++--
.../views/webauthn-registration-outcomes.vm | 93 +++++++++++++++++
9 files changed, 374 insertions(+), 22 deletions(-)
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
index 1ef8de2..6c7c8f1 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
@@ -45,7 +45,10 @@ public final class WebAuthnRegistrationContext extends BaseWebAuthnContext {
@Nullable private RegistrationResult registrationResult;
/** A display friendly nickname for the credential that is to be registered.*/
- @Nullable private String credentialNickname;
+ @Nullable private String credentialNickname;
+
+ /** The ID of the credential that is going to be removed.*/
+ @Nullable private byte[] credentialIdToRemove;
/**
* Get the attestation response as a result of creating a new credential.
@@ -145,4 +148,23 @@ public final class WebAuthnRegistrationContext extends BaseWebAuthnContext {
return credentialNickname;
}
+ /**
+ * Set the ID of the credential that is going to be removed.
+ *
+ * @param credentialId the credential identifier
+ */
+ public WebAuthnRegistrationContext setCredentialIdToRemove(@Nullable final byte[] id) {
+ credentialIdToRemove = id;
+ return this;
+ }
+
+ /**
+ * Get the ID of the credential that is going to be removed.
+ *
+ * @return Returns the credentialIdToRemove.
+ */
+ @Nullable public byte[] getCredentialIdToRemove() {
+ return credentialIdToRemove;
+ }
+
}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java
index cc8368d..6fa19ab 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java
@@ -21,6 +21,7 @@ import java.time.Instant;
import java.util.Optional;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -32,6 +33,8 @@ import com.yubico.webauthn.data.UserIdentity;
/**
* Influenced by the CredentialRegistration class in the Yubico demo libraries.
+ *
+ * Used to hold registrations, and for easy extraction of values for display.
*/
//TODO need our own storage record, so this should be test only and then replaced with the actual one eventually
//TODO make this more official and undeprecate
@@ -63,6 +66,14 @@ public class CredentialRegistration {
return credentialNickname.orElse("");
}
+ public String getCredentialIdBase64Url() {
+ return credential.getCredentialId().getBase64Url();
+ }
+
+ public String getTransportsString() {
+ return transports.stream().map(AuthenticatorTransport::getId).collect(Collectors.joining(","));
+ }
+
public CredentialRegistration() {
transports = new TreeSet<AuthenticatorTransport>();
}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java
index 69c01c9..368d58c 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java
@@ -15,18 +15,24 @@
package net.shibboleth.idp.plugin.authn.webauthn.storage;
import java.util.Collection;
+import java.util.Optional;
import com.yubico.webauthn.CredentialRepository;
-
+import com.yubico.webauthn.data.ByteArray;
/**
- * An IdP extension of the Yubico {@link CredentialRepository} interface to support additional operations required
- * by the IdP.
+ * An IdP extension of the Yubico {@link CredentialRepository} interface to
+ * support additional operations required by the IdP.
*/
public interface StorageServiceCredentialRepository extends CredentialRepository {
-
+
Collection<CredentialRegistration> getRegistrationsByUsername(final String username);
-
+
boolean addRegistrationByUsername(final String username, final CredentialRegistration reg);
+ Optional<CredentialRegistration> getRegistrationByUsernameAndCredentialId(final String username,
+ final ByteArray id);
+
+ boolean removeRegistrationByUsername(
+ final String username, final CredentialRegistration credentialRegistration);
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/DeletePublicKeyCredential.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/DeletePublicKeyCredential.java
new file mode 100644
index 0000000..eda1779
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/DeletePublicKeyCredential.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.admin.impl;
+
+import java.util.Optional;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.yubico.webauthn.data.ByteArray;
+
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnRegistrationAction;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * An action that stores the public key credential into the credential repository.
+ */
+public class DeletePublicKeyCredential extends AbstractWebAuthnRegistrationAction {
+
+ /** Class logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(DeletePublicKeyCredential.class);
+
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final WebAuthnRegistrationContext context) {
+
+ final String username = context.getUsername();
+ if (username == null) {
+ log.error("{} Unable to find username in registration context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+
+ final byte[] credentialId = context.getCredentialIdToRemove();
+ if (credentialId == null) {
+ log.error("{} Unable to find credentialId in registration context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+
+ final Optional<CredentialRegistration> credential =
+ getCredentialRepository().getRegistrationByUsernameAndCredentialId(
+ username, new ByteArray(credentialId));
+
+ if (credential.isEmpty()) {
+ // This is not an error
+ log.debug("{} Unable to find credential to remove, nothing to remove", getLogPrefix());
+ } else {
+ final boolean removed = getCredentialRepository().removeRegistrationByUsername(username, credential.get());
+ log.debug("{} Credential '{}' {} removed", getLogPrefix(), credential.get().getCredentialIdBase64Url(),
+ removed ? "was" : "was not");
+ }
+
+ // Remove the key to be deleted in-case the context state is re-used
+ context.setCredentialIdToRemove(null);
+ }
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractKeyRemovalInformationFromFormRequest.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractKeyRemovalInformationFromFormRequest.java
new file mode 100644
index 0000000..3bc15fe
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractKeyRemovalInformationFromFormRequest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.admin.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import jakarta.servlet.http.HttpServletRequest;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnRegistrationAction;
+import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.DecodingException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+
+
+/**
+ * An action that extracts the credential identifier for removal from the incoming HTTP request.
+ */
+public class ExtractKeyRemovalInformationFromFormRequest extends AbstractWebAuthnRegistrationAction {
+
+ /** Default token code field name. */
+ @Nonnull @NotEmpty public static final String DEFAULT_FIELD_NAME = "credentialId";
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(ExtractKeyRemovalInformationFromFormRequest.class);
+
+ /** Name of header. */
+ @NonnullAfterInit @NotEmpty private String fieldName;
+
+ /** Constructor. */
+ public ExtractKeyRemovalInformationFromFormRequest() {
+ fieldName = DEFAULT_FIELD_NAME;
+ }
+
+ /**
+ * Set the name of the field to examine.
+ *
+ * @param field field name
+ */
+ public void setFieldName(@Nonnull @NotEmpty final String field) {
+ checkSetterPreconditions();
+
+ fieldName = Constraint.isNotNull(StringSupport.trimOrNull(field), "Field name cannot be null or empty");
+ }
+
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final WebAuthnRegistrationContext context) {
+
+ final HttpServletRequest request = getHttpServletRequest();
+ if (request == null) {
+ log.debug("{} Profile action does not contain an HttpServletRequest", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+
+ final String credentialId = extractCredentialId(request);
+ if (credentialId == null) {
+ //TODO look at these eventIds
+ log.debug("{} CredentialID not found in HTTP request",getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
+ return;
+ }
+ try {
+ // Should be base64 encoded credential.
+ final byte[] credentialIdAsBytes = Base64Support.decode(credentialId);
+ context.setCredentialIdToRemove(credentialIdAsBytes);
+ log.trace("{} Credential to remove '{}'",getLogPrefix(),credentialId);
+ } catch (final DecodingException e) {
+ //TODO look at these eventIds
+ log.debug("{} Unable to base64 decode credentialID, can not remove credential", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
+ return;
+ }
+
+ }
+
+ /**
+ * Extract the credential identifier from the HTTP request parameters.
+ *
+ * @param httpRequest the http request
+ *
+ * @return the credential identifier
+ */
+ @Nullable private String extractCredentialId(@Nonnull final HttpServletRequest httpRequest) {
+ return httpRequest.getParameter(fieldName);
+ }
+
+}
\ No newline at end of file
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 75bc9df..8695eb7 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
@@ -44,8 +44,14 @@
<bean id="ExtractAuthenticatorAttestationFromFormRequest" parent="AbstractWebAuthnRegistrationAction"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ExtractAuthenticatorAttestationFromFormRequest"
p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:objectMapper-ref="shibboleth.authn.WebAuthn.JSONObjectMapper" />
-
+ p:objectMapper-ref="shibboleth.authn.WebAuthn.JSONObjectMapper" />
+
+ <bean id="ExtractKeyRemovalInformationFromFormRequest" parent="AbstractWebAuthnRegistrationAction"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ExtractKeyRemovalInformationFromFormRequest"
+ p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"/>
+
+ <bean id="DeletePublicKeyCredential" parent="AbstractWebAuthnRegistrationAction"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.DeletePublicKeyCredential"/>
<bean id="ValidateAuthenticatorAttestationResponse" parent="AbstractWebAuthnRegistrationAction"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ValidateAuthenticatorAttestationResponse" />
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 e35265c..b6b95ad 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
@@ -47,22 +47,37 @@
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
</on-render>
- <transition on="proceed" to="ExtractPublicKeyCredential" />
+ <transition on="addKey" to="AddKey" />
+ <transition on="deleteKey" to="DeleteKey" />
</view-state>
- <action-state id="ExtractPublicKeyCredential">
+ <action-state id="AddKey">
<evaluate expression="ExtractAuthenticatorAttestationFromFormRequest"/>
<evaluate expression="ValidateAuthenticatorAttestationResponse"/>
- <evaluate expression="StorePublicKeyCredential"/>
+ <evaluate expression="StorePublicKeyCredential"/>
<evaluate expression="'proceed'" />
- <transition on="proceed" to="DisplayWebAuthnSuccessfulRegistration" />
+ <transition on="proceed" to="DisplayWebAuthnAdminResult">
+ <!-- TODO externalise message bundle-->
+ <set name="flashScope.registrationOutcomes" value="'Key was registered successfully'"/>
+ </transition>
</action-state>
- <view-state id="DisplayWebAuthnSuccessfulRegistration" view="webauthn/webauthn-registered">
+ <action-state id="DeleteKey">
+ <evaluate expression="ExtractKeyRemovalInformationFromFormRequest"/>
+ <evaluate expression="DeletePublicKeyCredential"/>
+
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="DisplayWebAuthnAdminResult">
+ <!-- TODO externalise message bundle-->
+ <set name="flashScope.registrationOutcomes" value="'Key was removed successfully'"/>
+ </transition>
+ </action-state>
+
+ <view-state id="DisplayWebAuthnAdminResult" view="webauthn/webauthn-registration-outcomes">
<on-entry>
- <evaluate expression="LookupRegisteredCredentials"/>
+ <evaluate expression="LookupRegisteredCredentials"/>
</on-entry>
<on-render>
<evaluate expression="environment" result="viewScope.environment" />
@@ -80,7 +95,7 @@
<transition on="proceed" to="RegistrationComplete" />
</view-state>
- <end-state id="RegistrationComplete"/>
+ <end-state id="RegistrationComplete"/>
<bean-import resource="webauthn-registration-beans.xml" />
<bean-import resource="../../authn/WebAuthn/webauthn-abstract-beans.xml" />
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 594c6ef..0481101 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
@@ -43,7 +43,7 @@
document.getElementById("authenticatorAttestation").value = JSON.stringify(attestation);
document.getElementById("registrationSubmit").click();
}).catch(function (err){
- console.error(err);
+ console.error(err)
});
};
@@ -84,14 +84,20 @@
<th>Key Name</th>
<th>Transports</th>
<th>Registration Time</th>
- <th>Delete</th>
+ <th>Action</th>
</tr>
#foreach($cred in $webauthnRegContext.existingCredentials)
<tr>
- <td>$cred.nickname</td>
- <td>$cred.transports</td>
- <td>$cred.registrationTimestamp</td>
- <td><button class="webauthn-table-button" id="removeButton">Remove</button></td>
+ <td>$encoder.encodeForHTML($cred.nickname)</td>
+ <td>$encoder.encodeForHTML($cred.transportsString)</td>
+ <td>$encoder.encodeForHTML($cred.registrationTimestamp)</td>
+ <td>
+ <form id="deleteKeyForm" action="$flowExecutionUrl" method="post">
+ #parse("csrf/csrf.vm")
+ <input type="hidden" name="credentialId" value="$cred.credentialIdBase64Url"/>
+ <button class="webauthn-table-button" id="removeButton" type="submit" name="_eventId_deleteKey">Remove</button>
+ </form>
+ </td>
</tr>
#end
</table>
@@ -116,7 +122,7 @@
#parse("csrf/csrf.vm")
<input type="hidden" id="credentialNickname" name="credentialNickname"/>
<input type="hidden" id="authenticatorAttestation" name="authenticatorAttestation"/>
- <button class="hidden" id="registrationSubmit" type="submit" name="_eventId_proceed">Submit Registration</button>
+ <button class="hidden" id="registrationSubmit" type="submit" name="_eventId_addKey">Submit Registration</button>
</form>
</div>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registration-outcomes.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registration-outcomes.vm
new file mode 100644
index 0000000..70d9e66
--- /dev/null
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registration-outcomes.vm
@@ -0,0 +1,93 @@
+##
+## Velocity Template for DisplayWebauthnView view-state
+##
+## Velocity context will contain the following properties
+## flowExecutionUrl - the form action location
+## flowRequestContext - the Spring Web Flow RequestContext
+## 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
+## webauthnRegContext = WebAuthn registration context
+## authenticationWarningContext - context with login warning state
+## rpUIContext - the context with SP UI information from the metadata
+## encoder - HTMLEncoder class
+## request - HttpServletRequest
+## response - HttpServletResponse
+## environment - Spring Environment object for property resolution
+## custom - arbitrary object injected by deployer
+##
+#set ($rpContext = $profileRequestContext.getSubcontext('net.shibboleth.idp.profile.context.RelyingPartyContext'))
+##
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>#springMessageText("idp.title", "Web Login Service")</title>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
+ <link rel="stylesheet" type="text/css" href="$request.getContextPath()#springMessageText("
+ idp.css", "/css/placeholder.css" )">
+ <link rel="stylesheet" type="text/css" href="$request.getContextPath()/css/webauthn.css">
+</head>
+
+
+
+<body>
+ <main class="main">
+ <header>
+ <img class="main-logo" src="$request.getContextPath()#springMessageText("
+ idp.logo", "/images/placeholder-logo.png" )" alt="#springMessageText(" idp.logo.alt-text", "logo" )" />
+
+ #set ($serviceName = $rpUIContext.serviceName)
+ #if ($serviceName && !$rpContext.getRelyingPartyId().contains($serviceName))
+ <h1>#springMessageText("idp.login.loginTo", "Login to") $encoder.encodeForHTML($serviceName)</h1>
+ #end
+ </header>
+ <section>
+ <div class="centre">
+ <p>$registrationOutcomes</p>
+
+ <hr />
+
+ #if ($webauthnRegContext.existingCredentials)
+ <p>Registered credentials</p>
+ <table>
+ <tr>
+ <th>Key Name</th>
+ <th>Transports</th>
+ <th>Registration Time</th>
+ </tr>
+ #foreach($cred in $webauthnRegContext.existingCredentials)
+ <tr>
+ <td>$encoder.encodeForHTML($cred.nickname)</td>
+ <td>$encoder.encodeForHTML($cred.transportsString)</td>
+ <td>$encoder.encodeForHTML($cred.registrationTimestamp)</td>
+ </tr>
+ #end
+ </table>
+ #else
+ <div><span>You have no registered credentials</span></div>
+ #end
+ <br />
+ <form id="doneButtonForm" action="$flowExecutionUrl" method="post">
+ #parse("csrf/csrf.vm")
+ <button id="doneButton" type="submit" name="_eventId_proceed">Done</button>
+ </form>
+ </div>
+
+
+ </section>
+ </main>
+
+ <footer>
+ <div class="container container-footer">
+ <p class="footer-text">#springMessageText("idp.footer", "Insert your footer text here.")</p>
+ </div>
+ </footer>
+ </div>
+
+</body>
+
+</html>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list