npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

prf-passkey

v1.0.0

Published

A functional library for WebAuthn Pseudo-Random Function (PRF) key derivation with monadic patterns

Readme

PRF Passkey

CI/CD codecov npm version npm downloads TypeScript License: MIT Security: CodeQL

A functional TypeScript library for WebAuthn Pseudo-Random Function (PRF) extension with robust error handling using neverthrow for deterministic key derivation using passkeys.

Features

  • 🔐 Deterministic Key Derivation: Generate consistent cryptographic keys from passkeys
  • 🛡️ Type-Safe: Full TypeScript support with comprehensive type definitions
  • 🧩 Robust Error Handling: Built on neverthrow for reliable monadic error handling
  • 📦 Multiple Formats: ESM and CommonJS builds included
  • 🔧 Battle-Tested: Uses SimpleWebAuthn for encoding/decoding utilities
  • 🌐 Modern WebAuthn: Built on the latest WebAuthn PRF extension

Installation

npm install prf-passkey

Quick Start

import {
  registerPasskey,
  authenticateAndDeriveKey,
  textToSalt,
  randomChallenge,
  randomUserId
} from 'prf-passkey';

// Configure your app
const config = {
  rpName: 'My App',
  rpId: 'localhost',
  userVerification: 'required'
};

// Register a passkey and derive a pseudorandom value
const result = await registerPasskey(config, {
  userId: randomUserId(),
  userName: '[email protected]',
  userDisplayName: 'User Name',
  challenge: randomChallenge(),
  salt: textToSalt('my-salt-v1')
})();

// Handle the result
if (result.isOk()) {
  console.log('Pseudorandom value:', result.value.keyHex);
} else {
  console.error('Failed:', result.error.message);
}

Core Concepts

This library has a single, focused purpose: extracting pseudorandom values from WebAuthn passkeys using the PRF extension.

What is PRF?

The Pseudo-Random Function (PRF) extension allows passkeys to generate deterministic pseudorandom values based on a salt input. This enables:

  • Deterministic key derivation
  • Consistent encryption keys across sessions
  • Secure pseudorandom number generation

Result Type

The library uses neverthrow's Result<T, E> type for robust error handling:

if (result.isOk()) {
  // Access result.value
} else {
  // Handle result.error
}

API Reference

Core Functions

registerPasskey(config, options)

Register a new passkey and get a pseudorandom value during registration.

interface RegistrationOptions {
  userId: Uint8Array;
  userName: string;
  userDisplayName: string;
  challenge: Uint8Array;
  salt: Uint8Array;  // Input for PRF
}

// Returns: Result<RegistrationResult, Error>
interface RegistrationResult {
  credentialId: ArrayBuffer;
  encodedId: string;
  derivedKey: ArrayBuffer | null;  // PRF output
  keyHex: string | null;           // PRF output as hex
}

authenticateAndDeriveKey(config, options)

Authenticate with an existing passkey and get a pseudorandom value.

interface AuthenticationOptions {
  credentialId: Uint8Array;
  challenge: Uint8Array;
  salt: Uint8Array;  // Input for PRF
}

// Returns: Result<AuthenticationResult, Error>
interface AuthenticationResult {
  derivedKey: ArrayBuffer;  // PRF output
  keyHex: string;           // PRF output as hex
}

Utility Functions

  • textToSalt(text) - Convert text to salt bytes for PRF
  • randomChallenge() - Generate WebAuthn challenge
  • randomUserId() - Generate user ID
  • formatKeyAsHex() - Convert ArrayBuffer to hex string

Examples

Get Pseudorandom Value from New Passkey

import { registerPasskey, textToSalt, randomChallenge, randomUserId } from 'prf-passkey';

const config = { rpName: 'My App', rpId: 'localhost' };

const result = await registerPasskey(config, {
  userId: randomUserId(),
  userName: '[email protected]', 
  userDisplayName: 'User Name',
  challenge: randomChallenge(),
  salt: textToSalt('my-application-salt-v1')  // PRF input
})();

if (result.isOk()) {
  console.log('Pseudorandom value:', result.value.keyHex);
  // Store credential ID for future use
  localStorage.setItem('credentialId', result.value.encodedId);
}

Get Pseudorandom Value from Existing Passkey

import { authenticateAndDeriveKey, base64urlToUint8Array } from 'prf-passkey';

// Retrieve stored credential ID
const encodedCredentialId = localStorage.getItem('credentialId');
const credentialIdResult = base64urlToUint8Array(encodedCredentialId);

if (credentialIdResult.isOk()) {
  const result = await authenticateAndDeriveKey(config, {
    credentialId: credentialIdResult.value,
    challenge: randomChallenge(),
    salt: textToSalt('my-application-salt-v1')  // Same salt = same pseudorandom value
  })();
  
  if (result.isOk()) {
    console.log('Same pseudorandom value:', result.value.keyHex);
  }
}

Different Salts = Different Values

// Different salts produce different pseudorandom values from the same passkey
const encryptionSalt = textToSalt('encryption-v1');
const signingSalt = textToSalt('signing-v1');

const encryptionResult = await authenticateAndDeriveKey(config, {
  credentialId,
  challenge: randomChallenge(),
  salt: encryptionSalt
})();

const signingResult = await authenticateAndDeriveKey(config, {
  credentialId,
  challenge: randomChallenge(), 
  salt: signingSalt  // Different salt
})();

// These will be different values
console.log('Encryption PRF:', encryptionResult.value?.keyHex);
console.log('Signing PRF:', signingResult.value?.keyHex);

Browser Demo

Run the interactive browser demo:

npm run build
python -m http.server 8000  # or any local server

Open http://localhost:8000/examples/browser-demo.html

Requirements

  • Modern browser with WebAuthn support:
    • Chrome 108+
    • Safari 16+
    • Firefox 113+
  • Device with biometric authentication or security key
  • HTTPS connection (except localhost)

Quality & Trust

🔒 Security

  • CodeQL Analysis: Automated security scanning on every commit
  • Dependency Auditing: Regular security audits of all dependencies
  • Security Policy: Responsible disclosure process for vulnerabilities
  • No Secrets: Library never stores or logs sensitive data

✅ Testing

  • 96%+ Code Coverage: Comprehensive test suite with high coverage
  • Cross-Platform CI: Tested on Ubuntu, Windows, and macOS
  • Multi-Node Support: Compatible with Node.js 18, 20, and 22
  • 52 Test Cases: Unit, integration, and error handling tests

🏗️ Build Quality

  • TypeScript First: Full type safety with comprehensive definitions
  • ESLint + Security Rules: Automated code quality and security checks
  • Automated Releases: Consistent builds and releases via GitHub Actions
  • Dependency Management: Automated updates via Dependabot

📦 Production Ready

  • Battle-Tested Dependencies: Built on neverthrow and SimpleWebAuthn
  • Multiple Formats: ESM and CommonJS builds included
  • Tree Shakeable: Optimized for modern bundlers
  • Zero Runtime Dependencies: Minimal bundle size impact

Security Considerations

  • Always use HTTPS in production
  • Store credential IDs securely (not in localStorage)
  • Use unique salts for different key purposes
  • Consider salt versioning for key rotation
  • Validate all inputs on the server side

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

License

MIT License - see LICENSE.md for details.