Introduction
Hello, I’m @Mapdu, a Ruby developer at Money Forward.
I work on Money Forward ID development team, developing a web application that serves as the ID and authentication platform for Money Forward and its group companies.
In the previous post Introducing Passkeys: Registration and Authentication, we explored how passkeys work and why they are more secure than traditional passwords.
Although passkeys are highly secure and have the potential to replace traditional passwords, making passkeys widely adopted and educating traditional users on what passkeys are and how secure they can be remains a significant challenge that requires time and effort.
In this post, we will examine two key developments:
- Automatic Passkey Upgrades feature, which enables users to seamlessly transition from passwords to passkeys.
- Signal API, which ensures passkeys on passkey providers remain consistent with public key credentials on relying party servers.
UX Challenges and Significant Efforts
At Money Forward ID, we have started several campaigns to promote passkeys, aiming to reach more users and encourage them to migrate to this new authentication method.
The number of passkey registrations has significantly increased compared to the silent release. However, data shows that up to 50% of users choose to “Skip” the registration process.
Furthermore, when users proceed to register a passkey, a browser-native modal screen is displayed, and they are prompted by the browser to register a passkey. We noticed that about 30% of users select “Cancel” even on this browser-native modal screen.
As a result, only about 35% of users who visit promotional pages actually successfully register a passkey.
This highlights that the user experience (UX) around passkeys remains a challenge, requiring more efforts to make passkeys more accessible and appealing to users.
Refer: Passkey Usage Report @ Money Forward ID (vol.2, Aug 2023)
Transition from traditional passwords to passkeys
The data above shows that UX challenges and user awareness of passkeys are significant issues.
At WWDC 2024, Apple introduced a new password manager app as part of the iOS 18, iPadOS 18, and macOS Sequoia operating systems. The app includes the previously introduced Automatic Passkey Upgrades feature, enabling users to seamlessly transition from traditional passwords to passkeys.
Take a look at the following diagram.
Refer: https://developer.apple.com/videos/play/wwdc2024/10125/
- First, when you sign in using a password that is autofilled by a password manager.
- The application (relying party) sends a passkey upgrade request to the system, which evaluates whether it makes sense to create a passkey at this time and, if appropriate, returns the new passkey. These checks include:
- Verifying if a credential manager is set up on the device and supports automatic passkey upgrades.
- Confirming whether the device is configured for passkey use, such as having a passcode, Face ID, or Touch ID.
- If the system approves the request, it forwards the request to available credential managers. The credential manager then evaluates the request against its own criteria, such as:
- Whether it was recently used to fill a username and password for the same account (i.e., the account matches the passkey being registered).
- If all conditions are met, the credential manager generates a new passkey and returns it to the app. If the conditions are not satisfied, the app receives an error.
I hope not only Apple, but also other platforms like Google and Microsoft will support this feature soon. While users may not fully understand the technical details, the primary focus at this stage is to help them transition from traditional passwords to passkeys.
In fact, implementing automatic passkey upgrades has shown some positive results. We also wrote an article about it, which you can read in more detail here: Passkey Usage Report – Money Forward ID Vol. 6, Oct 2024.
Manage passkeys
Imagine you have multiple accounts for various purposes, such as accounting, tax calculations, or attendance. Everything works smoothly until you stop using these services, and the accounts become inactive.
Even after the accounts are deleted from the system, the application may still suggest logging in with passkeys for those accounts. However, these passkeys will no longer work, resulting in error messages indicating that the passkeys either don’t exist or are no longer valid.
This happens because, while your account has been deleted from the relying party’s system, the credential still remains in your passkey providers.
Over time, repeatedly encountering a list of invalid passkeys every time you access the application can become a bad experience, potentially leading users to stop using the application.
While users can manually delete invalid passkeys from their passkey providers, this process is often complicated and requires a certain technical knowledge.
Chrome supports the Signal API, which addresses specific use cases for managing passkeys on the relying party side. It ensures passkeys on passkey providers remain consistent with public key credentials on relying party servers.
In other words, the Signal API enables the relying party to manage passkeys on behalf of the user in a seamless and user-friendly manner.
- When an account is deleted from the relying party, the corresponding passkey is also removed from the passkey providers.
- When account information is updated on the relying party, the passkey is updated in the passkey providers as well.
Passkeys in practice
For this demo, I am using:
- Ruby and JavaScript as the primary programming languages
- WebAuthn JSON, a small WebAuthn API wrapper for client-side use
- WebAuthn Ruby, a library for implementing WebAuthn on the server-side
You can try the passkey demo at https://mapdu.dev .
Source code: https://github.com/NgocHai220998/sample_idp
Automatic Passkey Upgrades
Note that: this feature is currently available only on iOS 18, iPadOS 18, macOS Sequoia, and later versions.
- This function requires the user to have saved credentials, specifically a username and password, rather than a passkey.
Here are the key components of this Automatic Passkey Upgrades flow.
- User : The individual registering a passkey for their account.
- Client : The relying party client application (e.g., web browser, mobile app) that interacts with the user and server.
- Authenticator : The user’s device (e.g., phone, hardware security key) that creates and securely stores the passkey.
- Server : The Relying Party server managing the account and validating the registration.
Step 1 – Authenticating with the relying party using a password manager
In this step, the user selects an account suggested by the password manager for authentication. The password manager automatically fills in the account details and password into the corresponding fields of the application.
Step 2 – Performing passkey upgrades
After the user successfully authenticates, the relying party determines whether the user is eligible for a passkey upgrade. These steps are typically automated and require no additional interaction from the user.
If all conditions are met, the relying party will create a new passkey for the user.
Since these steps for create new passkey are quite similar to the passkey registration process I previously described, I will not go into further detail in this article.
<script name="Automatic Passkey Upgrades">
document.addEventListener("DOMContentLoaded", async () => {
performAutomaticPasskeyUpgrades();
});
</script>
// ----- Automatic Passkey Upgrades ------
const performAutomaticPasskeyUpgrades = async () => {
if (await isConditionalCreationAvailable()) {
return await postWebauthnCredentialsForAutomaticPasskeyUpgrades();
}
throw new Error('conditionalCreation not available');
}
// This triggers passkey auto-upgrade if password manager is already unlocked by user
const postWebauthnCredentialsForAutomaticPasskeyUpgrades = async () => {
const options = await getOptions();
const CreateOptions = webauthn.parseCreationOptionsFromJSON(options)
CreateOptions.mediation = "conditional";
const credential = await webauthn.create(CreateOptions);
const credentialData = {
...credential.toJSON(),
};
sentRegistrationData(credentialData, "/");
};
Please refer to Introducing Passkeys: Registration and Authentication for more details.
Note that: Passkey auto-upgrade also works on the password edit and two-factor authentication (2FA) pages, etc., not just during sign-in, as long as the information is filled in from a password manager that supports automatic passkey upgrades.
In fact, we at Money Forward ID first tested this feature on the password edit page, as testing it on the sign-in page was deemed slightly risky.
Webauthn Signal API
Note that: this feature is currently available only on Chrome for desktop, starting from version 132. As of the time of this post, you can only try this feature using the Chrome Beta version.
The Signal API is allowing RPs to signal changes to the passkey providers.
signalUnknownCredential
: Credential does not exist, allowing the passkey providers to delete this credential.signalAllAcceptedCredentials
: Passes a list of saved credentials, allowing the passkey providers to delete any invalid credentials.signalCurrentUserDetails
: Updated username and/or display name.
1. signalUnknownCredential
By invoking PublicKeyCredential.signalUnknownCredential
with an RP ID
and a credential ID
, the relying party (RP) can notify the passkey providers that the specified credential has been removed or no longer exists.
<script name="Call signalUnknownCredential API">
const credentialId = ... // base64url encoded credential ID
if (typeof PublicKeyCredential !== 'undefined' && 'signalAllAcceptedCredentials' in PublicKeyCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId: window.location.hostname,
credentialId: credentialId
});
}
</script>
This API should be invoked when a user deletes a passkey.
2. signalAllAcceptedCredentials
By invoking PublicKeyCredential.signalAllAcceptedCredentials
with an RP ID
, a user ID
, and a list of stored credential IDs
, the relying party (RP) can notify the passkey providers about the credentials that should remain in its storage.
While it is up to the passkey provider to determine how to handle this signal, it is expected that passkeys not included in this list will be removed. This ensures that users won’t see passkeys during sign-in for which the associated credentials no longer exist.
<script name="Call SignalAllAcceptedCredentials API">
const allAcceptedCredentialIds = [...]
const userId = ...
if (typeof PublicKeyCredential !== 'undefined' && 'signalAllAcceptedCredentials' in PublicKeyCredential) {
PublicKeyCredential.signalAllAcceptedCredentials({
userId: userId,
allAcceptedCredentialIds: allAcceptedCredentialIds,
rpId: window.location.hostname,
});
}
</script>
This API should be invoked when a user deletes a passkey on the RP and on every sign-in.
3. signalCurrentUserDetails
By invoking PublicKeyCredential.signalCurrentUserDetails
with an RP ID
, user ID
, username
, and display name
, the relying party (RP) can notify the passkey providers about updated user information.
<script>
const name = 'Your Full Name'
const displayName = 'Your Display Name'
const userId = ...
if (typeof PublicKeyCredential !== 'undefined' && 'signalCurrentUserDetails' in PublicKeyCredential && userId) {
PublicKeyCredential.signalCurrentUserDetails({
userId: userID,
displayName: displayName,
name: name,
rpId: window.location.hostname,
});
}
</script>
This API can be invoked when the user’s username or display name are updated, or on every sign-in.
Conclusion
From UX challenges to user education, there are significant hurdles that need to be addressed to make passkeys the preferred method of authentication. Features like Automatic Passkey Upgrades and the Signal API represent significant advancements, as they facilitate the transition from passwords to passkeys and enhance passkey management.
I hope you found the above information helpful.
See you in another topic (^-^)