001package Torello.Browser;
002
003import java.util.*;
004import javax.json.*;
005import javax.json.stream.*;
006import java.io.*;
007
008import java.lang.reflect.Method;
009import java.lang.reflect.Parameter;
010import java.util.function.Function;
011
012import Torello.Java.Additional.*;
013import Torello.Java.JSON.*;
014
015import static Torello.Java.JSON.JFlag.*;
016
017import Torello.Java.StrCmpr;
018import Torello.JavaDoc.StaticFunctional;
019import Torello.JavaDoc.JDHeaderBackgroundImg;
020import Torello.JavaDoc.Excuse;
021
022/**
023 * <SPAN CLASS=COPIEDJDK><B>This domain allows configuring virtual authenticators to test the WebAuthn
024 * API.</B></SPAN>
025 * 
026 * <EMBED CLASS='external-html' DATA-FILE-ID=CODE_GEN_NOTE>
027 */
028@StaticFunctional(Excused={"counter"}, Excuses={Excuse.CONFIGURATION})
029@JDHeaderBackgroundImg(EmbedTagFileID="WOOD_PLANK_NOTE")
030public class WebAuthn
031{
032    // ********************************************************************************************
033    // ********************************************************************************************
034    // Class Header Stuff
035    // ********************************************************************************************
036    // ********************************************************************************************
037
038
039    // No Pubic Constructors
040    private WebAuthn () { }
041
042    // These two Vector's are used by all the "Methods" exported by this class.  java.lang.reflect
043    // is used to generate the JSON String's.  It saves thousands of lines of Auto-Generated Code.
044    private static final Map<String, Vector<String>>    parameterNames = new HashMap<>();
045    private static final Map<String, Vector<Class<?>>>  parameterTypes = new HashMap<>();
046
047    // Some Methods do not take any parameters - for instance all the "enable()" and "disable()"
048    // I simply could not get ride of RAW-TYPES and UNCHECKED warnings... so there are now,
049    // offically, two empty-vectors.  One for String's, and the other for Classes.
050
051    private static final Vector<String>     EMPTY_VEC_STR = new Vector<>();
052    private static final Vector<Class<?>>   EMPTY_VEC_CLASS = new Vector<>();
053
054    static
055    {
056        for (Method m : WebAuthn.class.getMethods())
057        {
058            // This doesn't work!  The parameter names are all "arg0" ... "argN"
059            // It works for java.lang.reflect.Field, BUT NOT java.lang.reflect.Parameter!
060            //
061            // Vector<String> parameterNamesList = new Vector<>(); -- NOPE!
062
063            Vector<Class<?>> parameterTypesList = new Vector<>();
064        
065            for (Parameter p : m.getParameters()) parameterTypesList.add(p.getType());
066
067            parameterTypes.put(
068                m.getName(),
069                (parameterTypesList.size() > 0) ? parameterTypesList : EMPTY_VEC_CLASS
070            );
071        }
072    }
073
074    static
075    {
076        Vector<String> v = null;
077
078        parameterNames.put("enable", EMPTY_VEC_STR);
079
080        parameterNames.put("disable", EMPTY_VEC_STR);
081
082        v = new Vector<String>(1);
083        parameterNames.put("addVirtualAuthenticator", v);
084        Collections.addAll(v, new String[]
085        { "options", });
086
087        v = new Vector<String>(1);
088        parameterNames.put("removeVirtualAuthenticator", v);
089        Collections.addAll(v, new String[]
090        { "authenticatorId", });
091
092        v = new Vector<String>(2);
093        parameterNames.put("addCredential", v);
094        Collections.addAll(v, new String[]
095        { "authenticatorId", "credential", });
096
097        v = new Vector<String>(2);
098        parameterNames.put("getCredential", v);
099        Collections.addAll(v, new String[]
100        { "authenticatorId", "credentialId", });
101
102        v = new Vector<String>(1);
103        parameterNames.put("getCredentials", v);
104        Collections.addAll(v, new String[]
105        { "authenticatorId", });
106
107        v = new Vector<String>(2);
108        parameterNames.put("removeCredential", v);
109        Collections.addAll(v, new String[]
110        { "authenticatorId", "credentialId", });
111
112        v = new Vector<String>(1);
113        parameterNames.put("clearCredentials", v);
114        Collections.addAll(v, new String[]
115        { "authenticatorId", });
116
117        v = new Vector<String>(2);
118        parameterNames.put("setUserVerified", v);
119        Collections.addAll(v, new String[]
120        { "authenticatorId", "isUserVerified", });
121
122        v = new Vector<String>(2);
123        parameterNames.put("setAutomaticPresenceSimulation", v);
124        Collections.addAll(v, new String[]
125        { "authenticatorId", "enabled", });
126    }
127
128
129    // ********************************************************************************************
130    // ********************************************************************************************
131    // Types - Static Inner Classes
132    // ********************************************************************************************
133    // ********************************************************************************************
134
135    // public static class AuthenticatorId => String
136    
137    /** <CODE>[No Description Provided by Google]</CODE> */
138    public static final String[] AuthenticatorProtocol =
139    { "u2f", "ctap2", };
140    
141    /** <CODE>[No Description Provided by Google]</CODE> */
142    public static final String[] Ctap2Version =
143    { "ctap2_0", "ctap2_1", };
144    
145    /** <CODE>[No Description Provided by Google]</CODE> */
146    public static final String[] AuthenticatorTransport =
147    { "usb", "nfc", "ble", "cable", "internal", };
148    
149    /** <CODE>[No Description Provided by Google]</CODE> */
150    public static class VirtualAuthenticatorOptions
151        extends BaseType
152        implements java.io.Serializable
153    {
154        /** For Object Serialization.  java.io.Serializable */
155        protected static final long serialVersionUID = 1;
156        
157        public boolean[] optionals()
158        { return new boolean[] { false, true, false, true, true, true, true, true, true, }; }
159        
160        /** <CODE>[No Description Provided by Google]</CODE> */
161        public final String protocol;
162        
163        /**
164         * Defaults to ctap2_0. Ignored if |protocol| == u2f.
165         * <BR />
166         * <BR /><B>OPTIONAL</B>
167         */
168        public final String ctap2Version;
169        
170        /** <CODE>[No Description Provided by Google]</CODE> */
171        public final String transport;
172        
173        /**
174         * Defaults to false.
175         * <BR />
176         * <BR /><B>OPTIONAL</B>
177         */
178        public final Boolean hasResidentKey;
179        
180        /**
181         * Defaults to false.
182         * <BR />
183         * <BR /><B>OPTIONAL</B>
184         */
185        public final Boolean hasUserVerification;
186        
187        /**
188         * If set to true, the authenticator will support the largeBlob extension.
189         * https://w3c.github.io/webauthn#largeBlob
190         * Defaults to false.
191         * <BR />
192         * <BR /><B>OPTIONAL</B>
193         */
194        public final Boolean hasLargeBlob;
195        
196        /**
197         * If set to true, the authenticator will support the credBlob extension.
198         * https://fidoalliance.org/specs/fido-v2.1-rd-20201208/fido-client-to-authenticator-protocol-v2.1-rd-20201208.html#sctn-credBlob-extension
199         * Defaults to false.
200         * <BR />
201         * <BR /><B>OPTIONAL</B>
202         */
203        public final Boolean hasCredBlob;
204        
205        /**
206         * If set to true, tests of user presence will succeed immediately.
207         * Otherwise, they will not be resolved. Defaults to true.
208         * <BR />
209         * <BR /><B>OPTIONAL</B>
210         */
211        public final Boolean automaticPresenceSimulation;
212        
213        /**
214         * Sets whether User Verification succeeds or fails for an authenticator.
215         * Defaults to false.
216         * <BR />
217         * <BR /><B>OPTIONAL</B>
218         */
219        public final Boolean isUserVerified;
220        
221        /**
222         * Constructor
223         *
224         * @param protocol -
225         * 
226         * @param ctap2Version Defaults to ctap2_0. Ignored if |protocol| == u2f.
227         * <BR /><B>OPTIONAL</B>
228         * 
229         * @param transport -
230         * 
231         * @param hasResidentKey Defaults to false.
232         * <BR /><B>OPTIONAL</B>
233         * 
234         * @param hasUserVerification Defaults to false.
235         * <BR /><B>OPTIONAL</B>
236         * 
237         * @param hasLargeBlob 
238         * If set to true, the authenticator will support the largeBlob extension.
239         * https://w3c.github.io/webauthn#largeBlob
240         * Defaults to false.
241         * <BR /><B>OPTIONAL</B>
242         * 
243         * @param hasCredBlob 
244         * If set to true, the authenticator will support the credBlob extension.
245         * https://fidoalliance.org/specs/fido-v2.1-rd-20201208/fido-client-to-authenticator-protocol-v2.1-rd-20201208.html#sctn-credBlob-extension
246         * Defaults to false.
247         * <BR /><B>OPTIONAL</B>
248         * 
249         * @param automaticPresenceSimulation 
250         * If set to true, tests of user presence will succeed immediately.
251         * Otherwise, they will not be resolved. Defaults to true.
252         * <BR /><B>OPTIONAL</B>
253         * 
254         * @param isUserVerified 
255         * Sets whether User Verification succeeds or fails for an authenticator.
256         * Defaults to false.
257         * <BR /><B>OPTIONAL</B>
258         */
259        public VirtualAuthenticatorOptions(
260                String protocol, String ctap2Version, String transport, Boolean hasResidentKey, 
261                Boolean hasUserVerification, Boolean hasLargeBlob, Boolean hasCredBlob, 
262                Boolean automaticPresenceSimulation, Boolean isUserVerified
263            )
264        {
265            // Exception-Check(s) to ensure that if any parameters which are not declared as
266            // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
267            
268            if (protocol == null)  THROWS.throwNPE("protocol");
269            if (transport == null) THROWS.throwNPE("transport");
270            
271            // Exception-Check(s) to ensure that if any parameters which must adhere to a
272            // provided List of Enumerated Values, fails, then IllegalArgumentException shall throw.
273            
274            THROWS.checkIAE("protocol", protocol, "WebAuthn.AuthenticatorProtocol", WebAuthn.AuthenticatorProtocol);
275            THROWS.checkIAE("ctap2Version", ctap2Version, "WebAuthn.Ctap2Version", WebAuthn.Ctap2Version);
276            THROWS.checkIAE("transport", transport, "WebAuthn.AuthenticatorTransport", WebAuthn.AuthenticatorTransport);
277            
278            this.protocol                     = protocol;
279            this.ctap2Version                 = ctap2Version;
280            this.transport                    = transport;
281            this.hasResidentKey               = hasResidentKey;
282            this.hasUserVerification          = hasUserVerification;
283            this.hasLargeBlob                 = hasLargeBlob;
284            this.hasCredBlob                  = hasCredBlob;
285            this.automaticPresenceSimulation  = automaticPresenceSimulation;
286            this.isUserVerified               = isUserVerified;
287        }
288        
289        /**
290         * JSON Object Constructor
291         * @param jo A Json-Object having data about an instance of {@code 'VirtualAuthenticatorOptions'}.
292         */
293        public VirtualAuthenticatorOptions (JsonObject jo)
294        {
295            this.protocol                     = ReadJSON.getString(jo, "protocol", false, true);
296            this.ctap2Version                 = ReadJSON.getString(jo, "ctap2Version", true, false);
297            this.transport                    = ReadJSON.getString(jo, "transport", false, true);
298            this.hasResidentKey               = ReadBoxedJSON.getBoolean(jo, "hasResidentKey", true);
299            this.hasUserVerification          = ReadBoxedJSON.getBoolean(jo, "hasUserVerification", true);
300            this.hasLargeBlob                 = ReadBoxedJSON.getBoolean(jo, "hasLargeBlob", true);
301            this.hasCredBlob                  = ReadBoxedJSON.getBoolean(jo, "hasCredBlob", true);
302            this.automaticPresenceSimulation  = ReadBoxedJSON.getBoolean(jo, "automaticPresenceSimulation", true);
303            this.isUserVerified               = ReadBoxedJSON.getBoolean(jo, "isUserVerified", true);
304        }
305        
306        
307        /** Checks whether {@code 'this'} equals an input Java-{@code Object} */
308        public boolean equals(Object other)
309        {
310            if (this == other)                       return true;
311            if (other == null)                       return false;
312            if (other.getClass() != this.getClass()) return false;
313        
314            VirtualAuthenticatorOptions o = (VirtualAuthenticatorOptions) other;
315        
316            return
317                    Objects.equals(this.protocol, o.protocol)
318                &&  Objects.equals(this.ctap2Version, o.ctap2Version)
319                &&  Objects.equals(this.transport, o.transport)
320                &&  Objects.equals(this.hasResidentKey, o.hasResidentKey)
321                &&  Objects.equals(this.hasUserVerification, o.hasUserVerification)
322                &&  Objects.equals(this.hasLargeBlob, o.hasLargeBlob)
323                &&  Objects.equals(this.hasCredBlob, o.hasCredBlob)
324                &&  Objects.equals(this.automaticPresenceSimulation, o.automaticPresenceSimulation)
325                &&  Objects.equals(this.isUserVerified, o.isUserVerified);
326        }
327        
328        /** Generates a Hash-Code for {@code 'this'} instance */
329        public int hashCode()
330        {
331            return
332                    Objects.hashCode(this.protocol)
333                +   Objects.hashCode(this.ctap2Version)
334                +   Objects.hashCode(this.transport)
335                +   Objects.hashCode(this.hasResidentKey)
336                +   Objects.hashCode(this.hasUserVerification)
337                +   Objects.hashCode(this.hasLargeBlob)
338                +   Objects.hashCode(this.hasCredBlob)
339                +   Objects.hashCode(this.automaticPresenceSimulation)
340                +   Objects.hashCode(this.isUserVerified);
341        }
342    }
343    
344    /** <CODE>[No Description Provided by Google]</CODE> */
345    public static class Credential
346        extends BaseType
347        implements java.io.Serializable
348    {
349        /** For Object Serialization.  java.io.Serializable */
350        protected static final long serialVersionUID = 1;
351        
352        public boolean[] optionals()
353        { return new boolean[] { false, false, true, false, true, false, true, }; }
354        
355        /** <CODE>[No Description Provided by Google]</CODE> */
356        public final String credentialId;
357        
358        /** <CODE>[No Description Provided by Google]</CODE> */
359        public final boolean isResidentCredential;
360        
361        /**
362         * Relying Party ID the credential is scoped to. Must be set when adding a
363         * credential.
364         * <BR />
365         * <BR /><B>OPTIONAL</B>
366         */
367        public final String rpId;
368        
369        /** The ECDSA P-256 private key in PKCS#8 format. (Encoded as a base64 string when passed over JSON) */
370        public final String privateKey;
371        
372        /**
373         * An opaque byte sequence with a maximum size of 64 bytes mapping the
374         * credential to a specific user. (Encoded as a base64 string when passed over JSON)
375         * <BR />
376         * <BR /><B>OPTIONAL</B>
377         */
378        public final String userHandle;
379        
380        /**
381         * Signature counter. This is incremented by one for each successful
382         * assertion.
383         * See https://w3c.github.io/webauthn/#signature-counter
384         */
385        public final int signCount;
386        
387        /**
388         * The large blob associated with the credential.
389         * See https://w3c.github.io/webauthn/#sctn-large-blob-extension (Encoded as a base64 string when passed over JSON)
390         * <BR />
391         * <BR /><B>OPTIONAL</B>
392         */
393        public final String largeBlob;
394        
395        /**
396         * Constructor
397         *
398         * @param credentialId -
399         * 
400         * @param isResidentCredential -
401         * 
402         * @param rpId 
403         * Relying Party ID the credential is scoped to. Must be set when adding a
404         * credential.
405         * <BR /><B>OPTIONAL</B>
406         * 
407         * @param privateKey The ECDSA P-256 private key in PKCS#8 format. (Encoded as a base64 string when passed over JSON)
408         * 
409         * @param userHandle 
410         * An opaque byte sequence with a maximum size of 64 bytes mapping the
411         * credential to a specific user. (Encoded as a base64 string when passed over JSON)
412         * <BR /><B>OPTIONAL</B>
413         * 
414         * @param signCount 
415         * Signature counter. This is incremented by one for each successful
416         * assertion.
417         * See https://w3c.github.io/webauthn/#signature-counter
418         * 
419         * @param largeBlob 
420         * The large blob associated with the credential.
421         * See https://w3c.github.io/webauthn/#sctn-large-blob-extension (Encoded as a base64 string when passed over JSON)
422         * <BR /><B>OPTIONAL</B>
423         */
424        public Credential(
425                String credentialId, boolean isResidentCredential, String rpId, String privateKey, 
426                String userHandle, int signCount, String largeBlob
427            )
428        {
429            // Exception-Check(s) to ensure that if any parameters which are not declared as
430            // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
431            
432            if (credentialId == null) THROWS.throwNPE("credentialId");
433            if (privateKey == null)   THROWS.throwNPE("privateKey");
434            
435            this.credentialId          = credentialId;
436            this.isResidentCredential  = isResidentCredential;
437            this.rpId                  = rpId;
438            this.privateKey            = privateKey;
439            this.userHandle            = userHandle;
440            this.signCount             = signCount;
441            this.largeBlob             = largeBlob;
442        }
443        
444        /**
445         * JSON Object Constructor
446         * @param jo A Json-Object having data about an instance of {@code 'Credential'}.
447         */
448        public Credential (JsonObject jo)
449        {
450            this.credentialId          = ReadJSON.getString(jo, "credentialId", false, true);
451            this.isResidentCredential  = ReadPrimJSON.getBoolean(jo, "isResidentCredential");
452            this.rpId                  = ReadJSON.getString(jo, "rpId", true, false);
453            this.privateKey            = ReadJSON.getString(jo, "privateKey", false, true);
454            this.userHandle            = ReadJSON.getString(jo, "userHandle", true, false);
455            this.signCount             = ReadPrimJSON.getInt(jo, "signCount");
456            this.largeBlob             = ReadJSON.getString(jo, "largeBlob", true, false);
457        }
458        
459        
460        /** Checks whether {@code 'this'} equals an input Java-{@code Object} */
461        public boolean equals(Object other)
462        {
463            if (this == other)                       return true;
464            if (other == null)                       return false;
465            if (other.getClass() != this.getClass()) return false;
466        
467            Credential o = (Credential) other;
468        
469            return
470                    Objects.equals(this.credentialId, o.credentialId)
471                &&  (this.isResidentCredential == o.isResidentCredential)
472                &&  Objects.equals(this.rpId, o.rpId)
473                &&  Objects.equals(this.privateKey, o.privateKey)
474                &&  Objects.equals(this.userHandle, o.userHandle)
475                &&  (this.signCount == o.signCount)
476                &&  Objects.equals(this.largeBlob, o.largeBlob);
477        }
478        
479        /** Generates a Hash-Code for {@code 'this'} instance */
480        public int hashCode()
481        {
482            return
483                    Objects.hashCode(this.credentialId)
484                +   (this.isResidentCredential ? 1 : 0)
485                +   Objects.hashCode(this.rpId)
486                +   Objects.hashCode(this.privateKey)
487                +   Objects.hashCode(this.userHandle)
488                +   this.signCount
489                +   Objects.hashCode(this.largeBlob);
490        }
491    }
492    
493    
494    // Counter for keeping the WebSocket Request ID's distinct.
495    private static int counter = 1;
496    
497    /**
498     * Enable the WebAuthn domain and start intercepting credential storage and
499     * retrieval with a virtual authenticator.
500     * 
501     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
502     * {@link Ret0}&gt;</CODE>
503     *
504     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
505     * browser receives the invocation-request.
506     *
507     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
508     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
509     * {@code >} to ensure the Browser Function has run to completion.
510     */
511    public static Script<String, JsonObject, Ret0> enable()
512    {
513        final int          webSocketID = 44000000 + counter++;
514        final boolean[]    optionals   = new boolean[0];
515        
516        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
517        String requestJSON = WriteJSON.get(
518            parameterTypes.get("enable"),
519            parameterNames.get("enable"),
520            optionals, webSocketID,
521            "WebAuthn.enable"
522        );
523        
524        // This Remote Command does not have a Return-Value.
525        return new Script<>
526            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
527    }
528    
529    /**
530     * Disable the WebAuthn domain.
531     * 
532     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
533     * {@link Ret0}&gt;</CODE>
534     *
535     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
536     * browser receives the invocation-request.
537     *
538     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
539     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
540     * {@code >} to ensure the Browser Function has run to completion.
541     */
542    public static Script<String, JsonObject, Ret0> disable()
543    {
544        final int          webSocketID = 44001000 + counter++;
545        final boolean[]    optionals   = new boolean[0];
546        
547        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
548        String requestJSON = WriteJSON.get(
549            parameterTypes.get("disable"),
550            parameterNames.get("disable"),
551            optionals, webSocketID,
552            "WebAuthn.disable"
553        );
554        
555        // This Remote Command does not have a Return-Value.
556        return new Script<>
557            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
558    }
559    
560    /**
561     * Creates and adds a virtual authenticator.
562     * 
563     * @param options -
564     * 
565     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
566     * String&gt;</CODE>
567     * 
568     * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using
569     * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE>&lt;JsonObject,
570     * String&gt;</CODE> will be returned.
571     *
572     * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>,
573     * using {@link Promise#await()}, <I>and the returned result of this Browser Function may
574      * may be retrieved.</I>
575     *
576     * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B>
577     * <BR /><BR /><UL CLASS=JDUL>
578     * <LI><CODE>String (<B>authenticatorId</B></CODE>)
579     *     <BR />-
580     * </LI>
581     * </UL> */
582    public static Script<String, JsonObject, String> addVirtualAuthenticator
583        (WebAuthn.VirtualAuthenticatorOptions options)
584    {
585        // Exception-Check(s) to ensure that if any parameters which are not declared as
586        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
587        
588        if (options == null) THROWS.throwNPE("options");
589        
590        final int       webSocketID = 44002000 + counter++;
591        final boolean[] optionals   = { false, };
592        
593        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
594        String requestJSON = WriteJSON.get(
595            parameterTypes.get("addVirtualAuthenticator"),
596            parameterNames.get("addVirtualAuthenticator"),
597            optionals, webSocketID,
598            "WebAuthn.addVirtualAuthenticator",
599            options
600        );
601        
602        // 'JSON Binding' ... Converts Browser Response-JSON to 'String'
603        Function<JsonObject, String> responseProcessor = (JsonObject jo) ->
604            ReadJSON.getString(jo, "authenticatorId", false, true);
605        
606        return new Script<>(webSocketID, requestJSON, responseProcessor);
607    }
608    
609    /**
610     * Removes the given authenticator.
611     * 
612     * @param authenticatorId -
613     * 
614     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
615     * {@link Ret0}&gt;</CODE>
616     *
617     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
618     * browser receives the invocation-request.
619     *
620     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
621     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
622     * {@code >} to ensure the Browser Function has run to completion.
623     */
624    public static Script<String, JsonObject, Ret0> removeVirtualAuthenticator
625        (String authenticatorId)
626    {
627        // Exception-Check(s) to ensure that if any parameters which are not declared as
628        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
629        
630        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
631        
632        final int       webSocketID = 44003000 + counter++;
633        final boolean[] optionals   = { false, };
634        
635        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
636        String requestJSON = WriteJSON.get(
637            parameterTypes.get("removeVirtualAuthenticator"),
638            parameterNames.get("removeVirtualAuthenticator"),
639            optionals, webSocketID,
640            "WebAuthn.removeVirtualAuthenticator",
641            authenticatorId
642        );
643        
644        // This Remote Command does not have a Return-Value.
645        return new Script<>
646            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
647    }
648    
649    /**
650     * Adds the credential to the specified authenticator.
651     * 
652     * @param authenticatorId -
653     * 
654     * @param credential -
655     * 
656     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
657     * {@link Ret0}&gt;</CODE>
658     *
659     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
660     * browser receives the invocation-request.
661     *
662     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
663     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
664     * {@code >} to ensure the Browser Function has run to completion.
665     */
666    public static Script<String, JsonObject, Ret0> addCredential
667        (String authenticatorId, WebAuthn.Credential credential)
668    {
669        // Exception-Check(s) to ensure that if any parameters which are not declared as
670        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
671        
672        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
673        if (credential == null)      THROWS.throwNPE("credential");
674        
675        final int       webSocketID = 44004000 + counter++;
676        final boolean[] optionals   = { false, false, };
677        
678        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
679        String requestJSON = WriteJSON.get(
680            parameterTypes.get("addCredential"),
681            parameterNames.get("addCredential"),
682            optionals, webSocketID,
683            "WebAuthn.addCredential",
684            authenticatorId, credential
685        );
686        
687        // This Remote Command does not have a Return-Value.
688        return new Script<>
689            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
690    }
691    
692    /**
693     * Returns a single credential stored in the given virtual authenticator that
694     * matches the credential ID.
695     * 
696     * @param authenticatorId -
697     * 
698     * @param credentialId -
699     * 
700     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
701     * {@link WebAuthn.Credential}&gt;</CODE>
702     * 
703     * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using
704     * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE>&lt;JsonObject,
705     * {@link WebAuthn.Credential}&gt;</CODE> will be returned.
706     *
707     * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>,
708     * using {@link Promise#await()}, <I>and the returned result of this Browser Function may
709      * may be retrieved.</I>
710     *
711     * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B>
712     * <BR /><BR /><UL CLASS=JDUL>
713     * <LI><CODE>{@link WebAuthn.Credential} (<B>credential</B></CODE>)
714     *     <BR />-
715     * </LI>
716     * </UL> */
717    public static Script<String, JsonObject, WebAuthn.Credential> getCredential
718        (String authenticatorId, String credentialId)
719    {
720        // Exception-Check(s) to ensure that if any parameters which are not declared as
721        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
722        
723        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
724        if (credentialId == null)    THROWS.throwNPE("credentialId");
725        
726        final int       webSocketID = 44005000 + counter++;
727        final boolean[] optionals   = { false, false, };
728        
729        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
730        String requestJSON = WriteJSON.get(
731            parameterTypes.get("getCredential"),
732            parameterNames.get("getCredential"),
733            optionals, webSocketID,
734            "WebAuthn.getCredential",
735            authenticatorId, credentialId
736        );
737        
738        // 'JSON Binding' ... Converts Browser Response-JSON to 'WebAuthn.Credential'
739        Function<JsonObject, WebAuthn.Credential> responseProcessor = (JsonObject jo) ->
740            ReadJSON.getObject(jo, "credential", WebAuthn.Credential.class, false, true);
741        
742        return new Script<>(webSocketID, requestJSON, responseProcessor);
743    }
744    
745    /**
746     * Returns all the credentials stored in the given virtual authenticator.
747     * 
748     * @param authenticatorId -
749     * 
750     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
751     * {@link WebAuthn.Credential}[]&gt;</CODE>
752     * 
753     * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using
754     * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE>&lt;JsonObject,
755     * {@link WebAuthn.Credential}[]&gt;</CODE> will be returned.
756     *
757     * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>,
758     * using {@link Promise#await()}, <I>and the returned result of this Browser Function may
759      * may be retrieved.</I>
760     *
761     * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B>
762     * <BR /><BR /><UL CLASS=JDUL>
763     * <LI><CODE>{@link WebAuthn.Credential}[] (<B>credentials</B></CODE>)
764     *     <BR />-
765     * </LI>
766     * </UL> */
767    public static Script<String, JsonObject, WebAuthn.Credential[]> getCredentials
768        (String authenticatorId)
769    {
770        // Exception-Check(s) to ensure that if any parameters which are not declared as
771        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
772        
773        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
774        
775        final int       webSocketID = 44006000 + counter++;
776        final boolean[] optionals   = { false, };
777        
778        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
779        String requestJSON = WriteJSON.get(
780            parameterTypes.get("getCredentials"),
781            parameterNames.get("getCredentials"),
782            optionals, webSocketID,
783            "WebAuthn.getCredentials",
784            authenticatorId
785        );
786        
787        // 'JSON Binding' ... Converts Browser Response-JSON to 'WebAuthn.Credential[]'
788        Function<JsonObject, WebAuthn.Credential[]> responseProcessor = (JsonObject jo) ->
789            (jo.getJsonArray("credentials") == null)
790                ? null
791                : RJArrIntoStream.objArr(jo.getJsonArray("credentials"), null, 0, WebAuthn.Credential.class).toArray(WebAuthn.Credential[]::new);
792        
793        return new Script<>(webSocketID, requestJSON, responseProcessor);
794    }
795    
796    /**
797     * Removes a credential from the authenticator.
798     * 
799     * @param authenticatorId -
800     * 
801     * @param credentialId -
802     * 
803     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
804     * {@link Ret0}&gt;</CODE>
805     *
806     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
807     * browser receives the invocation-request.
808     *
809     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
810     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
811     * {@code >} to ensure the Browser Function has run to completion.
812     */
813    public static Script<String, JsonObject, Ret0> removeCredential
814        (String authenticatorId, String credentialId)
815    {
816        // Exception-Check(s) to ensure that if any parameters which are not declared as
817        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
818        
819        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
820        if (credentialId == null)    THROWS.throwNPE("credentialId");
821        
822        final int       webSocketID = 44007000 + counter++;
823        final boolean[] optionals   = { false, false, };
824        
825        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
826        String requestJSON = WriteJSON.get(
827            parameterTypes.get("removeCredential"),
828            parameterNames.get("removeCredential"),
829            optionals, webSocketID,
830            "WebAuthn.removeCredential",
831            authenticatorId, credentialId
832        );
833        
834        // This Remote Command does not have a Return-Value.
835        return new Script<>
836            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
837    }
838    
839    /**
840     * Clears all the credentials from the specified device.
841     * 
842     * @param authenticatorId -
843     * 
844     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
845     * {@link Ret0}&gt;</CODE>
846     *
847     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
848     * browser receives the invocation-request.
849     *
850     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
851     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
852     * {@code >} to ensure the Browser Function has run to completion.
853     */
854    public static Script<String, JsonObject, Ret0> clearCredentials(String authenticatorId)
855    {
856        // Exception-Check(s) to ensure that if any parameters which are not declared as
857        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
858        
859        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
860        
861        final int       webSocketID = 44008000 + counter++;
862        final boolean[] optionals   = { false, };
863        
864        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
865        String requestJSON = WriteJSON.get(
866            parameterTypes.get("clearCredentials"),
867            parameterNames.get("clearCredentials"),
868            optionals, webSocketID,
869            "WebAuthn.clearCredentials",
870            authenticatorId
871        );
872        
873        // This Remote Command does not have a Return-Value.
874        return new Script<>
875            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
876    }
877    
878    /**
879     * Sets whether User Verification succeeds or fails for an authenticator.
880     * The default is true.
881     * 
882     * @param authenticatorId -
883     * 
884     * @param isUserVerified -
885     * 
886     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
887     * {@link Ret0}&gt;</CODE>
888     *
889     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
890     * browser receives the invocation-request.
891     *
892     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
893     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
894     * {@code >} to ensure the Browser Function has run to completion.
895     */
896    public static Script<String, JsonObject, Ret0> setUserVerified
897        (String authenticatorId, boolean isUserVerified)
898    {
899        // Exception-Check(s) to ensure that if any parameters which are not declared as
900        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
901        
902        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
903        
904        final int       webSocketID = 44009000 + counter++;
905        final boolean[] optionals   = { false, false, };
906        
907        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
908        String requestJSON = WriteJSON.get(
909            parameterTypes.get("setUserVerified"),
910            parameterNames.get("setUserVerified"),
911            optionals, webSocketID,
912            "WebAuthn.setUserVerified",
913            authenticatorId, isUserVerified
914        );
915        
916        // This Remote Command does not have a Return-Value.
917        return new Script<>
918            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
919    }
920    
921    /**
922     * Sets whether tests of user presence will succeed immediately (if true) or fail to resolve (if false) for an authenticator.
923     * The default is true.
924     * 
925     * @param authenticatorId -
926     * 
927     * @param enabled -
928     * 
929     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
930     * {@link Ret0}&gt;</CODE>
931     *
932     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
933     * browser receives the invocation-request.
934     *
935     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
936     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
937     * {@code >} to ensure the Browser Function has run to completion.
938     */
939    public static Script<String, JsonObject, Ret0> setAutomaticPresenceSimulation
940        (String authenticatorId, boolean enabled)
941    {
942        // Exception-Check(s) to ensure that if any parameters which are not declared as
943        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
944        
945        if (authenticatorId == null) THROWS.throwNPE("authenticatorId");
946        
947        final int       webSocketID = 44010000 + counter++;
948        final boolean[] optionals   = { false, false, };
949        
950        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
951        String requestJSON = WriteJSON.get(
952            parameterTypes.get("setAutomaticPresenceSimulation"),
953            parameterNames.get("setAutomaticPresenceSimulation"),
954            optionals, webSocketID,
955            "WebAuthn.setAutomaticPresenceSimulation",
956            authenticatorId, enabled
957        );
958        
959        // This Remote Command does not have a Return-Value.
960        return new Script<>
961            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
962    }
963    
964}