Interface IUserAuthenticationPasskeys
- All Known Subinterfaces:
IUserAuthenticationUI
A passkey ceremony has two halves. The server half is here: start... returns the options of the ceremony as the JSON text of WebAuthn's PublicKeyCredentialCreationOptionsJSON or PublicKeyCredentialRequestOptionsJSON, and finish... takes the JSON text of the credential the client made (PublicKeyCredential.toJSON(), RegistrationResponseJSON or AuthenticationResponseJSON). The client half - calling navigator.credentials.create() or get() in a Browser, or the native authenticator API in iiziRun - is the client's, and carries the two texts unchanged.
The challenge of a ceremony is made by the server, kept in its memory only, bound to the client session that asked for it, and valid for one finish... call and for a few minutes. The relying party ID is the application's: the domain the application is reached at, configured per application. Nothing a client sends chooses it.
A passkey is accepted with user verification only (a fingerprint, a face or the device's PIN, checked by the authenticator and signed into its answer), so a sign-in with a passkey is a sign-in with two factors and is asked no second one. It is the proof of a second factor of its session, as a verified code is (CC-LOGIN-M11-PASSKEY-PROOF, IUserAuthenticationSecondFactor.getSecondFactorProof(IClientSessionGyro)): the session changes the second factor of the user and trusts its device.
The passkey of an account recovery (CC-LOGIN-PASSKEY-SECOND-FACTOR). A passkey that is registered by a session whose second factor was a recovery code is no proof of a second factor for 24 hours from its registration. Until then it is a first factor, as a password is: where a second factor is due of its user, finishPasskeyAssertion(IClientSessionGyro, String) signs nobody in and answers SigninResult.SUCCESS_SECOND_FACTOR_REQUIRED, and IUserAuthenticationSecondFactor.verifySecondFactor(IClientSessionGyro, String, String, int) completes the sign-in; a confirmation with it sets the time of the session and is no proof.
Every refusal of a credential is answered with the one result SigninResult.INVALID_PASSKEY; the reason is in the server log.
The policy "require a passkey where available". Where the application has it on and the client has reported a platform authenticator, a password alone signs nobody in: the password paths of IUserAuthenticationUI hold the sign-in back, recording nothing in the session, and answer with a result that is pending - SigninResult.SUCCESS_PASSKEY_REQUIRED for a user who has a passkey (the sign-in is completed by finishPasskeyAssertion(IClientSessionGyro, String)), or SigninResult.SUCCESS_PASSKEY_ENROLMENT_REQUIRED for a user who has none (completed by finishPasskeyRegistration(IClientSessionGyro, String, String)). performInternalLogin returns it as the result of its information, which then has no user; changePassword returns it; performInternalSignup, which has no result to return, throws it in its UserException, after the account is created. The sign-in waits for 5 minutes, one per client session. What the client reports about itself is its own statement: the policy asks for a passkey where the client says one is available, and cannot ask where it says none is.
- Author:
- Christopher Mindus
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final longThe age of a sign-in that still counts as recent, in milliseconds: 5 minutes.Method Summary
Modifier and TypeMethodDescriptionfinishPasskeyAssertion(IClientSessionGyro clientGyro, String responseJSON) Finishes a sign-in with a passkey: the credential is verified against the challenge ofstartPasskeyAssertion(IClientSessionGyro, boolean), the origin, the relying party ID and the stored public key, and the user is signed in and recorded in the session.finishPasskeyRegistration(IClientSessionGyro clientGyro, String responseJSON, String nickname) Finishes the registration of a passkey: the credential is verified against the challenge ofstartPasskeyRegistration(IClientSessionGyro), the origin and the relying party ID, and its public key is stored for the user.longgetAuthenticationTime(IClientSessionGyro clientGyro) Gets the time the user of the session was last authenticated: by a sign-in or by a confirmation.default longgetPasskeyChallengeValidity(IClientSessionGyro clientGyro, boolean conditional) The time a challenge the server makes for this client session is valid, in milliseconds (CC-FIX-PASSKEY-RETRY-CHALLENGE).default StringgetPasskeyRelyingPartyID(IClientSessionGyro clientGyro) Gets the relying party ID the application's passkey ceremonies use, for a message that names it (CC-FIX-SIGNIN-RECOVERY).booleanisPasskeySignInAvailable(IClientSessionGyro clientGyro) Checks if passkeys are offered to this client session: the application has them enabled, its relying party ID is known, and the client has reported that it can do WebAuthn (setPasskeyCapabilities(IClientSessionGyro, boolean, boolean, boolean)).default booleanisPasskeyUsableOnPage(IClientSessionGyro clientGyro) Checks if a passkey can be used on the page of this client session: passkeys are offered (isPasskeySignInAvailable(IClientSessionGyro)) AND the relying party ID the ceremony would use is valid for the host the page was opened on - equal to it, or a domain suffix of it - which is the check the Browser makes before any authenticator is asked (CC-FIX-SIGNIN-RECOVERY, the third amendment: athttp://localhost:8080of a server whose relying party ID is its external host, Chrome refused every creation with aSecurityError, and the page reported it as the user's cancel).booleanisRecentlyAuthenticated(IClientSessionGyro clientGyro) Checks if the user of the session was authenticated withinRECENT_SIGNIN_MS: the condition of every change of a sign-in method.listPasskeys(IClientSessionGyro clientGyro) Lists the passkeys of the signed-in user, the newest first.reauthenticate(IClientSessionGyro clientGyro, String password) Confirms the signed-in user with the password ("Confirm it is you"), before a change of a sign-in method.removePasskey(IClientSessionGyro clientGyro, String passkeyID) Removes a passkey of the signed-in user.renamePasskey(IClientSessionGyro clientGyro, String passkeyID, String nickname) Renames a passkey of the signed-in user.default booleanrenewPasskeyChallenge(IClientSessionGyro clientGyro, boolean create) Extends the challenge of the ceremony armed on a passkey button of this client session, so that the button does not run its ceremony against a challenge the server has forgotten (CC-FIX-PASSKEY-RETRY-CHALLENGE).voidsetPasskeyCapabilities(IClientSessionGyro clientGyro, boolean webAuthn, boolean platformAuthenticator, boolean conditionalMediation) Records what the client reports about itself, as the client bridge measured it.startPasskeyAssertion(IClientSessionGyro clientGyro, boolean conditional) Starts a sign-in (or a confirmation of the signed-in user) with a passkey and returns the options for the client'snavigator.credentials.get().startPasskeyRegistration(IClientSessionGyro clientGyro) Starts the registration of a new passkey for the signed-in user, or for the user of a sign-in that waits for one (SigninResult.SUCCESS_PASSKEY_ENROLMENT_REQUIRED), and returns the options for the client'snavigator.credentials.create().
Field Details
RECENT_SIGNIN_MS
static final long RECENT_SIGNIN_MSThe age of a sign-in that still counts as recent, in milliseconds: 5 minutes.- See Also:
Method Details
isPasskeySignInAvailable
Checks if passkeys are offered to this client session: the application has them enabled, its relying party ID is known, and the client has reported that it can do WebAuthn (setPasskeyCapabilities(IClientSessionGyro, boolean, boolean, boolean)). The "Sign in with a passkey" button is shown only when this method returnstrue.- Parameters:
clientGyro- The Client Session Gyro.- Returns:
- true when a passkey ceremony can be started for the client session.
isPasskeyUsableOnPage
Checks if a passkey can be used on the page of this client session: passkeys are offered (isPasskeySignInAvailable(IClientSessionGyro)) AND the relying party ID the ceremony would use is valid for the host the page was opened on - equal to it, or a domain suffix of it - which is the check the Browser makes before any authenticator is asked (CC-FIX-SIGNIN-RECOVERY, the third amendment: athttp://localhost:8080of a server whose relying party ID is its external host, Chrome refused every creation with aSecurityError, and the page reported it as the user's cancel). Every offer of a passkey - "Sign in with a passkey", the remembered passkey accounts, "Create a passkey", the passkey as the choice of a second factor, the passkey rows of the security page - is made only when this answerstrue; whether the application offers passkeys at all, and so whether the start page is the one with the passkey button or the classic one, staysisPasskeySignInAvailable(IClientSessionGyro)'s.- Parameters:
clientGyro- The Client Session Gyro.- Returns:
- true when a passkey ceremony can be started AND can succeed on the page's host.
getPasskeyRelyingPartyID
Gets the relying party ID the application's passkey ceremonies use, for a message that names it (CC-FIX-SIGNIN-RECOVERY).- Parameters:
clientGyro- The Client Session Gyro.- Returns:
- The relying party ID, null when the application offers no passkeys.
getPasskeyChallengeValidity
The time a challenge the server makes for this client session is valid, in milliseconds (CC-FIX-PASSKEY-RETRY-CHALLENGE). The arming of a passkey ceremony renews the challenge on the client before this time runs out, so that "Try again" - and any passkey button left waiting - never runs a ceremony against a challenge the server has already expired or forgotten; a stale ceremony would succeed on the device and then be refused by the server, which cost the user a wasted gesture and a second tap. The default is the production value; a development or test server may shorten it.- Parameters:
clientGyro- The Client Session Gyro.conditional-truefor the challenge of the user-name autofill, which waits on the page and is kept longer.- Returns:
- The validity in milliseconds, always positive.
renewPasskeyChallenge
Extends the challenge of the ceremony armed on a passkey button of this client session, so that the button does not run its ceremony against a challenge the server has forgotten (CC-FIX-PASSKEY-RETRY-CHALLENGE). The SignIn module calls this beforegetPasskeyChallengeValidity(IClientSessionGyro, boolean)runs out, for as long as the button waits for the user. The challenge bytes are unchanged and still single-use.- Parameters:
clientGyro- The Client Session Gyro.create-truefor a registration (navigator.credentials.create()),falsefor a sign-in or a confirmation (get()).- Returns:
- true when a challenge of that kind was under way for the client session and was extended; false when there is none (the caller stops renewing).
setPasskeyCapabilities
void setPasskeyCapabilities(IClientSessionGyro clientGyro, boolean webAuthn, boolean platformAuthenticator, boolean conditionalMediation) Records what the client reports about itself, as the client bridge measured it. The values are the client's own statement: they decide what is offered and whether the policy "require a passkey where available" applies to this client, never whether a credential is accepted.- Parameters:
clientGyro- The Client Session Gyro.webAuthn- The client can do WebAuthn (PublicKeyCredentialexists in a Browser; the passkey plug-in is present in iiziRun).platformAuthenticator- The device has a user-verifying platform authenticator (isUserVerifyingPlatformAuthenticatorAvailable()).conditionalMediation- The client can offer passkeys in the autofill of the user name field (isConditionalMediationAvailable()).
startPasskeyAssertion
String startPasskeyAssertion(IClientSessionGyro clientGyro, boolean conditional) throws UserException Starts a sign-in (or a confirmation of the signed-in user) with a passkey and returns the options for the client'snavigator.credentials.get().For a client session that is not signed in, the options name no credential: the authenticator offers the passkeys it holds for the application's relying party ID (discoverable credentials), so that no user name is sent and nothing tells whether an account exists. For a sign-in that waits for a passkey (
SigninResult.SUCCESS_PASSKEY_REQUIRED) and for a signed-in session, the options name the passkeys of that user. For a sign-in that waits for its second factor (SigninResult.SUCCESS_SECOND_FACTOR_REQUIRED) of a user who has a passkey that may be it, the options name those passkeys, and the answer goes toIUserAuthenticationSecondFactor.verifySecondFactor(IClientSessionGyro, String, String, int)with the methodpasskey(CC-LOGIN-PASSKEY-SECOND-FACTOR); the options of the autofill never do.- Parameters:
clientGyro- The Client Session Gyro.conditional-truewhen the options are for the autofill of the user name field (conditional mediation): the challenge is then kept longer.- Returns:
- The JSON text of the
PublicKeyCredentialRequestOptionsJSON. - Throws:
UserException- WithSigninResult.PASSKEYS_NOT_AVAILABLEwhen the application does not offer passkeys,TOO_MANY_INVALID_LOGINS_RETRY_LATERwhen the client source is held back, or a server or application result.
finishPasskeyAssertion
IUserAuthenticationInfo finishPasskeyAssertion(IClientSessionGyro clientGyro, String responseJSON) throws UserException Finishes a sign-in with a passkey: the credential is verified against the challenge ofstartPasskeyAssertion(IClientSessionGyro, boolean), the origin, the relying party ID and the stored public key, and the user is signed in and recorded in the session. When the session is signed in as that user already, this is a confirmation: the authentication time of the session is set to now (getAuthenticationTime(IClientSessionGyro)). Either way the session has the proof of a second factor from now on, of the methodMFAProof.METHOD_PASSKEY- but for the passkey of an account recovery in its first 24 hours, as the class says.- Parameters:
clientGyro- The Client Session Gyro.responseJSON- The JSON text of the client'sAuthenticationResponseJSON.- Returns:
- The authenticated user information. When the sign-in waits for its second factor (the passkey of an account recovery in its first 24 hours), the information has the result
SigninResult.SUCCESS_SECOND_FACTOR_REQUIREDand no user. - Throws:
NullPointerException- IfresponseJSONis null.UserException- WithSigninResult.INVALID_PASSKEYfor a credential that is refused for any reason,SigninResult.PASSKEY_REQUEST_EXPIREDwhen there is no challenge to answer (none was started, it was used, or its time ran out),TOO_MANY_INVALID_LOGINS_RETRY_LATERwhen the client source is held back, or a server or application result.
startPasskeyRegistration
Starts the registration of a new passkey for the signed-in user, or for the user of a sign-in that waits for one (SigninResult.SUCCESS_PASSKEY_ENROLMENT_REQUIRED), and returns the options for the client'snavigator.credentials.create(). The options ask for a discoverable credential with user verification and for no attestation, and name the passkeys the user has already so that an authenticator holding one is not registered twice.- Parameters:
clientGyro- The Client Session Gyro.- Returns:
- The JSON text of the
PublicKeyCredentialCreationOptionsJSON. - Throws:
UserException- WithSigninResult.RECENT_SIGNIN_REQUIREDwhen the session is not signed in or its sign-in is older thanRECENT_SIGNIN_MS(reauthenticate(IClientSessionGyro, String)or a passkey confirmation first),SigninResult.PASSKEYS_NOT_AVAILABLEwhen the application does not offer passkeys or the user has the most passkeys a user may have, or a server or application result.
finishPasskeyRegistration
PasskeyRegistration finishPasskeyRegistration(IClientSessionGyro clientGyro, String responseJSON, String nickname) throws UserException Finishes the registration of a passkey: the credential is verified against the challenge ofstartPasskeyRegistration(IClientSessionGyro), the origin and the relying party ID, and its public key is stored for the user. A sign-in that waited for the passkey is completed: the user is signed in and recorded in the session.- Parameters:
clientGyro- The Client Session Gyro.responseJSON- The JSON text of the client'sRegistrationResponseJSON.nickname- The name the user gives the passkey, null or empty for the default (the date of the registration); cut to 100 characters.- Returns:
- The registration: the new passkey, and the completed sign-in when there was one.
- Throws:
NullPointerException- IfresponseJSONis null.UserException- WithSigninResult.INVALID_PASSKEY,SigninResult.PASSKEY_REQUEST_EXPIRED, or a server or application result.
listPasskeys
Lists the passkeys of the signed-in user, the newest first. No key material is returned.- Parameters:
clientGyro- The Client Session Gyro.- Returns:
- The unmodifiable list, empty when the user has none.
- Throws:
IllegalStateException- If the session is not signed in.
renamePasskey
Renames a passkey of the signed-in user.- Parameters:
clientGyro- The Client Session Gyro.passkeyID- The passkey,PasskeyInfo.id.nickname- The new name, not empty; cut to 100 characters.- Returns:
SUCCESS;SigninResult.RECENT_SIGNIN_REQUIRED;SigninResult.INVALID_PASSKEYwhen the user has no such passkey; or a server or application result.- Throws:
NullPointerException- IfpasskeyIDornicknameis null.IllegalArgumentException- Ifnicknameis blank.IllegalStateException- If the session is not signed in.
removePasskey
Removes a passkey of the signed-in user. The authenticator keeps its copy, which the server refuses from then on.- Parameters:
clientGyro- The Client Session Gyro.passkeyID- The passkey,PasskeyInfo.id.- Returns:
SUCCESS;SigninResult.RECENT_SIGNIN_REQUIRED;SigninResult.INVALID_PASSKEYwhen the user has no such passkey; or a server or application result.- Throws:
NullPointerException- IfpasskeyIDis null.IllegalStateException- If the session is not signed in.
reauthenticate
Confirms the signed-in user with the password ("Confirm it is you"), before a change of a sign-in method. The password is checked as a sign-in checks it: counted and held back per account and per client source. On success the authentication time of the session is set to now. The confirmation with a passkey isstartPasskeyAssertion(IClientSessionGyro, boolean)andfinishPasskeyAssertion(IClientSessionGyro, String)on the signed-in session.- Parameters:
clientGyro- The Client Session Gyro.password- The password of the signed-in user.- Returns:
SUCCESS,INVALID_USER_OR_PASSWORD,TOO_MANY_INVALID_LOGINS_RETRY_LATER, or a server or application result.- Throws:
NullPointerException- Ifpasswordis null.IllegalArgumentException- Ifpasswordis empty.IllegalStateException- If the session is not signed in.
getAuthenticationTime
Gets the time the user of the session was last authenticated: by a sign-in or by a confirmation.- Parameters:
clientGyro- The Client Session Gyro.- Returns:
- The time in epoch milliseconds, zero when the session is not signed in.
isRecentlyAuthenticated
Checks if the user of the session was authenticated withinRECENT_SIGNIN_MS: the condition of every change of a sign-in method.- Parameters:
clientGyro- The Client Session Gyro.- Returns:
- true when the session is signed in and its authentication is recent.