Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Anchor
WebAuthnFlows
WebAuthnFlows
Overview

The WebAuthn flow is capable of running can run as either a second single-factor authentication (similar to U2F, but using the WebAuthn APIs) or as a first and only (sole) factor of authentication which could still satisfy multi-factor requirements . For example, device bound passkeys such as those found on a hardware security key, incorporate two factors; something the user has and something the user is. However, multi-device synched passkeys are not, technically, something you have—although they may still provide adequate multi-factor authentication assurances in certain circumstances. When acting in a single-factor authentication mode, the flow can be configured as either a usernameless (passkey) flow or passwordless flow: this is toggled using the (see Authentication Context Classes for more information).

When operating in sole-factor authentication mode, the flow can be set up in two ways: a usernameless (true passkey) authentication mode, where the user is identified implicitly by their chosen credential, or in a passwordless authentication mode, where the user must first provide their username to identify themselves. Deciding between usernameless and passwordless modes is configured by toggling the usernameless property idp.authn.webauthn.usernameless.enabled property.

...

(it is false by default, denoting a passwordless mode).

Anchor
AuthnFlows
AuthnFlows
Authentication Flows

...

A usernameless flow does not require the user to enter their username during authentication, the user is implicitly identified from the credential they choose on the authenticator. To support a usernameless flow, the authenticator must allow Discoverable Credentials (previously known as a Resident Key and now referred to as a passkey) where the private key and associated metadata is are stored on the authenticator (FIDO2 compatible authenticators should work). This is important, as the IdP, without a known username, will not be able to preselect a user and credential to use; this must come instead from the user selecting the correct credential—suitable for the IdPs origin—from the authenticator itself.

...

Anchor
passwordless-authn
passwordless-authn
Passwordless flow

Collecting the username is the initial step in a passwordless flowA passwordless flow first requires the collection of the user’s username, and therefore, it does not require storing credentials on the authenticator. Instead, for example, the credential can be encrypted and stored on the server (possibly in the credential ID sent to the server during registration and returned by the IdP during authentication). Additionally, the authenticator can use the set of user credentials known to the IdP to narrow down the possible options available for selection.

During authentication, the authenticator is required to:

  • test the user is present by using some form of authorization gesture (for example, by touching the authenticator or clicking on a key to use), and

  • verify the user’s identity by some form of local authorization (for example, using a pin PIN code or biometric recognition), and

  • test the credential requested by the IdP and used by the authenticator is allowed (has been registered with the IdP).

...

Note

The passwordless flow will pass the credential IDs of the user to the browser in the allowCredentials request option. This could be used to perform username enumeration. If you are worried about this, either configure the usernameless or second factor flows.

Anchor
2fa-authn
2fa-authn
Second factor authentication (2FA) flow

The WebAuthn flow will operate in second-factor mode automatically only if the following three conditions are met. First, the

  1. The property idp.authn.webauthn.2fa.enabled

...

  1. is set to true.

...

  1. A previous authentication factor

...

  1. has produced an

...

  1. AuthenticationResult from the MFA context.

...

  1. A principal name

...

  1. has been found through a lookup strategy, by default from the C14N context or the Session Context using the CanonicalUsernameLookupStrategy.

If these conditions are not met, authentication will revert to either a passwordless or usernameless flow (depending on which is enabled).

...

  • test the user is present by using some form of authorization gesture (for example, by touching the authenticator or clicking on a key to use), and

  • test the credential used is allowed (has been registered with the IdP).

This Note, this mode must be used within an appropriate MFA flow, where authn/WebAuthn is used as the second factor.

Anchor
MFAFlowConfig
MFAFlowConfig
Configuration of the MFA flow

Before you start, make sure to enable the MFA login module and configure the flow in conf/authn/authn.properties. It should be possible to You could run the WebAuthn plugin as a standalone authentication method for the IdP. However, it is designed to work within a multi-factor authentication (MFA) flow . Even even when used alone to provide single-factor authentication, this design allows for greater flexibility when configuring authentication routes. For example, if no credentials are available for WebAuthn authentication, it can fall back to using a password.

...

In its simplest form, you can set up the WebAuthn plugin to be as the only means of authentication in either  usernameless  or or passwordless modes.

Note, that configuring only WebAuthn authentication will also require a FIDO2 credential to access the registration flow. HenceConsequently, unless you have another mechanism of seeding the registration of credentials within the credential repository, the user will not be able to access the registration page: please see the final example for a possible solution to this.

