webauthn-server-implementationPasskeys Implementation

9 WebAuthn Server Implementation Libraries Compared

This post helps you find the right WebAuthn server library to offer passkeys. 9 libraries are compared and a strategy to find the right library is given.

Blog-Post-Author

Nicolai

Created: December 15, 2023

Updated: October 1, 2024


Our mission is to make the Internet a safer place, and the new login standard passkeys provides a superior solution to achieve that. That's why we want to help you understand passkeys and its characteristics better.

1. Introduction: WebAuthn Server Implementation

2. How Can I Implement Passkeys On My Own?

3. WebAuthn Server Backend Libraries: A Comparison

    3.1 TypeScript: SimpleWebAuthn

    3.2 .NET: fido2-net-lib

    3.3 Python: py_webauthn

    3.4 Ruby: webauthn-ruby

    3.5 Go: go-webauthn

    3.6 Java: java-webauthn-server

    3.7 Rust: webauthn-rs

    3.8 PHP: webauthn-framework

    3.9 Java: webauthn4j

4. Recommendations: Navigating the WebAuthn Server Library Landscape

    4.1 Step 1: Choose Your WebAuthn Library

    4.2 Step 2: Define Your WebAuthn Server Options

    4.3 Step 3: Define the Database Structure

    4.4 Step 4: Test On Your Users' Devices

5. Conclusion

1. Introduction: WebAuthn Server Implementation

Providing secure and simple user authentication is a must-have for digital companies in 2024. Passkeys, as the new login standard, are the ideal solution to meet these needs. However, the enhanced user experience and security of passkeys for the user comes at a price when implementing them as a developer. The implementation difficulty stems from the fact that passkeys are relatively new - to users, but also to developers and that their implementation can be quite challenging compared to password- based authentication. In fact, you need at least four API endpoints for passkey authentication compared to one API endpoint for password authentication.

One of core components on server side for providing passkey authentication is the WebAuthn server (green library part).

WebAuthn Server ArchitectureSource: Yubico

In this blog post, we compare several WebAuthn server libraries / packages / SDKs, analyze differences and provide a recommendation for developers who are new to passkey implementation.

2. How Can I Implement Passkeys On My Own?

To better understand, why a WebAuthn server library is needed in the first place, lets have a look at how passkeys can be implemented. In principle, there are two ways to integrate passkeys into websites and apps:

  1. Use a third-party passkey solution (e.g. Corbado)
  2. Implement passkeys on your own using one of the WebAuthn server libraries from below

While a third-party passkey solution is easy to integrate and usually saves a lot of engineering time (especially for edge cases, maintenance, recovery, fallbacks and improved passkey UX), some developers just favor to implement everything themselves.

Lets have a look how the do-it-yourself passkey implementation works. In a very basic setup, a mechanism to register (sign-up) and authenticate (login) is needed. Both processes, also called WebAuthn ceremonies, are handled differently, even though the overall flow follows a similar schema:

  1. The frontend (the browser) initiates a sign-up or login request and calls a backend API endpoint.
  2. The backend generates WebAuthn sign-up or login parameters. These are called PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions respectively. One of the most important parts of these WebAuthn parameters is the challenge. The WebAuthn parameters are then sent back to the frontend.
  3. The frontend receives these WebAuthn parameters and uses them to verify the user's identity using the local device authentication system (e.g. via Face ID, Touch ID, Windows Hello). In sign-up processes, a new public-private-key pair is created, the private key stored locally, while the public key is sent to the server. In login processes, the challenge is signed using the private key of the passkey and sent to another backend API endpoint.
  4. In sign-up processes, the backend receives the public key and stores it. In logins, the backend receives the signed challenge and verifies its integrity using the WebAuthn server.

Since every sign-up / login process involves these steps, the backend needs to keep track of users, passkeys and sign-up / login requests.

Ben Gould Testimonial

Ben Gould

Head of Engineering

I’ve built hundreds of integrations in my time, including quite a few with identity providers and I’ve never been so impressed with a developer experience as I have been with Corbado.

10,000+ devs trust Corbado & make the Internet safer with passkeys. Got questions? We’ve written 150+ blog posts on passkeys.

Join Passkeys Community

If you want to get more in-depth knowledge about the way passkeys work and how a simple implementation looks like (without using a third-party passkey solution), you can investigate our blog article here.

