WebAuthnRegistration
Overview
The plugin comes with an administration flow for registering and managing FIDO2 credentials. The inbuilt flow represents the minimum viable product for implementing such a feature. In the future other plugins may provide this functionality.
The registration flow can be accessed by navigating to:
http[s]://hostname/idp/profile/admin/webauthn-registration
Configuration Steps
Other than following steps 1 and 2, you do not need to change any of the default registration options to register and use a FIDO2 credential with the IdP successfully. If you do want more control over the process, there are a few options available:
(Required) Decide how the user should authenticate to the registration flow.
(Required) Configure a suitable access control policy.
(Optional) Configure how user account details are passed to the WebAuthn API and authenticator.
(Optional) Decide what type of authenticator you want to support.
(Optional) Decide if you want to require only ‘trusted’ (from FIDO Alliance Metadata) authenticators to be registered
Integration with the authentication flow
The registration flow uses the normal authentication process of the IdP to verify the user that intends to register a credential. It is recommended to implement authentication through the MFA flow, and the following instructions assume you have completed that step.
By default, the flow collects the username as a first step so it can look up any existing credentials before passing control to the authentication system. This information can be accessed in the MFA configuration using the isWebAuthnAvailable()
method on the WebAuthnRegistrationContext
, to decide which flow to use. The example MFA Flow With Password Fallback is a suitable initial setup that allows users to register their first credentials upon inputting their username and password; other combinations are possible.
AccessPolicy Configuration
The user and client accessing the registration flow are subject to an AccessControlConfiguration set by the property idp.authn.webauthn.admin.registration.accessPolicy. The default policy, AccessByCurrentUser
, is not defined in the IdP's access control configuration and needs to be added for the flow to load: you can add it to the map in conf/access-control.xml like so:
...
<entry key="AccessByCurrentUser">
<bean parent="shibboleth.PredicateAccessControl">
<constructor-arg>
<bean id="AccessByCurrentUserPredicate"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AllowCurrentUserAccessPredicate">
</bean>
</constructor-arg>
</bean>
</entry>
...
This policy is essential if you plan on creating an MFA flow (such as MFA Flow With Password Bypass) which provides another authentication method if the user has no registered credentials (e.g., either using isWebAuthnAvailable()
or when signalling a custom event).
The AccessByCurrentUser
policy checks the username found in the registration context—collected by the username view when the user first enters the registration flow—is the same as the principal name (identity) of the user that authenticated. This is important, if we imagine a scenario where a nefarious user wanted to ‘change' or possibly 'downgrade’ a user’s authenticate method from WebAuthn to, say, username and password (or whatever else was configured as a ‘backup’), all they would need to do is enter a non-existent username into the username collection step of the registration page and then have the MFA flow direct them to a different flow (because they do not have any registered credentials) where, for example, they can proceed to try different usernames and passwords for authentication. Importantly, the registration flow uses the principal name of the authenticated user to register credentials against.
The AccessByCurrentUser
policy safeguards this by ensuring that the username initially entered into the registration page—for which the credentials are retrieved, and any flow decision is based—matches the authenticating user's principal name. If they do not match, access to the registration page is denied. This measure prevents a user from switching usernames between the registration and authentication flows.
Access Policies and Subject Canonicalization
There are some caveats to consider when enabling the access policy. The principal name that results from authentication is determined by the Authentication flow, the SubjectCanonicalization flow, and possibly any transformations that have been applied to the username. It is, therefore, entirely possible that the user who entered the registration flow is the same as the user who authenticated, but their username and principal name do not match.
To address this, the registration process collects the username from the username input view and applies two transformations. The first transformation utilizes configurable string manipulations, while the second executes the SubjectCanonicalization subflow. Together, these two steps create a final output, which is then matched against the principal name obtained during the authentication process.
Registration Username String Transformations
The following properties control the string transformations applied to the registration username:
idp.authn.webauthn.registration.username.uppercase : upper case the username?
idp.authn.webauthn.registration.username.lowercase : lowercase the username?
idp.authn.webauthn.registration.username.trim : trim the username?
You can supply a custom transformation by defining the bean, shibboleth.authn.webauthn.registration.UsernameTransformations.
Note, if you do this and you are using the passwordless authentication mode, you will need to supply the same transformation settings to the authentication flow, otherwise, it will not match with the username used to register the credential.
idp.authn.webauthn.passwordless.username.uppercase : upper case the username?
idp.authn.webauthn.passwordless.username.lowercase : lower case the username?
idp.authn.webauthn.passwordless.username.trim : trim the username?
You can supply a custom transformation by defining the bean, shibboleth.authn.webauthn.passwordless.UsernameTransformations.
Subject Canonicalization
The SubjectCanonicalization subflow is run immediatly after username collection. By default, the IdP wide shibboleth.PostLoginSubjectCanonicalizationFlows are run, but a different list of flows can be configured by setting the property idp.authn.webauthn.registration.c14n.postUsernameFlows.
Custom Comparison Predicate
If the default comparison predicate used by the access policy is not sufficient for your needs. You can create a different comparison predicate and plug it into AccessByCurrentUser
: see the example below.
Disabling Registration Username Collection
It is possible to turn off the username collection step in the registration flow to simplify this problem by setting the property idp.authn.webauthn.registration.collectUsername to false. You’d then need to think about how best to support users trying to register their first credential.
User Identity for Credential Generation
The IdP will pass user account details to the WebAuthn API during registration. Some of this information is used to improve the user experience whilst creating credentials, and the authenticator uses some to bind credentials to user accounts at the IdP. The generation of this information is described in the following sections.
User ID (UserHandle) Population
The user ID (user.id) should be the primary key of the user account within the IdP. It must not exceed 64 bytes in length and should not include personally identifiable information. The ID should not change for a given user and is used by the authenticator to bind a credential to a user during registration with the IdP. In an authentication response, it is returned as the user handle.
In the federated context, the credential is registered against the IdP and so the user ID of the credential will be the same no matter who the requesting, upstream, service provider is—an authenticator will only store one credential for the IdP per user ID.
By default, the user ID is generated at runtime using a random byte sequence of 64 bytes. To ensure that each user has only one ID, an ID will only be generated if the authenticated user does not already have one linked to an existing credential.
You may prefer to pull this value from the attribute resolver. This can be achieved by modifying the following properties. in conf/authn/webauthn-registration.properties:
change the property idp.authn.webauthn.registration.userid.strategy to reference the bean shibboleth.authn.webauthn.registration.AttributeContextUserIdLookupStrategy.
ensure the attribute resolver is enabled after authentication, idp.authn.webauthn.admin.registration.resolveAttributes=true.
decide which attribute from the resolver context to use using idp.authn.webauthn.registration.userid.attributeId.
Note, the AttributeContextUserIdLookupStrategy requires the attribute to be a single StringAttributeValue converted to a byte array assuming a UTF-8 character set.
Complete control over the strategy is possible by defining your own bean in conf/authn/webauthn-registration-config.xml of type Function<ProfileRequestContext, byte[]>
and referenced by the idp.authn.webauthn.registration.userid.strategy property in conf/authn/webauthn-registration.properties
User Name Population
The WebAuthn name (user.name) is a human-palatable identifier for a user’s account a credential is associated with. This is separate from the internal username the IdP uses to store the credential against and is only used by the authenticator to help the user select the correct credential to authenticate with. It may be truncated by the authenticator to 64 bytes.
By default, this is taken from the principal name of the user who authenticated (contained in the SubjectContext). You may prefer to pull this value from the attribute resolver: this is supported out of the box by utilizing a built-in strategy, which can be activated by modifying the following properties in conf/authn/webauthn-registration.properties:
change the property idp.authn.webauthn.registration.name.strategy to reference the bean shibboleth.authn.webauthn.registration.AttributeContextWebAuthnNameLookupStrategy.
ensure the attribute resolver is enabled after authentication, idp.authn.webauthn.admin.registration.resolveAttributes=true.
decide which attribute from the resolver context to use using idp.authn.webauthn.registration.name.attributeId.
Complete control over the strategy is possible by defining your own bean in conf/authn/webauthn-registration-config.xml of type Function<ProfileRequestContext, String>
and referenced by the idp.authn.webauthn.registration.name.strategy property in conf/authn/webauthn-registration.properties
User Display Name Population
The user display name (user.displayName) is a human-palatable name for the user’s account. It is only intended for display to the user during registration. It is not used during authentication. Some authenticators do not currently show the display name.
By default, the user’s display name is taken from the principal name in the SubjectContext. That is, the canonical principal name of the subject that is authenticated to the registration endpoint. However, you may prefer to pull this value from the attribute resolver. This is supported by changing the following properties in conf/authn/webauthn-registration.properties:
change the property idp.authn.webauthn.registration.displayname.strategy to reference the bean shibboleth.authn.webauthn.registration.AttributeContextDisplayNameLookupStrategy.
ensure the attribute resolver is enabled after authentication, idp.authn.webauthn.admin.registration.resolveAttributes=true.
decide which attribute from the resolver context to use using idp.authn.webauthn.registration.displayname.attributeId.
Complete control over the strategy is possible by defining your own bean in conf/authn/webauthn-registration-config.xml of type Function<ProfileRequestContext, String>
and referenced by the idp.authn.webauthn.registration.displayname.strategy property in conf/authn/webauthn-registration.properties
Credential Generation Options
Discoverable Credentials (Passkeys)
A Discoverable Credential, formally a Resident Key, stores the private key and associated metadata on the authenticator. This allows usernameless flows and is strictly what is required for a FIDO2 credential to be a ‘passkey’. The alternative allows the private key to be stored, in a protected format, at the relying party (for example, packaged inside the credential ID). Noting that some security keys have limited space for Discoverable Credentials.
Technically, a Discoverable Credential can either be synchronised via some sync fabric (a multi-device or synched passkey e.g, cloud-synched via iCloud Keychain) or bound to the device (single-device or device-bound passkey e.g, a security key). Note that currently there is no way in the plugin to set or detect the different types, the W3C work on this is still a working draft.
During the registration of a FIDO2 credential, you can explicitly configure the ResidentKeyRequirement by setting the idp.authn.webauthn.registration.residentKey property. The possible values, set in conf/authn/webauthn-registration.properties, are listed below:
Option | Default | Description |
---|---|---|
idp.authn.webauthn.registration.residentKey | preferred | Require a residentKey/DiscoverableCredential (passkey) to be created when registering a credential. One-of 'discouraged', 'preferred', 'required' |
If you ‘preferred’ Discoverable Credentials during registration but require them for authentication, you may end up in a situation where the user registers a credential they can not later use.
Authenticator Attachment Options
Authenticator attachment modality specifies the mechanism by which a client can communicate with an authenticator. For example, can the authenticator be removed and ‘roam’ across platforms like a security key, or is the authenticator internal to the device and attached to the given platform like a Trusted Platform Module.
This distinction between roaming and platform attachments can be confusing when a platform authenticator can also be used in a roaming context. The most common example of this is that of a mobile device authenticator: when authenticating on the device itself it acts as a platform authenticator, but when authenticating another client via cross-device authentication (QR code and Bluetooth) it acts as a roaming authenticator.
During the registration of a FIDO2 credential, you can explicitly configure the AuthenticatorAttachment by setting the idp.authn.webauthn.registration.authenticatorAttachment property. The possible values, set in conf/authn/webauthn-registration.properties, are listed below:
Option | Default | Description |
---|---|---|
idp.authn.webauthn.registration.authenticatorAttachment | any | The authenticator attachment (authenticator type) requirement. One-of 'any', 'cross-platform', or 'platform' |
Attestation Conveyance
Attestation allows the IdP (acting as a WebAuthn RP) to verify the provenance of the authenticator used when registering a FIDO2 credential. This is provided in the form of an attestation statement. Attestation is optional as it can provide a poor user experience (the user must consent to the release of the attestation statement during registration), has an unclear meaning if the credential is synchronised around multiple devices (what created it might not be the same as what eventually uses it), and is a possible privacy concern (adds another data point for fingerprinting).
During registration of a FIDO2 credential, you can explicitly configure the AttestationConveyencePreference by setting the idp.authn.webauthn.registration.attestationConveyancePreference property. The possible values, set in conf/authn/webauthn-registration.properties, are listed below:
Option | Default | Description |
---|---|---|
idp.authn.webauthn.registration.attestationConveyancePreference | none | How should the attestation be conveyed during registration? One-of 'none', 'indirect', 'direct', or 'enterprise'. Indirect allows the client to anonymise the attestation. Enterprise may include uniquely identifying information and might be required to only allow organizationally attested authenticators. |
Noting, if you want to use the basic support for only allowing credentials to be registered from ‘trusted’ authenticators that are contained within the FIDO metadata feed, you’d typically need to enable ‘direct’ attestation.
User Verification
User verification requires an authenticator to authorise a user to create or use credentials. Typically this involves some kind of authorization guester such as fingerprint recognition, face recognition, or a PIN. Whilst this is required during authentication to enable passwordless or usernameless modes, you can separately choose how it is handled during registration.
During the registration of a FIDO2 credential, you can explicitly configure the UserVerificationPreference by setting the idp.authn.webauthn.registration.userVerification property. The possible values, set in conf/authn/webauthn-registration.properties, are listed below:
Option | Default | Description |
---|---|---|
idp.authn.webauthn.registration.userVerification | discouraged (so a registered key can be used as a second factor as well as a first factor) | Require User Verification on registration. One-of ‘discouraged’, ‘preferred’, ‘required’. |
Note that if user verification is required during registration, you may exclude authenticators that are usable in second-factor scenarios (which don’t require a user-verification-capable authenticator).
Other Registration Options
The following additional registration options can be set in conf/authn/webauthn-registration.properties.
Option | Default | Description |
---|---|---|
idp.authn.webauthn.preferredPublicKeyParams | EdDSA,ES256,ES384,ES512,RS1,RS256,RS384,RS512 | The preferred set of COSE signature algorithms which a created credential will use. The sequence is ordered from the most preferred to the least. The client makes the best effort to create the most preferred it can. |
Credential Registration Policies
The registration flow comes with a basic, extendable, policy engine for accepting and rejecting credentials based on the authenticator that created them. To enable policy checks, set the property idp.authn.webauthn.registration.authenticator.policy.enabled to true in conf/authn/webauthn-registration.properties.
Policies typically work off an Authenticator’s Attestation GUID (AAGUID) and require attestation statements from the authenticator. See the attestation conveyance section on how to configure this.
The default policy is defined by a list shibboleth.authn.WebAuthn.registration.ChainedRegistrationPolicyList of policies configured in conf/authn/webauthn-registration-config.xml. The following policies are included out of the box:
Policy Name | Description | Value |
---|---|---|
AllowlistAuthenticatorPolicy | An allowed list of authenticators based on their Authenticator Attestation GUID (AAGUID) | The comma-separated list of authenticators can be directly specified in the XML configuration or, for convenience, set by the idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators property. |
Labelling Credentials
In the registration interface, authenticators and credentials can be enhanced with labels using a basic and extensible labelling engine. Labels are for display purposes only and do not add any extra functionality to the registration or authentication process.
The labelling engine works for both the user registration view and the admin registration view. It is enabled by default and controllable by the list bean shibboleth.authn.WebAuthn.CredentialLabellerList defined in both conf/authn/webauthn-registration-config.xml and conf/authn/webauthn-management-config.xml. The following labellers are included out of the box:
Labeller | Description | Default Label | Value |
---|---|---|---|
SecondFactorOnlyCredentialLabeller | A list of authenticators based on their Authenticator Attestation GUID (AAGUID) that can only be used for second-factor authentication. | SecondFactorOnly | 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. |
PasskeyCredentialLabeller | Labels credentials as ‘passkey’s if they are discoverable credentials. | Passkey | Defined in either conf/authn/webauthn-registration-config.xml or conf/authn/webauthn-management-config.xml |
Inline Registration
During the authentication process, the user has the option to 'Register a new credential.' This action will guide them to a new registration flow where they can create a credential. Once the registration is complete, the registration page will display a button to 'Resume' authentication. Clicking this button will return the user to the point in the authentication flow they left off, allowing them to continue the authentication process.
This feature is enabled by default but can be disabled using the property idp.authnwebauthn.registration.allowInline in conf/authn/webauthn-registration.properties.