Search code examples
javaauthenticationwebauthnfidoyubico

WebAuthn : how to register an authenticator several time


I'm using Yubico demo to implement a webauthn server : https://developers.yubico.com/WebAuthn/WebAuthn_Walk-Through.html

I have implemented the CredentialRepository class to replace In Memory with access to a database.

Currently I am facing a problem: I want to be able to use this server so that a client can connect with a Yubikey (or Andorid phone) to several websites. I would like the customer to register his key for each site. The problem is that the Yubico server does not want us to register the same key several times on the server while keeping the same username.

Would there be a way around this problem by making the registration of a key depend on the origin of the site where the key must be registered and not on the username?

(I don't want to replace the username with a different value for comprehension issues in the database)

All ideas are welcome. Thank you.


Solution

  • What is preventing you from enrolling an authenticator multiple times for the same account lies within the PublicKeyCreationOptions that is being sent by the relying party. Within the PublicKeyCreationOptions is a field, excludeCredentials (line 22 in the screenshot link below).

    PublicKeyCreationOptions Example

    If you attempt to register a credential that has a credential ID that matches any of the items in the list, then the WebAuthn ceremony will not complete the request.

    Why is this being done?

    Users not familiar with how WebAuthn works may not realize that they only need to register an authenticator once, and that they can use that same authenticator across multiple devices. A confused user may attempt to register a security key on multiple devices, and may create the perception of a more cumbersome user experience. This is why we utilize the excludeList in our general guidance. Here is some background as to how this list is being built.

    How is the server creating this list?

    When the relying party is built, you pass in the userStorage which allows the rp object to talk to your database where you're storing the credentials. When you begin your registration request, you pass in the userID when you are building the PublicKeyCredentialCreationOptions object needed by the RegistrationRequest object. If you observe the rp.startRegistration method you will see that the excludeList is built by searching userStorage for every credential that matches the userID of the user who made the request.

    It's worth mentioning that the our example is typically acting as a relying party for a single origin/application, while yours is handling multiple.

    With that context you have a few options

    Here are some recommendations:

    • Provide guidance to your users noting that they can use the same account/authenticator to enter into your multiple websites. This will only be applicable if your relying party is acting as a single origin authentication service, then routing the user back to the website they are coming from; example, Users in website1.com, website2.com and website3.com are all routed to my-relyingparty.com, authenticated, and routed back to their original site
    • As noted the java-webauthn-server is looking for registrations by username. You will first need to leverage multiple rp objects as they contain a reference to a specific origin (each registration you create with this rp object will correspond to the identity/origin you set). You can either create multiple instances of the java-webauthn-server for each origin, or create logic in your single instance to 1) Create multiple rp objects for each origin and 2) dynamically choose the rp object to use based on the origin of the users request. You could choose to leverage the same credential repository across multiple server instances or rp by overloading the startRegistrations method to search by both userID and rp ID/origin - While this is technically possible, it'll be a fair amount of rework.
    • Opt not to send existing credentials in the excludeCredentials list, but this may be confusing to some users

    Hope this helps