Class PasskeyService
getInstance()); everything it keeps is in memory, bound to the key of a client session, limited in time and in number.The class knows no session object and no application: the user authentication interface (UserAuthenticationUIImpl) resolves them, checks the access and the throttle, and records the user; the settings of the application come as PasskeySettings. That is what lets the tests run every ceremony on recorded authenticator responses.
The options. A registration asks for a discoverable credential with user verification, offers ES256, EdDSA and RS256, asks for no attestation, and excludes the passkeys the user has. A sign-in asks for user verification; it names the passkeys of the user when the user is known to the server already (a sign-in that waits for a passkey, a signed-in session), and none when it is not - nothing a stranger types makes the server name a credential, so nothing tells whether an account exists or has passkeys.
- Author:
- Christopher Mindus
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classA verified sign-in with a passkey.static final classWhat a client reported about itself.static final classThe refusal of an answer without a request: no challenge is under way for the client session, it was answered already, or its time ran out.static final classThe refusal of a registration for a user who has the most passkeys a user may have (SigninResult.PASSKEYS_NOT_AVAILABLE).static final classA sign-in that waits for its passkey: the password was accepted, nothing is recorded.Field Summary
FieldsModifier and TypeFieldDescriptionThe COSE algorithms offered, the preferred first: ES256, EdDSA, RS256.static final longThe time the report of a client about itself is kept: 12 hours.static final longThe time a challenge is valid: 5 minutes; 15 minutes for the autofill of the user name field, which waits on the page until the user picks a passkey.static final intThe timeout the options give the client for the ceremony, in milliseconds.static final longThe time a challenge is valid: 5 minutes; 15 minutes for the autofill of the user name field, which waits on the page until the user picks a passkey.static final intThe most sign-ins that wait, and the most client reports that are kept.static final intThe most passkeys a user may have for one relying party.static final intThe most sign-ins that wait, and the most client reports that are kept.static final longThe time a sign-in waits for its passkey: 5 minutes.static final longThe time a passkey that was registered through an account recovery is no proof of a second factor, from its registration: 24 hours (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-039; D-27 of the SignIn survey).Constructor Summary
ConstructorsConstructorDescriptionPasskeyService(PasskeyChallenges challenges, LongSupplier clock) Creates a service on a challenge store and a clock: for tests.Method Summary
Modifier and TypeMethodDescriptionfinishAssertion(String session, PasskeySettings settings, String responseJSON) Finishes a sign-in or a confirmation with a passkey: the answer is verified against the challenge of the client session and the stored public key, and the use is recorded.finishRegistration(String session, PasskeySettings settings, long userID, String responseJSON, String nickname) Finishes the registration of a passkey: the answer is verified against the challenge of the client session, and the passkey is stored for the user.finishRegistration(String session, PasskeySettings settings, long userID, String responseJSON, String nickname, boolean recovery) Finishes the registration of a passkey, asfinishRegistration(String,PasskeySettings,long,String,String)does, and marks a passkey that is registered through an account recovery (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-039).getCapabilities(String session) Gets what a client reported about itself.longgetChallengeValidity(boolean conditional) The time a challenge the server makes is valid, in milliseconds (CC-FIX-PASSKEY-RETRY-CHALLENGE): whatstartAssertion(String, PasskeySettings, long, boolean)andstartRegistration(String, PasskeySettings, long, String, String)give it.Gets the counts of what the service keeps, for the tests and the log.static PasskeyServiceGets the service of the server.getPending(String session) Gets the sign-in of a client session that waits for its passkey.getSecondFactors(PasskeySettings settings, long userID) Gets the passkeys of a user that are the proof of a second factor: the passkeys of the user for the relying party of the application, but the ones that wait after a recovery.hold(String session, PasskeySettings settings, UserAuthenticationInfo info) Holds a sign-in whose password was accepted until its passkey arrives.hold(String session, PasskeySettings settings, UserAuthenticationInfo info, String secondFactor) Holds a sign-in whose password was accepted, and whose second factor when one was due, until its passkey arrives (CC-SIGNIN-MERGE-L5-L6-L8).booleanisOffered(PasskeySettings settings, String session) Checks if passkeys are offered to a client session: the application offers them and the client reported that it can do WebAuthn.booleanisRequired(PasskeySettings settings, String session) Checks if the policy "require a passkey where available" applies to a client session: the application offers passkeys and has the policy on, and the client reported WebAuthn and a user-verifying platform authenticator.static StringMakes the name of a passkey as it is stored: without control characters, cut to 100 characters; the default is the date of the registration.voidEnds the wait of a client session's sign-in: it was completed, or another sign-in took its place.booleanrenewChallenge(String session, PasskeyChallenges.Kind kind) Extends the challenge of a ceremony under way so that a passkey button left waiting does not run its ceremony against a challenge the server has forgotten (CC-FIX-PASSKEY-RETRY-CHALLENGE): the SignIn module renews the challenge of an armed button beforegetChallengeValidity(boolean)runs out.voidsetCapabilities(String session, boolean webAuthn, boolean platformAuthenticator, boolean conditionalMediation) Records what a client reports about itself.startAssertion(String session, PasskeySettings settings, long userID, boolean conditional) Starts a sign-in or a confirmation with a passkey.startAssertion(String session, PasskeySettings settings, long userID, boolean conditional, boolean secondFactor) Starts a sign-in or a confirmation with a passkey, or the second factor of a sign-in that waits for one (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-055).startRegistration(String session, PasskeySettings settings, long userID, String userName, String displayName) Starts the registration of a passkey for a user the server has authenticated.booleanwaitsAfterRecovery(StoredPasskey passkey) Checks if a passkey waits after the account recovery it was registered through (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-039): forRECOVERY_WAIT_MSfrom its registration such a passkey is no proof of a second factor.
Field Details
CHALLENGE_MS
public static final long CHALLENGE_MSThe time a challenge is valid: 5 minutes; 15 minutes for the autofill of the user name field, which waits on the page until the user picks a passkey.- See Also:
CONDITIONAL_CHALLENGE_MS
public static final long CONDITIONAL_CHALLENGE_MSThe time a challenge is valid: 5 minutes; 15 minutes for the autofill of the user name field, which waits on the page until the user picks a passkey.- See Also:
PENDING_MS
public static final long PENDING_MSThe time a sign-in waits for its passkey: 5 minutes.- See Also:
RECOVERY_WAIT_MS
public static final long RECOVERY_WAIT_MSThe time a passkey that was registered through an account recovery is no proof of a second factor, from its registration: 24 hours (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-039; D-27 of the SignIn survey). Until then it is a first factor, as a password is: where a second factor is due, the sign-in with it is asked for one.- See Also:
CAPABILITIES_MS
public static final long CAPABILITIES_MSThe time the report of a client about itself is kept: 12 hours.- See Also:
CLIENT_TIMEOUT_MS
public static final int CLIENT_TIMEOUT_MSThe timeout the options give the client for the ceremony, in milliseconds.- See Also:
MAX_PASSKEYS
public static final int MAX_PASSKEYSThe most passkeys a user may have for one relying party.- See Also:
MAX_PENDING
public static final int MAX_PENDINGThe most sign-ins that wait, and the most client reports that are kept.- See Also:
MAX_CAPABILITIES
public static final int MAX_CAPABILITIESThe most sign-ins that wait, and the most client reports that are kept.- See Also:
ALGORITHMS
Constructor Details
PasskeyService
Creates a service on a challenge store and a clock: for tests.- Parameters:
challenges- The challenge store.clock- The clock, milliseconds.- Throws:
NullPointerException- If a parameter is null.
Method Details
getInstance
getChallengeValidity
public long getChallengeValidity(boolean conditional) The time a challenge the server makes is valid, in milliseconds (CC-FIX-PASSKEY-RETRY-CHALLENGE): whatstartAssertion(String, PasskeySettings, long, boolean)andstartRegistration(String, PasskeySettings, long, String, String)give it. The arming of a passkey ceremony renews the challenge on the client before this time runs out, so that a button left waiting never runs a ceremony against a challenge the server has already forgotten.- Parameters:
conditional-truefor the challenge of the user-name autofill, which waits on the page and is kept longer.- Returns:
- The time in milliseconds.
renewChallenge
Extends the challenge of a ceremony under way so that a passkey button left waiting does not run its ceremony against a challenge the server has forgotten (CC-FIX-PASSKEY-RETRY-CHALLENGE): the SignIn module renews the challenge of an armed button beforegetChallengeValidity(boolean)runs out. The challenge bytes are unchanged and still single-use.- Parameters:
session- The key of the client session.kind- The kind of ceremony:PasskeyChallenges.Kind.ASSERTIONorPasskeyChallenges.Kind.REGISTRATION.- Returns:
- true when a challenge of the session and kind was under way and was extended.
- Throws:
NullPointerException- If a parameter is null.
setCapabilities
public void setCapabilities(String session, boolean webAuthn, boolean platformAuthenticator, boolean conditionalMediation) Records what a client reports about itself.- Parameters:
session- The key of the client session.webAuthn- The client can do WebAuthn.platformAuthenticator- The device has a user-verifying platform authenticator.conditionalMediation- The client can offer passkeys in the autofill of a field.- Throws:
NullPointerException- Ifsessionis null.
getCapabilities
Gets what a client reported about itself.- Parameters:
session- The key of the client session.- Returns:
- The report; one that says no to everything when the client reported nothing or its report is older than
CAPABILITIES_MS. - Throws:
NullPointerException- Ifsessionis null.
isOffered
Checks if passkeys are offered to a client session: the application offers them and the client reported that it can do WebAuthn.- Parameters:
settings- The settings of the application.session- The key of the client session.- Returns:
- true when a ceremony can be started.
- Throws:
NullPointerException- If a parameter is null.
isRequired
Checks if the policy "require a passkey where available" applies to a client session: the application offers passkeys and has the policy on, and the client reported WebAuthn and a user-verifying platform authenticator. The report is the client's own statement: a client that says it has no authenticator is not asked for a passkey, which is what "where available" means - the policy that holds for every client is the second factor.- Parameters:
settings- The settings of the application.session- The key of the client session.- Returns:
- true when a sign-in with a password must be completed with a passkey.
- Throws:
NullPointerException- If a parameter is null.
hold
public PasskeyService.PendingSignIn hold(String session, PasskeySettings settings, UserAuthenticationInfo info) throws PasskeyException, SQLException Holds a sign-in whose password was accepted until its passkey arrives. A sign-in of the client session that waits already is replaced.- Parameters:
session- The key of the client session.settings- The settings of the application.info- The authentication the password check made, successful, with its user.- Returns:
- The sign-in that waits.
- Throws:
NullPointerException- If a parameter is null.IllegalArgumentException- If the authentication is not a successful one with a user.PasskeyException- If too many sign-ins wait.SQLException- For SQL errors.
hold
public PasskeyService.PendingSignIn hold(String session, PasskeySettings settings, UserAuthenticationInfo info, String secondFactor) throws PasskeyException, SQLException Holds a sign-in whose password was accepted, and whose second factor when one was due, until its passkey arrives (CC-SIGNIN-MERGE-L5-L6-L8). A sign-in of the client session that waits already is replaced.- Parameters:
session- The key of the client session.settings- The settings of the application.info- The authentication the password check made, successful, with its user.secondFactor- The method of the second factor the sign-in verified before it waits, null when it verified none.- Returns:
- The sign-in that waits.
- Throws:
NullPointerException- Ifsession,settingsorinfois null.IllegalArgumentException- If the authentication is not a successful one with a user.PasskeyException- If too many sign-ins wait.SQLException- For SQL errors.
getPending
Gets the sign-in of a client session that waits for its passkey.- Parameters:
session- The key of the client session.- Returns:
- The sign-in, null when none waits or its time ran out.
- Throws:
NullPointerException- Ifsessionis null.
release
Ends the wait of a client session's sign-in: it was completed, or another sign-in took its place.- Parameters:
session- The key of the client session.- Throws:
NullPointerException- Ifsessionis null.
startAssertion
public String startAssertion(String session, PasskeySettings settings, long userID, boolean conditional) throws PasskeyException, SQLException Starts a sign-in or a confirmation with a passkey.- Parameters:
session- The key of the client session.settings- The settings of the application, with passkeys enabled.userID- The user the server knows already - of the sign-in that waits, or of the signed-in session - or zero for none.conditional-truefor the autofill of the user name field.- Returns:
- The JSON text of the
PublicKeyCredentialRequestOptionsJSON. - Throws:
NullPointerException- Ifsessionorsettingsis null.PasskeyException- If the application offers no passkeys, or too many challenges are under way.SQLException- For SQL errors.
startAssertion
public String startAssertion(String session, PasskeySettings settings, long userID, boolean conditional, boolean secondFactor) throws PasskeyException, SQLException Starts a sign-in or a confirmation with a passkey, or the second factor of a sign-in that waits for one (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-055).- Parameters:
session- The key of the client session.settings- The settings of the application, with passkeys enabled.userID- The user the server knows already - of the sign-in that waits, or of the signed-in session - or zero for none.conditional-truefor the autofill of the user name field.secondFactor-truewhen the passkey is asked for as the second factor of the user: the request names the passkeys that are the proof of one (getSecondFactors(PasskeySettings, long)), not the ones that wait after a recovery.- Returns:
- The JSON text of the
PublicKeyCredentialRequestOptionsJSON. - Throws:
NullPointerException- Ifsessionorsettingsis null.PasskeyException- If the application offers no passkeys, or too many challenges are under way.SQLException- For SQL errors.
finishAssertion
public PasskeyService.Asserted finishAssertion(String session, PasskeySettings settings, String responseJSON) throws PasskeyException, SQLException Finishes a sign-in or a confirmation with a passkey: the answer is verified against the challenge of the client session and the stored public key, and the use is recorded.- Parameters:
session- The key of the client session.settings- The settings of the application, with passkeys enabled.responseJSON- The JSON text of the client'sAuthenticationResponseJSON.- Returns:
- The verified sign-in: the passkey, whose owner is the user who signed in.
- Throws:
NullPointerException- If a parameter is null.PasskeyService.ExpiredException- If no challenge is under way for the client session.PasskeyException- If the answer is refused; the message says why.SQLException- For SQL errors.
waitsAfterRecovery
Checks if a passkey waits after the account recovery it was registered through (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-039): forRECOVERY_WAIT_MSfrom its registration such a passkey is no proof of a second factor. Its answers verify as those of every passkey do; what an answer is worth is the caller's to decide (PasskeyService.Asserted.recoveryWait).- Parameters:
passkey- The passkey.- Returns:
- true when the passkey was registered through a recovery less than 24 hours ago.
- Throws:
NullPointerException- Ifpasskeyis null.
getSecondFactors
public List<StoredPasskey> getSecondFactors(PasskeySettings settings, long userID) throws SQLException Gets the passkeys of a user that are the proof of a second factor: the passkeys of the user for the relying party of the application, but the ones that wait after a recovery.- Parameters:
settings- The settings of the application.userID- The user ID.- Returns:
- The passkeys, the newest first; empty when the user has none, and when the application has no relying party ID.
- Throws:
NullPointerException- Ifsettingsis null.SQLException- For SQL errors.
startRegistration
public String startRegistration(String session, PasskeySettings settings, long userID, String userName, String displayName) throws PasskeyException, SQLException Starts the registration of a passkey for a user the server has authenticated.- Parameters:
session- The key of the client session.settings- The settings of the application, with passkeys enabled.userID- The user ID.userName- The user name: the name of the account the authenticator shows.displayName- The name of the person, null or blank for the user name.- Returns:
- The JSON text of the
PublicKeyCredentialCreationOptionsJSON. - Throws:
NullPointerException- Ifsession,settingsoruserNameis null.PasskeyService.LimitException- If the user hasMAX_PASSKEYSpasskeys.PasskeyException- If the application offers no passkeys, or too many challenges are under way.SQLException- For SQL errors.
finishRegistration
public StoredPasskey finishRegistration(String session, PasskeySettings settings, long userID, String responseJSON, String nickname) throws PasskeyException, SQLException Finishes the registration of a passkey: the answer is verified against the challenge of the client session, and the passkey is stored for the user.- Parameters:
session- The key of the client session.settings- The settings of the application, with passkeys enabled.userID- The user the server has authenticated NOW, as when the registration was started.responseJSON- The JSON text of the client'sRegistrationResponseJSON.nickname- The name the user gives the passkey, null or blank for the default.- Returns:
- The stored passkey.
- Throws:
NullPointerException- Ifsession,settingsorresponseJSONis null.PasskeyService.ExpiredException- If no challenge is under way for the client session.PasskeyException- If the answer is refused; the message says why.SQLException- For SQL errors.
finishRegistration
public StoredPasskey finishRegistration(String session, PasskeySettings settings, long userID, String responseJSON, String nickname, boolean recovery) throws PasskeyException, SQLException Finishes the registration of a passkey, asfinishRegistration(String,PasskeySettings,long,String,String)does, and marks a passkey that is registered through an account recovery (CC-LOGIN-PASSKEY-SECOND-FACTOR, T1-039).- Parameters:
session- The key of the client session.settings- The settings of the application, with passkeys enabled.userID- The user the server has authenticated NOW, as when the registration was started.responseJSON- The JSON text of the client'sRegistrationResponseJSON.nickname- The name the user gives the passkey, null or blank for the default.recovery-truewhen the user got here through an account recovery: the passkey is stored with the mark, and is no proof of a second factor forRECOVERY_WAIT_MS(waitsAfterRecovery(StoredPasskey)).- Returns:
- The stored passkey.
- Throws:
NullPointerException- Ifsession,settingsorresponseJSONis null.PasskeyService.ExpiredException- If no challenge is under way for the client session.PasskeyException- If the answer is refused; the message says why.SQLException- For SQL errors.
nickname
Makes the name of a passkey as it is stored: without control characters, cut to 100 characters; the default is the date of the registration.- Parameters:
nickname- The name the user gave, null or blank for the default.now- The time of the registration, epoch milliseconds.- Returns:
- The name, never empty.
getCounts