Introduction

Password-based authentication has long been the weakest link in application security. With phishing, credential stuffing, and password reuse rampant, modern organizations are looking toward passwordless authentication methods that are more secure and user-friendly.

This post explains how to use a YubiKey hardware security key to implement FIDO2-based passwordless login using WebAuthn, including optional integration with enterprise IAM solutions like ForgeRock Identity Cloud.

What Is FIDO2 and Why YubiKey?

FIDO2 is an open standard for passwordless authentication, co-developed by the FIDO Alliance and the World Wide Web Consortium (W3C). It combines two components:

  • WebAuthn – A browser API that allows web applications to use public key cryptography.
  • CTAP2 (Client to Authenticator Protocol) – Used by external authenticators like YubiKey.

The YubiKey stores private credentials securely and never shares them with a server. Authentication involves a challenge-response flow where a cryptographic signature is verified, and the user physically touches the key to complete the login—making it phishing-resistant and secure.

Prerequisites

To follow along, you’ll need:

  • A YubiKey 5 Series or compatible FIDO2 device.
  • A modern browser (Chrome, Firefox, Safari, or Edge).
  • Basic knowledge of JavaScript and server-side APIs.
  • A web app project using Node.js, Python, or Java (server-side).
  • (Optional) Access to ForgeRock Identity Cloud or ForgeRock AM.

How YubiKey + WebAuthn Login Works

Here’s a simplified flow of how passwordless authentication works using YubiKey and WebAuthn:

+--------------------+        +----------------+        +----------------+
|   Web Browser      | <----> |   Web Server   | <----> |   Database     |
+--------------------+        +----------------+        +----------------+
        |                             |                          |
        |---- Start Registration ---->|                          |
        |<-- Send Challenge (RP) -----|                          |
        |                             |                          |
        |-- Create Credential w/Key ->|                          |
        |                             |--- Store Public Key ---->|
        |                             |                          |
        |---- Start Login ----------->|                          |
        |<-- Send Challenge ----------|                          |
        |-- Auth w/ YubiKey --------->|--- Verify Signature ---->|
        |                             |                          |

Step 1: User Registration Using WebAuthn + YubiKey

During user registration, a credential is generated and stored securely on the client-side YubiKey. The public key is saved on the server.

Client-side JavaScript

// Step 1: Get public key registration options from the server
const options = await fetch('/register/options').then(res => res.json());

// Step 2: Use WebAuthn API to create credentials with YubiKey
const credential = await navigator.credentials.create({ publicKey: options });

// Step 3: Send the new credential to server for verification and storage
await fetch('/register/complete', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(credential)
});

Server-side (simplified)

// Verify client response and store public key
const { id, publicKey } = parseWebAuthnCredential(clientPayload);
saveUserCredential(userId, id, publicKey);

Step 2: Passwordless Login Using WebAuthn

After registration, the user can log in without a password using their YubiKey.

Client-side JavaScript

// Step 1: Request login challenge
const requestOptions = await fetch('/login/options').then(res => res.json());

// Step 2: Use WebAuthn API to get assertion from YubiKey
const assertion = await navigator.credentials.get({ publicKey: requestOptions });

// Step 3: Send the signed assertion to the server
await fetch('/login/complete', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(assertion)
});

Server-side Verification

// Verify assertion using stored public key
const isValid = verifyWebAuthnAssertion(assertion, storedPublicKey);
if (isValid) loginUser(userId);

Optional: ForgeRock Identity Cloud Integration

ForgeRock provides native support for WebAuthn through Authentication Trees.

How to Enable WebAuthn in ForgeRock

  1. Go to Journeys > Select your Tree (e.g., Passwordless Login)

  2. Add WebAuthn Registration Node and WebAuthn Authentication Node

  3. Configure options:

    • User verification: preferred or required
    • Authenticator attachment: cross-platform (for YubiKey)
  4. Deploy and test in a browser that supports WebAuthn

This setup allows ForgeRock to act as the Relying Party (RP) and coordinate the entire WebAuthn challenge/response lifecycle.

Security Considerations

  • YubiKey supports origin binding, so credentials are only valid for the domain they were created on.
  • Use multi-device support or allow backup credentials in case a user loses their YubiKey.
  • Always use TLS for all WebAuthn endpoints.

Conclusion

Hardware-based authentication like YubiKey with FIDO2 and WebAuthn is a proven way to eliminate passwords without compromising security. By following the steps above, you can offer users a fast, secure, and phishing-resistant login experience that integrates seamlessly with modern identity providers such as ForgeRock.

Passwordless authentication is not the future—it’s already here.

Additional Resources