Passkey credential management

Learn how to manage passkeys on behalf of your users

In this article we are going to outline best practices for managing passkeys for your users as a relying party. The guidance given on this page should provide you with the different mechanisms that you should consider to allow both your users and administrators to manage credentials to protect the integrity of your application.

Creating a new passkey

This was covered extensively in our section on passkey registration requests.

Retrieving a user’s passkeys

Your relying party should have a mechanism to present a list of passkey’s that belong to a user. This list could be provided to the client application for use in a “passkey management” screen.

The sample code below demonstrates code that can retrieve the list of passkeys belonging to a user. Our example outputs the entire table entry related to a passkey. Providing the full table entry does not introduce security risks even if the output was received by a hacker, the public key is no good without the corresponding private key on the authenticator.

Also keep in mind that this implementation is specific to our use of AWS Aurora as our data source, but the fundamental logic should remain the same if you are leveraging another database.

Figure 1 demonstrates sample code that can be used to retrieve all of the passkeys belonging to a user.

@Override
public Set<PublicKeyCredentialDescriptor> getCredentialIdsForUsername(String username) {
    return getRegistrationsByUsername(username).stream()
        .map(registration -> PublicKeyCredentialDescriptor.builder()
        .id(registration.getCredential().getCredentialId())
        .build())
        .collect(Collectors.toSet());
}

@Override
public Collection<CredentialRegistration> getRegistrationsByUsername(String username) {
    String keyJsonOutput = gson.toJson(username);

    final String SQL = "SELECT registration FROM credentialRegistrations WHERE username = :keyJsonOutput";

    return client.forSql(SQL)
        .withParamSets(new GetParams(keyJsonOutput))
        .execute()
        .mapToList(RegistrationDTO.class)
        .stream()
        .map(r -> gson.fromJson(r.registration, CredentialRegistration.class))
        .collect(Collectors.toList());
}

Figure 1

Editing a passkey

Once a user registers a passkey, there aren’t any reasons for them to have to make edits to the credential itself. This is not like changing a password, or phone number for SMS based OTP, where the user provides a new input. Changes to a passkey will be done by either, completely removing it from their account, or registering a new credential with another authenticator. There may be attributes to a passkey that could be changed in order to help the user experience. Some of these attributes could include:

  • Nickname used to identify the passkey

  • Field denoting the last time a passkey was used

  • Field denoting the last time a passkey’s nickname was changed

Deleting a passkey

There may be instances where a user wants to delete a passkey from their account. This could be due to a lost, stolen, or misplaced authenticator.

When removing a passkey from a user’s account, our recommendation is to “deactivate” it, rather than completely removing it from your credential repository.

The logic in your application should not attempt to utilize a credential if it is deactivated. A deactivated credential could help in instances where a user mistakenly removes their only passkey registered in their account, and they are no longer able to gain access. This also allows for your administrators to have an audit log of the passkeys that have been registered in your application.

Next we will dive into advanced topics that could help you better secure your high assurance passkey application.