...

WebAuthn as a second factor of authentication

As described in the second-factor authentication section, the WebAuthn flow can be used as a second factor of authentication where the user only needs to demonstrate possession of a registered credential (typically via a user gesture such as pressing a physical or virtual button). A simple MFA configuration which runs the Password flow before determining if the WebAuthn flow should run is shown below:

Expand
title2nd Factor WebAuthn MFA Config

conf/authn/mfa-authn-config.xml

Code Block
languagexml
<util:map id="shibboleth.authn.MFA.TransitionMap">    
        <entry key="">
          <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password" />
      </entry>
      <entry key="authn/Password">
          <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor" />
      </entry>
  </util:map>   
    
  <!-- If password flow is not enough, use WebAuthn flow as a 2nd factor. Although that will not work if no credentials -->
  <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
      <constructor-arg>
          <value>
          <![CDATA[
              nextFlow = "authn/WebAuthn";
              // Check if second factor is necessary for request to be satisfied.
              authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
              mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
              if (mfaCtx.isAcceptable()) {
                  nextFlow = null;
              }
              nextFlow;   // pass control to second factor or end with the first
          ]]>
          </value>
      </constructor-arg>
  </bean>

Anchor
MFAFlowConfigRegistration
MFAFlowConfigRegistration
WebAuthn as the sole factor

...

with password fallback for registration

The following MFA configuration establishes the WebAuthn plugin as a sole, single-factor, authentication method. However, it also permits the auth/Password flow to be used if the user is in a WebAuthn Registration registration flow and has not yet enrolled any FIDO2 credentials. This allows users to register their first FIDO2 credential by authenticating against their username and password (and possibly a different second-factor). After the first credential has been enrolled, there is no fallback option, and subsequent registration attempts will require a FIDO2 credential.

...

Note

If you follow this approach you MUST ensure you use (and think about) a suitable access control policy to prevent users from ‘downgrading’ the authentication mechanism used, see the credential registration section for more information.

Anchor
CustomEventsOnNoCreds
CustomEventsOnNoCreds
Signalling custom events when the user has no registered credentials

In case the user has not registered any FIDO2 credentials, there There are two ways to signal a custom event to the IdP if the user has not registered a FIDO2 credential. The first signal can be emitted just after collecting the username for the passwordless or 2fa 2FA flows. The second signal can be emitted after the authenticator has sent the attestation (authentication) response back to the IdP—this is the only signal you can expect from the usernameless (passkey) flow since there is no username collection step.

For the passwordless and second factor flows, setting idp.authn.webauthn.passwordless.signalEventOnNoCredentialsto true will signal a custom event ID (described by the property idp.authn.webauthn.passwordless.noCredentialsEventId) If the user ( identified by the username entered in the username input step ) has no registered FIDO2 credentials. This will signal a custom event is signalled immediately after username collection, ending the flow. To have return controlled to the MFA orchestration layer, the event must be described to the system in the conf/authn/authn-events-flow.xml in the usual way (see the example below).

For all flows, but specifically for a usernameless (passkey) flow, setting idp.authn.webauthn.signalEventOnNoCredentialsRegisteredForUserHandleto true will signal a custom eventID (described by the property idp.authn.webauthn.userHandleNoRegisteredCredentialsEventId) if the user, identified by the userHandle (user.id) in the authenticator's attestation response, does not belong to a known user with at least one registered FIDO2 credential. The event is signalledjust beforethe attestation (authentication) response is validated. Again, the event must be described to the system in the conf/authn/authn-events-flow.xml in the usual way if it is to be used as a transition in the MFA flow (see the example below).

...

Note

Using this feature needs careful consideration. You probably do not, for example, want to use signalEventOnNoCredentialsRegisteredForUserHandle as a signal to allow a user to drop back into a username and password flow because anybody could bypass WebAuthn authentication just by using a credential they know does not exist (although the ‘attacker’ would need to have an authenticator with a credential registered for that TLS protected origin, e.g., a previously registered and remove removed key for a different account).

WebAuthn as the sole factor, using custom events to drive a fallback for registration and authentication

