[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