...
Expand |
---|
title | WebAuthn 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 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.
While this would not prevent a malicious party from altering it, 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 for that to add those since the service provider is responsible for determining the strength of the authentication required authentication.
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 cannot currently be determined from the plugin. Even if the typical passkey process (which requires user verification) combines ‘something you are’ (biometrics) or ‘something you know’ (pin) with ‘something you have’ (an authenticator), if the credential is synchronized (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'. Either way, it is up to you to decide how best to handle this.
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 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. |
Reference
Expand |
---|
|
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.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 |
|
...