Some of the MFA configurations discussed so far allow a different authentication method methods to run as a fallback if the user has no registered FIDO2 credentials. This requires the registration step to provide its own username collection page so a decision can be made about which flow to follow when the user has not registered any credentials. This only affects registration, if a user performs authentication without first registering a FIDO2 credential, the authentication flow will ultimately fail. This is probably a good thing, however, for flexibility it is possible to disable username collection on the registration flow and delegate ‘NoCredentials’ signalling to the authentication flow using custom events.

To disable username collection on the registration flow, set the idp.authn.webauthn.registration.collectUsername propertyto false. Then, configure the MFA flow to trigger a different authentication flow if the user has no credentials—following the examples in the custom events topic. It is worth noting this will affect registration and standard authentication. That is, any user without credentials will end up signalling those custom events, and, if the MFA flow is set up with a fallback flow, they will authenticate using that fallback. For registration you can of course require that the fallback method (all methods really) was of a particular quality by setting the default authentication methods property idp.authn.webauthn.admin.registration.defaultAuthenticationMethods to require MFA (or whatever you want). Similarly, if the Service Provider has requested MFA in their request, the authentication fallback for a standard authentication also needs to support itauthentication must produce an authentication result that satisfies the request.

WebAuthn as the sole factor, using custom events to drive a different two-factor fallback for authentication and registration

