Class WebAuthnConfigurer

java.lang.Object
com.codename1.backend.security.SecurityConfigurer
com.codename1.backend.security.WebAuthnConfigurer

public final class WebAuthnConfigurer extends SecurityConfigurer

Passkeys: a user who is signed in registers one, and from then on signs in with it.

http.formLogin(Customizer.withDefaults())
    .webAuthn(passkeys -> passkeys
        .rpId("example.com")
        .rpName("Example")
        .allowedOrigins("https://example.com"));

The relying party id is the domain the passkeys belong to, and the allowed origins are where the ceremonies may run -- each exactly as a client reports it, compared whole. An Android application reports android:apk-key-hash: and the base64url SHA-256 of its signing certificate; list it beside the web origin when the application is to use the same passkeys.

The endpoints

They take and answer JSON, the forms the WebAuthn specification defines, so a browser's PublicKeyCredential.parseCreationOptionsFromJSON / parseRequestOptionsFromJSON / toJSON() and the Codename One client's WebAuthnClient work with them as they are. No page is served: the application has its own, or is an app.

Request Who Does
POST /webauthn/register/options signed in answers the options to make a passkey with
POST /webauthn/register signed in takes the authenticator's answer; {"success":true,"credentialId":"..."}, or 400 and {"success":false,"error":"..."}
DELETE /webauthn/register/{credentialId} signed in, the owner removes a passkey; 204, or 404
POST /webauthn/authenticate/options anybody answers the options to sign in with
POST /login/webauthn anybody takes the authenticator's answer and signs the user in; {"authenticated":true,"redirectUrl":"/"}, or 401 and {"authenticated":false}

POST /webauthn/register takes the credential as the client has it, with an optional label beside it or as a query parameter, or wrapped as Spring Security wraps it: {"publicKey": {"credential": {...}, "label": "..."}}.

A ceremony's options wait in the session they were asked from, for five minutes, and are taken out before the answer is looked at, so a challenge is answered once. "Signed in" means in this session: somebody a remember-me cookie brought back is asked to sign in before they may add or remove a passkey.

A refused sign-in is answered 401 and nothing more; which check refused it is in the WebAuthnException the failureHandler is given, for the server's log.

A second factor

A passkey whose authenticator verified the user -- a fingerprint, a face, a PIN -- is two factors in one step, and signs the user in without the chain's mfa() asking for a code. One that did not verify the user is one factor, as a password is, and the second factor is then asked for as it is after a password. To have every passkey sign-in be the first kind, call userVerification("required").

CSRF

On a chain that keeps a session the two sign-in requests and the three registration requests are checked for the CSRF token like any other POST and DELETE. A page sends it as it does for its other requests. An app, which has no page to read it from, is served by a chain that leaves the ceremony out of the check:

http.csrf(csrf -> csrf.ignoringRequestMatchers("/webauthn/**", "/login/webauthn"));

The four POSTs lose little by it: each answers, or is answered by, a challenge that is 32 random bytes kept in the caller's own session, which a page of another site can neither read nor guess. DELETE has no such thing; leave it under the check where browsers sign in to the same chain.

Where things come from

Passkeys are kept in the application's UserCredentialRepository bean and its PublicKeyCredentialUserEntityRepository bean -- in the database with the Jdbc implementation of each -- or in the ones given here. With neither they are kept in memory and are gone when the server stops, which a server outside a development profile says once when it starts. A user who signs in is looked up in the application's UserDetailsService, for their authorities and for whether they may sign in at all.

  • Method Details

    • rpId

      public WebAuthnConfigurer rpId(String rpId)
      The relying party id: the domain passkeys are bound to, such as example.com. Required.
    • rpName

      public WebAuthnConfigurer rpName(String rpName)
      What the user is shown as the site's name; the id unless set.
    • allowedOrigins

      public WebAuthnConfigurer allowedOrigins(String... origins)
      The origins a ceremony may run on: https://example.com, with a port when it is not the default and nothing after it; and android:apk-key-hash:... for an Android application. Required.
    • userVerification

      public WebAuthnConfigurer userVerification(String userVerification)
      Whether the authenticator must verify the user: required, preferred -- the default -- or discouraged. See the class for what it means for a second factor.
    • residentKey

      public WebAuthnConfigurer residentKey(String residentKey)
      Whether a new credential must be one a sign-in can find without being told the user: required -- the default -- preferred or discouraged.
    • authenticatorAttachment

      public WebAuthnConfigurer authenticatorAttachment(String authenticatorAttachment)
      platform for the device's own authenticator, cross-platform for a security key, null -- the default -- for either.
    • allowUnverifiedAttestation

      public WebAuthnConfigurer allowUnverifiedAttestation(boolean allow)
      Whether a registration whose attestation this server cannot verify is accepted as if it carried none; see WebAuthnRelyingPartyOperations.setAllowUnverifiedAttestation(boolean). Off unless set, and such a registration is refused with a message naming the format.
    • allowCrossOrigin

      public WebAuthnConfigurer allowCrossOrigin(boolean allow)
      Whether a ceremony may run in a frame of another origin. Off unless set.
    • usernameFirst

      public WebAuthnConfigurer usernameFirst(boolean usernameFirst)
      Whether POST /webauthn/authenticate/options may be told a username -- in a JSON body or as a parameter -- and then lists that user's credentials, so that a security key which holds no discoverable credential can answer. Off unless set, because the list tells whoever asks that the user exists and has passkeys; a passkey needs no name.
    • challengeValiditySeconds

      public WebAuthnConfigurer challengeValiditySeconds(int seconds)
      How long a ceremony's options wait for their answer; 300 seconds unless set.
    • userCredentialRepository

      public WebAuthnConfigurer userCredentialRepository(UserCredentialRepository repository)
      Where credentials are kept, in place of the application's bean.
    • userEntityRepository

      public WebAuthnConfigurer userEntityRepository(PublicKeyCredentialUserEntityRepository repository)
      Where users' handles are kept, in place of the application's bean.
    • userDetailsService

      public WebAuthnConfigurer userDetailsService(UserDetailsService userDetailsService)
      The users a passkey signs in, in place of the application's bean.
    • relyingPartyOperations

      public WebAuthnConfigurer relyingPartyOperations(WebAuthnRelyingPartyOperations operations)
      The ceremonies themselves, made by the application: everything set here about the relying party and the repositories is then its.
    • signatureCounterListener

      What is told of a signature counter that did not advance: a credential that may have been copied. The sign-in is refused either way.
    • defaultSuccessUrl

      public WebAuthnConfigurer defaultSuccessUrl(String defaultSuccessUrl)
      The redirectUrl a sign-in is answered with when no page asked for it.
    • successHandler

      public WebAuthnConfigurer successHandler(AuthenticationSuccessHandler successHandler)
      Answers a sign-in itself, instead of the JSON.
    • failureHandler

      public WebAuthnConfigurer failureHandler(AuthenticationFailureHandler failureHandler)
      Answers a refused sign-in itself, instead of the 401; it is handed the exception, which says why.
    • clock

      public WebAuthnConfigurer clock(Clock clock)
      The clock a challenge's lifetime is read from; for tests.
    • init

      public void init(HttpSecurity http)
      Description copied from class: SecurityConfigurer
      Shares what other parts need to know; nothing by default.
      Overrides:
      init in class SecurityConfigurer
    • configure

      public void configure(HttpSecurity http)
      Description copied from class: SecurityConfigurer
      Adds this part's filters; nothing by default.
      Overrides:
      configure in class SecurityConfigurer