In real-life scenarios, when implementing passkeys yourself, keep in mind that its not just about providing the necessary API endpoints and basic implementation to sign up and login. On top of that, you need to address the following topics and use cases:

  • Device management
  • Cross-platform and cross-device login flows
  • User management
  • Session management
  • Passkey-readiness device detection
  • User education
  • Fallback authentication options
  • Recovery mechanisms
  • Options for account sharing

Though, for the basic passkey implementation, you only need to adhere to the WebAuthn standard. Implementing a well-known and supported WebAuthn server library is usually enough. The library generates the WebAuthn server parameters and verifies login challenges, essentially taking over the cryptographic and most complex part for you.

Slack Icon

Become part of our Passkeys Community for updates and support.

Join

3. WebAuthn Server Backend Libraries: A Comparison

All of the analyzed WebAuthn server libraries provide the necessary functionalities to offer passkey authentication. Therefore, we paid special attention to the following criteria:

  • Authenticator Selection Criteria: How can you set preferences for certain behavior and security features (e.g. forcing the creation of resident keys )?
  • Global Settings: How much can you centralize the configuration needed?
  • Documentation: How well is the WebAuthn server library documented and easy-to-follow
  • Examples: Are there any example implementations that you take as reference.

The following WebAuthn server libraries were analyzed (ordered by descending number of GitHub stars in Decemer 2023):

3.1 TypeScript: SimpleWebAuthn

SimpleWebAuthn

  • Programming Language: Typescript
  • Framework: Node.js
  • Author: Matthew Miller (Duo Security / Cisco)
  • Stars: 1000
  • Used by: 781
  • Contributors: 19
  • FIDO Conformance: Yes
  • Global Settings: No
  • Authenticator Selection Criteria: As suggested by W3C (should contain variables authenticatorAttachment, residentKey, requireResidentKey and userVerification which are explained on the W3C page as well)
  • Documentation: Nicely structured with focus on fast setup and ease of use, includes passkey knowledge e.g. explains what passkeys are and how they work
  • Examples: Example project available in the docs
  • Recommended data structures:
type UserModel = { id: string; username: string; currentChallenge?: string; }; /** * It is strongly advised that authenticators get their own DB * table, ideally with a foreign key to a specific UserModel. * * "SQL" tags below are suggestions for column data types and * how best to store data received during registration for use * in subsequent authentications. */ type Authenticator = { // SQL: Encode to base64url then store as `TEXT`. Index this column credentialID: Uint8Array; // SQL: Store raw bytes as `BYTEA`/`BLOB`/etc... credentialPublicKey: Uint8Array; // SQL: Consider `BIGINT` since some authenticators return atomic timestamps as counters counter: number; // SQL: `VARCHAR(32)` or similar, longest possible value is currently 12 characters // Ex: 'singleDevice' | 'multiDevice' credentialDeviceType: CredentialDeviceType; // SQL: `BOOL` or whatever similar type is supported credentialBackedUp: boolean; // SQL: `VARCHAR(255)` and store string array as a CSV string // Ex: ['usb' | 'ble' | 'nfc' | 'internal'] transports?: AuthenticatorTransport[]; };
  • Other Characteristics: Great WebAuthn Debugger for WebAuthn responses and does not only offer a backend / server library but also a library for the frontend (basically wrapping some of the Web Authentication APIs from the client)
  • Personal Assessment: Most intuitive setup seen so far and very straightforward. Moreover, its one of the few libraries that also offers a browser library to help with client implementations makes it pretty outstanding. The author of the library is heavily active in the WebAuthn / passkey community.
  • **GitHub Link: **https://github.com/MasterKale/SimpleWebAuthn

SimpleWebAuthn Screenshot

3.2 .NET: fido2-net-lib

fido2-net-lib

  • Programming Language: C#
  • Framework: .NET
  • Authors: Anders à berg, Alex Seigler
  • Stars: 962
  • Used by: 200
  • Contributors: 40
  • FIDO Conformance: Yes
  • Global Settings: Yes
  • Authenticator Selection Criteria: As suggested by W3C
  • Documentation: Just the Readme with a couple of samples, some passkey intelligence on the library's website
  • Examples: Demo controller and example credential store available to show example integration
  • Recommended data structure:
public class StoredCredential { /// <summary> /// The Credential ID of the public key credential source. /// </summary> public byte[] Id { get; set; } /// <summary> /// The credential public key of the public key credential source. /// </summary> public byte[] PublicKey { get; set; } /// <summary> /// The latest value of the signature counter in the authenticator data from any ceremony using the public key credential source. /// </summary> public uint SignCount { get; set; } /// <summary> /// The value returned from getTransports() when the public key credential source was registered. /// </summary> public AuthenticatorTransport[] Transports { get; set; } /// <summary> /// The value of the BE flag when the public key credential source was created. /// </summary> public bool IsBackupEligible { get; set; } /// <summary> /// The latest value of the BS flag in the authenticator data from any ceremony using the public key credential source. /// </summary> public bool IsBackedUp { get; set; } /// <summary> /// The value of the attestationObject attribute when the public key credential source was registered. /// Storing this enables the Relying Party to reference the credent’al's attestation statement at a later time. /// </summary> public byte[] AttestationObject { get; set; } /// <summary> /// The value of the clientDataJSON attribute when the public key credential source was registered. /// Storing this in combination with the above attestationObject item enables the Relying Party to re-verify the attestation signature at a later time. /// </summary> public byte[] AttestationClientDataJson { get; set; } public List<byte[]> DevicePublicKeys { get; set; } public byte[] UserId { get; set; } public PublicKeyCredentialDescriptor Descriptor { get; set; } public byte[] UserHandle { get; set; } public string AttestationFormat { get; set; } public DateTimeOffset RegDate { get; set; } public Guid AaGuid { get; set; } }
  • Other Characteristics: Used by Bitwarden (the startup passwordless.dev was aquired by Bitwarden)
  • Personal Assessment: Developer-friendly library with a good amount of supplementary material
  • GitHub Link: https://github.com/passwordless-lib/fido2-net-lib

fido2-net-lib Screentshot

3.3 Python: py_webauthn

py_webauthn

  • Programming Language: Python
  • Framework: no specific
  • Author: Duo Labs
  • Stars: 727
  • Used by: 912
  • Contributors: 23
  • FIDO Conformance: N/A
  • Global Settings: No
  • Authenticator Selection Criteria: As suggested by W3C
  • Documentation: Little to none, previous passkey knowledge is required
  • Examples: Small code samples for sign up and login are included
  • Recommended data structure: N/A
  • Other Characteristics: Requires Python >= 3.8
  • Personal Assessment: Comparatively compact library with small amount of files, you can get an overview in a short amount of time.
  • GitHub Link: https://github.com/duo-labs/py_webauthn

py_webauthn Screenshot

3.4 Ruby: webauthn-ruby

webauthn-ruby

  • Programming Language: Ruby
  • Framework: Ruby on Rails
  • Author: Cedarcode
  • Stars: 580
  • Used by: 679
  • Gem Downloads: 18 Mio
  • Contributors: 27
  • FIDO Conformance: Yes
  • Global settings: Yes
  • Authenticator Selection Criteria: As suggested by W3C
  • Documentation: Readme is the only source of documentation
  • Examples: N/A
  • Recommended data structure: N/A
  • Other Characteristics: Wrappers for this library are also available (warden-webauthn, devise-passkeys)
  • Personal Assessment: Since the supplementary material is quite limited we would only recommend this library if you are familiar with both Ruby and Passkeys
  • GitHub Link: https://github.com/cedarcode/webauthn-ruby

webauthn-ruby Screenshot

3.5 Go: go-webauthn

go-webauthn

  • Programming Language: Go
  • Framework: Go based frameworks like Gin, Echo, ¦
  • Author: Originally from Duo Labs / continued by James Elliott
  • Stars: 480
  • Used by: 280
  • Contributors: 18
  • FIDO Conformance: Yes
  • Global Settings: Yes
  • Authenticator Selection Criteria: As suggested by W3C
  • Documentation: Readme with some samples is the only source of documentation
  • Examples: Example repo available
  • Recommended Data Structure: N/A
  • Other Characteristics: Large number of files, getting an overview takes some time
  • Personal Assessment: Popular library which is easy to use once you get familiar with it, however we recommend to learn some passkey knowledge (How they work, what the procedures look like, etc) beforehand.
  • GitHub Link: https://github.com/go-webauthn/webauthn

go-webauthn Screenshot

3.6 Java: java-webauthn-server

java-webauthn-server

  • Programming Language: Java
  • Framework: Maven / Gradle
  • Author: Yubico
  • Stars: 391
  • Used by: N/A
  • Contributors: 27
  • FIDO Conformance: N/A
  • Global Settings: Yes
  • Authenticator Selection Criteria: As suggested by W3C.
  • Documentation: Readme is the only documentation, but rather detailed
  • Examples: N/A
  • Recommended Data Structure: N/A
  • Other Characteristics: Comes with a Credential Repository interface, providing clear guidance on required database retrieval methods.
  • Personal Assessment: Although you must manage the database yourself, the credential repository helps significantly in the database design process. Naturally Java implementations come with extensive number of files as each attribute needs a class which is usually stored in its own file. This makes getting an overview a more time-consuming task.
  • GitHub Link: https://github.com/Yubico/java-webauthn-server

java-webauthn-server Screenshot

3.7 Rust: webauthn-rs

webauthn-rs

  • Programming Language: Rust
  • Framework: Rust based (Rocket, Axum, ¦)
  • Author: Kanidm Identity Management Project
  • Stars: 351
  • Used by: 617
  • Contributors: 25
  • FIDO Conformance: N/A
  • Global Settings: Yes
  • Authenticator Selection Criteria: Very limited configuration possibilities. The other libraries are implemented exactly according to the W3C Standard, displaying a greater configurability
  • Documentation: Autogenerated technical documentation exists, but not very detailed
  • Examples: Tutorial repo with examples for 3 different web frameworks is available
  • Recommended Data Structure: N/A
  • Other Characteristics: The library has passed a security audit performed by SUSE product security.
  • Personal Assessment: Because of the limited configurability and docs we would only recommend this library if your primary framework is Rust-based and you know how passkeys work
  • GitHub Link: https://github.com/kanidm/webauthn-rs

webauthn-rs Screenshot

3.8 PHP: webauthn-framework

webauthn-framework

  • Programming Language: PHP
  • Framework: Symfony
  • Author: Florent Morselli
  • Stars: 347
  • Used by: N/A
  • Contributors: 18
  • FIDO Conformance: Yes
  • Global Settings: Yes
  • Authenticator Selection Criteria: As suggested by W3C.
  • Documentation: Extensive documentation including technical details, but also explaining passkeys from the ground up
  • Examples: Example repo available
  • Recommended Data Structure:
<?php declare(strict_types=1); namespace App\Entity; use App\Repository\PublicKeyCredentialSourceRepository; use DateTimeImmutable; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Uid\Uuid; use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource; use Webauthn\TrustPath\TrustPath; #[ORM\Table(name: 'pk_credential_sources')] #[ORM\Entity(repositoryClass: PublicKeyCredentialSourceRepository::class)] class PublicKeyCredentialSource extends BasePublicKeyCredentialSource { #[ORM\Column(type: Types::DATETIME_IMMUTABLE)] public readonly DateTimeImmutable $createdAt; #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 255)] #[ORM\GeneratedValue(strategy: 'NONE')] private string $id; public function __construct( string $publicKeyCredentialId, string $type, array $transports, string $attestationType, TrustPath $trustPath, AbstractUid $aaguid, string $credentialPublicKey, string $userHandle, int $counter ) { $this->id = Uuid::v4()->toRfc4122(); $this->createdAt = new DateTimeImmutable(); parent::__construct($publicKeyCredentialId, $type, $transports, $attestationType, $trustPath, $aaguid, $credentialPublicKey, $userHandle, $counter); } public function getId(): string { return $this->id; } }

webauthn-framework Screenshot

3.9 Java: webauthn4j

webauthn4j

  • Programming Language: Java
  • Framework: Maven
  • Author: WebAuthn4j
  • Stars: 342
  • Used by: N/A
  • Contributors: 19
  • FIDO Conformance: Yes
  • Global Settings: Yes
  • Authenticator Selection Criteria: Instead of the W3C suggested UserVerificationRequirement parameter with values discouraged, preferred, required, webauthn4j offers verificationRequired and userPrecenseRequired as Boolean variables
  • Documentation: Dry, but extensive documentation with a few code samples is available
  • Examples: Example repo available
  • Recommended Data Structure: N/A
  • Other Characteristics: N/A
  • Personal Assessment: Reading through the docs gets boring and complex quickly, as it is just one big page which focuses solely on functionality, not the concept behind passkeys.
  • GitHub Link: https://github.com/webauthn4j/webauthn4j

webauthn4j Screenshot

The following table provides an overview of the WebAuthn server libraries:

WebAuthn Server Overview

Substack Icon

Subscribe to our Passkeys Substack for the latest news, insights and strategies.

Subscribe

4. Recommendations: Navigating the WebAuthn Library Landscape

4.1 Step 1: Choose Your WebAuthn Library

Since most of the libraries are equally powerful and implement the WebAuthn standard, we recommend the following decision-tree:

  • A WebAuthn Server Library in Your Framework & Programming Language is Available: We have not listed framework libraries that depend on one of the libraries above (e.g. there are customizations for PHP available for Symfony and Laravel). In case there is a framework implementation, choose this one as it allows you to implement the WebAuthn API calls natively into your current framework structure and use your database abstraction to implement the storage.
  • A WebAuthn Server Library in Your Programming Language is Available: As you need backend API endpoints, choose the WebAuthn server library in your native programming language so it can natively embedded into your current API endpoints.
  • There is no WebAuthn Server Library Available in Your Programming Language: In this case, you should consider using a passkey-as-a-service solution like Corbado. If that is not an option, we recommend using go-webauthn or SimpleWebAuthn as they have the biggest adoption, are lightweight and are also used by a lot of commercial and open-source projects.

If you just want to learn more about WebAuthn servers in general without already having a specific project we can make some recommendations since there are some differences between the libraries and their supplementary material like docs and example implementations. So, for software developers eager to kickstart their passkey implementation journey, we advise to choose the following implementations:

  • For Quick Testing: py_webauthn package offers instant implementation with ready-to-use code snippets. With just one method call you can generate the options you need for the authentication. Since you dont need to embed the library in a framework and configure it, it is ideal for quick testing.
  • For Intuitive Implementation:SimpleWebauthn package written in Typescript is straightforward and developer-friendly. Like py_webauthn the implementation is very minimalistic, but it additionally comes with docs that guide you through the authentication process. In contrast to py_webauthn however, SimpleWebAuthn does not come with a clear example which runs without modification.
  • For Deep Understanding:webauthn-framework library for PHP stands out with extensive, structured documentation focusing on passkey concepts. The main differences to other libraries with a good amount of docs like webauthn4j are the design and vividness of the docs.

For an even deeper understanding of how WebAuthn works on the server side, you can read the very detailed section WebAuthn Relying Party Operations in the WebAuthn RFC which details every step that needs to be implemented for registering a new credential (7.1) and verifying an authentication assertion assertion (7.2).

4.2 Step 2: Define Your WebAuthn Server Options

Evaluate the specific passkey and WebAuthn requirements you have. In this blog post, we assumed you only want to support passkeys as discoverable credentials. Read about the PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions together with the client-side navigator.credentials.create() and navigator.credentials.get() WebAuthn API calls to set the parameters in the WebAuthn server SDK config correctly for your use case.

4.3 Step 3: Define the Database Structure

For all the WebAuthn server libraries, you will need to provide the appropriate database structure to persist / access the following information:

  • Credentials
  • Users
  • Challenges
  • Authenticators

For some libraries, there are specific recommendations and examples (if we found them useful, we have provided them above). Its essential to fully understand which WebAuthn fields need to be stored where. Pay special attention to identify which value you want to use for the User ID (user.id). We have a more detailed explanation here. Also take into consideration what happens when a user might delete a passkey. Besides that, you can optionally constrain the usage of certain authenticators. A list of valid authenticators related to passkeys can be found here. In case you also want to support and check the attestations of security keys, this is a whole different story. You find more information here.

4.4 Step 4: Test On Your Users' Devices

Identify on which devices your users will use passkeys and fallback authentication methods. In case you are not sure which devices, browsers and operating systems your users use, we provide you with free passkey analyzer tool, that can be added to any website or web app in a matter of minutes (you dont need to use Corbado as auth solution to use the passkey analyzer). If you have specific questions on the passkey adoption and passkey-readiness share of certain devices, feel free to reach out to us. We are happy to provide you with further insights and help you on this topic (see also our latest blog post regarding passkey- readiness). Moreover, you should keep in mind that for Windows 10 and Linux you will need to come up with dedicated solutions as these operating system provide the least (if at all) passkey support.

5. Conclusion

For almost every language or framework out there exists a well-established WebAuthn server library by now. Comparing libraries of different languages shows no clear superiority of certain implementations. Rather you should use the framework / programming language you are most familiar with. Alternatively, if you dont want to implement WebAuthn yourself and take care of all the stuff that comes along, you can try a dedicated, pre-built passkey-authentication solution like Corbado. Posing a passkey centered all- in-one authentication solution, it comes with great passkey intelligence, session management as well as fallback authentication methods, so you can focus on developing your product and let go of authentication. You can try it out for free with unlimited users here.

Share this article


LinkedInTwitterFacebook

Table of Contents

Enjoyed this read?

🤝 Join our Passkeys Community

Share passkeys implementation tips and get support to free the world from passwords.

🚀 Subscribe to Substack

Get the latest news, strategies, and insights about passkeys sent straight to your inbox.


We provide UI components, SDKs and guides to help you add passkeys to your app in <1 hour

Start for free