Enabling event signalling signaling when a user does not yet have a registered FIDO2 credential (see the previous section), allows us to configure a different an alternative type of two-factor authentication fallback. For example, the following MFA configuration will first run initiate the WebAuthn flow, which will fail if the user has no existing does not have any registered credentials, it then proceeds . If that occurs, the process will then proceed to the auth/Password flow and. If necessary (for instance, if required (e.g, requested by the SPservice provider), to it will also include the authn/DuoOIDC flow . If either registration or admin flows are configured to to perform a second-factor authentication. After users register their first key, they will use the WebAuthn plugin for authentication exclusively.

Info

If either registration or admin flows are configured to require MFA (by setting their default authentication methods), they will also fall back to authn/Password and authn/DuoOIDC.

...

Expand
titleWebAuthn MFA Flow With Password and Duo fallback

conf/authn/mfa-authn-config.xml

Code Block
<util:map id="shibboleth.authn.MFA.TransitionMap">
    
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkPasswordOrWebAuthnForRegistration" />            
        </entry>       
        
         <entry key="authn/WebAuthn">
            <bean parent="shibboleth.authn.MFA.Transition">
                <property name="nextFlowStrategyMap">
                    <map>          
                        <entry key="NoRegisteredWebAuthnCredentials" value="authn/Password" />
                        <entry key="NoCredentialsRegisteredForUserHandle" value="authn/Password" />
                    </map>
                </property>
            </bean>
        </entry>         
        <entry key="authn/Password">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/WebAuthn" />            
        </entry>
    </util:map>
    
    <!-- If the MFA context is not acceptable from the first factor, run the DuoOIDC flow -->
    <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
        <constructor-arg>
            <value>
            <![CDATA[
                nextFlow = "authn/DuoOIDC";
                // Check if second factor is necessary for request to be satisfied.
                authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
                mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
                if (mfaCtx.isAcceptable()) {
                    nextFlow = null;
                }
                nextFlow;   // pass control to second factor or end with the first
            ]]>
            </value>
        </constructor-arg>
    </bean>

Prepopulating the WebAuthn username

...

into the authn/Password flow

When using the passwordless flow with a fallback to authn/Password, you could can modify the login.vm view to pre-fill the username input with the username entered into the WebAuthn context. For example, at the top of login.vm:

Code Block
#set ($webAuthnCtx= $authenticationContext.getSubcontext("net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext"))
#set ($webAuthnUsername = $webAuthnCtx.getUsernamegetRawUsername())

You Which you can then use this information to automatically fill in the username field:

Code Block
<input type="text" id="username" name="j_username" #if($webAuthnUsername)readonly#end
                        value="#if($webAuthnUsername)$encoder.encodeForHTML($$webAuthnUsername$webAuthnUsername)#end" />

See UserInterface for a detailed look at the IdP’s view templates. This won't prevent a malicious user from changing the username; that would need a server-side solution, such as a post-authentication login flow (ProfileInterceptConfiguration ). 

A malicious party could still alter the username, preventing which would require server-side measures (e.g. a post-flow step in the MFA Flow, or a post-login authentication flow). However, there is typically no need to add those since the service provider is responsible for determining the strength of the authentication required.

Anchor
ACR
ACR
Authentication Context Classes (Supported Principals)

...

The set you specify for the flow is entirely up to you based on the type of authentication flow you have configured , and what authentication assurances you want. For example, when used as a second - factor in 2FA mode, it seems feasible to signal MFA from the WebAuthn plugin. On the other hand, using the plugin alone, in either passwordless or usernameless modes, may not be enough to signal MFA unless you are certain that the credential is tied to a specific device—which device (a device-bound passkey)—which cannot currently be determined from the plugin. Even if the typical passkey process (which process—which requires user verification) combines verification—combines ‘something you are’ (biometrics) or ‘something you know’ (pin) with ‘something you have’ (an authenticator) , if the credential is synchronized across multiple devices (as is common with passkey providers like iCloud Keychain and Google Password Manager), it could be argued that the authenticator does not really qualify as 'something you have'. —although they may still provide adequate multi-factor authentication assurances in certain circumstances. Either way, it is up to you to decide how best to handle this.

Reference

...

titleProperties

...

Name

...

Type

...

Default

...

Description

Authentication Credential Policies

The authentication flow comes with a basic, extendable, policy engine for accepting and rejecting FIDO2 credentials at the point of use. To enable policy checks, set the property idp.authn.webauthn.

...

credential.policy.enabled

...

Boolean

...

false

...

to true in conf/authn/webauthn.properties.

In addition to inspecting the authenticating FIDO2 credential, a policy can also make decisions regarding the authenticator that created the credential, but only if the Authenticator Attestation GUID (AAGUID) was stored with the credential during registration. For guidance on how to configure this, refer to the attestation conveyance registration section.

The default policy is defined by a list shibboleth.authn.WebAuthn.ChainedCredentialPolicyList of policies configured in conf/authn/webauthn-config.xml. Out of the box, the following policies are included:

Policy Name

Description

Value

SecondFactorOnlyCredentialPolicyRule

A list of authenticators based on their Authenticator Attestation GUID (AAGUID) that can only be used for second-factor authentication, and will be rejected if used as a sole factor of authentication.

Even if the authenticator indicates User Verification during authentication, the credential can still be excluded. This is potentially useful for omitting untrusted software authenticators.

The comma-separated list of authenticators can be directly specified in the XML configuration or, for convenience, set by the idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators property.

Info

In the future, it will be possible to inspect the authenticator used during authentication and not just the authenticator that created the credential. This is useful if the credential has been transferred to a different authenticator.

Reference

Description
Expand
titleProperties

Name

Type

Name

Type

Default

Description

idp.authn.webauthn.usernameless.enabled

Boolean

false

Which type of flow is supported? Usernameless (true) or passwordless (false)

idp.authn.webauthn.2fa.enabled

Boolean

false

Enable this flow to act as second factor authentication

idp.authn.webauthn.2fa.allowedPreviousFactors

String list

authn/Password

Which previous factors are acceptable to allow the WebAuthn flow to act as a second factor of authentication

idp.authn.webauthn.2fa.forceSecondFactorFlow

Boolean

false

Force second factor even if no acceptable previous factors ran

idp.authn.webauthn.2fa.username.strategy

Bean reference

shibboleth.authn.webauthn.CanonicalUsernameLookupStrategy

The bean name of the username lookup strategy. By default, this comes from the principal name established by the first factor.

dp.authn.webauthn.updateSignatureCount

Boolean

true

Should we update an authenticators signature counter inside the credential repository after each successful authentication?

idp.authn.webauthn.passwordless.signalEventOnNoCredentials

Boolean

false

Should an event be built if there are no credentials found for the given user? Only applicable to passwordless authentication.

idp.authn.webauthn.passwordless.noCredentialsEventId

String

NoRegisteredWebAuthnCredentials

If an event is triggered when no credentials are found for the user, what should the event ID be?

idp.authn.webauthn.signalEventOnNoCredentialsRegisteredForUserHandle

Boolean

false

Should a custom event be built if the userHandle supplied by the authenticator during authentication is not related to any registered credentials?

idp.authn.webauthn.userHandleNoRegisteredCredentialsEventId

String

NoCredentialsRegisteredForUserHandle

If an event is triggered when no credentials can be found for the given userHandle, what should the event ID be?

idp.authn.webauthn.passwordless.2fausername.enableduppercase

Boolean

falseEnable

this flow to act as second factor authenticationBasic transformations that should be applied to the username that is collected as part of the passwordless flow

idp.authn.webauthn.2fapasswordless.username.allowedPreviousFactorslowercase

String list

authn/Password

Which previous factors are acceptable to allow the WebAuthn flow to act as a second factor of authenticationBoolean

false

Basic transformations that should be applied to the username that is collected as part of the passwordless flow

idp.authn.webauthn.passwordless.2fausername.forceSecondFactorFlowtrim

Boolean

falseForce second factor even if no acceptable previous factors ran

Basic transformations that should be applied to the username that is collected as part of the passwordless flow

idp.authn.webauthn.2fapasswordless.usernamec14n.strategypostUsernameFlows

Bean referenceref

shibboleth.authn.webauthn.CanonicalUsernameLookupStrategyPostLoginSubjectCanonicalizationFlows

The bean name of the username lookup strategy. By default, this comes from the principal name established by the first factor.

dp.authn.webauthn.updateSignatureCount

Boolean

true

Should we update an authenticators signature counter inside the credential repository after each successful authentication?

idp.authn.webauthn.passwordless.signalEventOnNoCredentials

Boolean

false

Should an event be built if there are no credentials found for the given user? Only applicable to passwordless authentication.

idp.authn.webauthn.passwordless.noCredentialsEventId

String

NoRegisteredWebAuthnCredentials

If an event is triggered when no credentials are found for the user, what should the event ID be?

idp.authn.webauthn.signalEventOnNoCredentialsRegisteredForUserHandle

Boolean

false

Should a custom event be built if the userHandle supplied by the authenticator during authentication is not related to any registered credentials?

idp.authn.webauthn.userHandleNoRegisteredCredentialsEventId

String

NoCredentialsRegisteredForUserHandle

If an event is triggered when no credentials can be found for the given userHandle, what should the event ID be?

idp.authn.webauthn.passwordless.username.uppercase

Boolean

false

Basic transformations that should be applied to the username that is collected as part of the passwordless flow

idp.authn.webauthn.passwordless.username.lowercase

Boolean

false

Basic transformations that should be applied to the username that is collected as part of the passwordless flow

idp.authn.webauthn.passwordless.username.trim

Boolean

false

Basic transformations that should be applied to the username that is collected as part of the passwordless flow

idp.authn.webauthn.passwordless.c14n.postUsernameFlows

Bean ref

shibboleth.PostLoginSubjectCanonicalizationFlows

The ID of the bean that supplies the c14n flows that are applied to the username entered during the passwordless flow

Expand
titleBeans

ID of the bean that supplies the c14n flows that are applied to the username entered during the passwordless flow

Expand
titleBeans

Name

Type

Description

shibboleth.authn.WebAuthn.ChainedCredentialPolicyList

List

List of credential policies to apply during authentication

shibboleth.authn.WebAuthn.WebAuthnAuthenticationClientFactory

WebAuthnAuthenticationClientFactory

The WebAuthn client factory that creates a WebAuthnAuthenticationClient that performs critical WebAuthn functions.

shibboleth.authn.WebAuthn.SecondFactorOverride

Predicate<ProfileRequestContext>

Should the authentication flow run in 2FA mode irrespective of the conventional logic used to determine that.

shibboleth.authn.WebAuthn.passwordless.UsernameTransformations

List<Pair<Pattern,String>>

Match patterns and replacement strings to apply to the username collected for passwordless authentication

shibboleth.authn.WebAuthn.ChallengeGeneratorStrategy

Function<ProfileRequestContext,byte[]>

The strategy used to generate the WebAuthn challenge

shibboleth.authn.WebAuthn.CredentialRepository

WebAuthnCredentialRepository

The credential repository to use

shibboleth.authn.WebAuthn.UpdateSignatureCountPredicate

Predicate<ProfileRequestContext>

A predicate that determines if the signature counter should be updated for every authentication operation.

shibboleth.authn.WebAuthn.RemoveAfterValidation

Consumer<ProfileRequestContext>

A cleanup hook that is executed on successful authentication.

shibboleth.authn.WebAuthn.AuditExtractors

MapFactoryBean

A Map of audit extractors to use

shibboleth.authn.WebAuthn.WebAuthnFidoMetadataServiceFactory

FidoMetadataServiceFactory

The FIDO metadata factory that creates a FidoMetadataService.