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

@hawcx/oauth-client

v0.2.0

Published

Lightweight OAuth client for exchanging authorization codes and verifying JWT claims with Hawcx delegation support

Downloads

215

Readme

Hawcx OAuth Client SDK (Node.js)

A lightweight, production-ready Node.js/TypeScript library for exchanging OAuth authorization codes and verifying JWT claims. Built with security, reliability, and ease of use in mind.

Features

  • 🔐 Secure JWT Verification: RS256/ES256/EdDSA signature verification with configurable validation
  • 🆕 PKCE Support: Native PKCE (RFC 7636) support for enhanced OAuth security
  • 🚀 Simple API: One function call to go from code to verified claims
  • 🎯 High-Level Hawcx Client: One-line MFA setup with automatic encryption/signing
  • 🛡️ Comprehensive Error Handling: Clear, actionable error messages for all failure scenarios
  • 📝 TypeScript First: Full type definitions for better IDE support and type checking
  • 🧪 Production Ready: Extensive error handling and security best practices
  • 🔧 Express/Fastify Compatible: Works with any Node.js framework
  • 🔍 Detailed Logging: Structured logging for debugging (no sensitive data logged)

Installation

npm install @hawcx/oauth-client

# or with yarn
yarn add @hawcx/oauth-client

# or with pnpm
pnpm add @hawcx/oauth-client

Quick Start

OAuth Code Exchange

import { exchangeCodeForClaims } from '@hawcx/oauth-client';

// Exchange authorization code for verified claims
const claims = await exchangeCodeForClaims({
  code: req.body.code,
  oauthTokenUrl: process.env.OAUTH_TOKEN_ENDPOINT!,
  clientId: process.env.OAUTH_CLIENT_ID!,
  apiKey: process.env.HAWCX_API_KEY!,
  publicKey: process.env.OAUTH_PUBLIC_KEY!,
  // Optional (recommended for production):
  codeVerifier: session.pkceVerifier,  // PKCE support
  audience: 'my-app',  // Validate 'aud' claim
  issuer: 'https://oauth.example.com',  // Validate 'iss' claim
  leeway: 10  // Clock skew tolerance
});

// Use the verified claims
const userId = claims.sub;
// Mint your own access token (SDK only verifies, doesn't mint)

Hawcx Delegation (MFA Setup)

For Hawcx MFA setup and user management, use the delegation client:

import { HawcxDelegationClient, MfaMethod } from '@hawcx/oauth-client';

// 🎉 One-line initialization from explicit keys!
const client = HawcxDelegationClient.fromKeys({
  spSigningKey: process.env.SP_ED25519_PRIVATE_KEY_PEM!,
  spEncryptionKey: process.env.SP_X25519_PRIVATE_KEY_PEM!,
  idpVerifyKey: process.env.IDP_ED25519_PUBLIC_KEY_PEM!,
  idpEncryptionKey: process.env.IDP_X25519_PUBLIC_KEY_PEM!,
  baseUrl: 'https://example.hawcx.com',
  spId: process.env.OAUTH_CLIENT_ID!
});

// Initiate MFA setup (Email, SMS, or TOTP)
const result = await client.initiateMfaChange({
  userid: "[email protected]",
  mfaMethod: MfaMethod.SMS, 
  phoneNumber: "+15551234567"
});

// Verify OTP and complete MFA setup
await client.verifyMfaChange({
  userid: "[email protected]",
  sessionId: result.session_id,
  otp: "123456"
});

// Get user credentials
const creds = await client.getUserCredentials("[email protected]");
console.log(`MFA method: ${creds.mfa_method}`);

What it does automatically: ECIES encryption, Ed25519 signatures, request/response crypto, Hawcx payload formatting, type-safe MfaMethod enum

Demo App

A simple demo application is included to show the OAuth and delegation flows in action:

cd demo
cp env.template .env
# Edit .env with your credentials
npm install
npm run dev
# Visit http://localhost:3000

See demo/README.md for details.

Configuration

Environment Variables

For OAuth Code Exchange:

OAUTH_TOKEN_ENDPOINT="https://oauth.example.com/token"
OAUTH_CLIENT_ID="your-client-id"
OAUTH_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n..."
OAUTH_ISSUER="https://oauth.example.com"  # Optional but recommended
OAUTH_AUDIENCE="your-client-id"  # Optional but recommended

For Hawcx Delegation:

SP_ED25519_PRIVATE_KEY_PEM="-----BEGIN PRIVATE KEY-----..."
SP_X25519_PRIVATE_KEY_PEM="-----BEGIN PRIVATE KEY-----..."
IDP_ED25519_PUBLIC_KEY_PEM="-----BEGIN PUBLIC KEY-----..."
IDP_X25519_PUBLIC_KEY_PEM="-----BEGIN PUBLIC KEY-----..."

Public Key Formats

The SDK accepts public keys in multiple formats:

From file:

const claims = await exchangeCodeForClaims({
  // ...
  apiKey: process.env.HAWCX_API_KEY!,
  publicKey: '/path/to/public.pem'  // Absolute path
});

From environment variable:

const claims = await exchangeCodeForClaims({
  // ...
  apiKey: process.env.HAWCX_API_KEY!,
  publicKey: process.env.OAUTH_PUBLIC_KEY!  // PEM string
});

Expected PEM format:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----

Error Handling

The SDK provides specific exceptions for different failure scenarios:

import {
  exchangeCodeForClaims,
  OAuthExchangeError,
  JWTVerificationError,
  InvalidPublicKeyError
} from '@hawcx/oauth-client';

try {
  const claims = await exchangeCodeForClaims({
    code,
    oauthTokenUrl,
    clientId,
    apiKey,
    publicKey
  });
} catch (error) {
  if (error instanceof OAuthExchangeError) {
    // Code exchange failed (invalid code, network error, etc.)
    console.error(`Exchange failed: ${error.message}`);
    console.error(`HTTP Status: ${error.statusCode}`);
    console.error(`Response: ${error.responseBody}`);
  } else if (error instanceof JWTVerificationError) {
    // JWT verification failed (invalid signature, expired, etc.)
    console.error(`Verification failed: ${error.message}`);
    console.error(`Original error: ${error.originalError}`);
  } else if (error instanceof InvalidPublicKeyError) {
    // Public key is invalid or unreadable
    console.error(`Key error: ${error.message}`);
  }
}

Common Error Scenarios

| Exception | Common Causes | Recommended Action | |-----------|--------------|-------------------| | OAuthExchangeError | Invalid/expired code, network issues | Ask user to re-authenticate | | JWTVerificationError | Token tampering, expired token | Log incident, ask user to re-authenticate | | InvalidPublicKeyError | Wrong key, file not found | Check configuration, verify key format |

Security Best Practices

✅ DO

  • Always validate audience and issuer in production environments
  • Use HTTPS for all OAuth endpoints
  • Store keys securely (environment variables, secrets manager)
  • Set appropriate leeway for clock skew (5-10 seconds typical)
  • Log authentication failures for security monitoring
  • Rotate keys regularly following your security policy

❌ DON'T

  • Never log JWT tokens or claims containing sensitive data
  • Don't disable signature verification in production
  • Don't use the id_token as your application's access token (mint your own)
  • Don't commit keys to version control
  • Don't ignore verification errors or catch them silently

API Reference

exchangeCodeForClaims()

async function exchangeCodeForClaims(
  options: ExchangeCodeOptions
): Promise<Record<string, any>>

Parameters:

  • code (string): Authorization code from OAuth flow
  • oauthTokenUrl (string): Token endpoint URL
  • clientId (string): OAuth client identifier
  • apiKey (string): API key for Hawcx OAuth
  • publicKey (string, optional): RS256 public key (PEM string or file path) - optional if jwksUrl provided
  • jwksUrl (string, optional): JWKS URL for dynamic key fetching (alternative to publicKey)
  • codeVerifier (string, optional): PKCE code verifier (RFC 7636)
  • redirectUri (string, optional): OAuth redirect URI
  • timeout (number, optional): Request timeout in seconds (default: 20)
  • audience (string, optional): Expected 'aud' claim (recommended)
  • issuer (string, optional): Expected 'iss' claim (recommended)
  • verifyExp (boolean, optional): Verify token expiration (default: true)
  • leeway (number, optional): Clock skew tolerance in seconds (default: 0)
  • algorithms (string[], optional): Allowed signing algorithms (default: RS256, ES256/384/512, EdDSA)

Returns: Promise<Record<string, any>> - Verified JWT claims

Throws:

  • OAuthExchangeError: Code exchange failed
  • JWTVerificationError: JWT verification failed
  • InvalidPublicKeyError: Public key invalid

verifyJwt()

function verifyJwt(
  options: VerifyJwtOptions
): Record<string, any>

Verify a JWT and return its claims. See full documentation in API.md.

HawcxDelegationClient

See DELEGATION.md for full Hawcx delegation client documentation.

TypeScript Support

This package is written in TypeScript and includes full type definitions:

import type {
  ExchangeCodeOptions,
  VerifyJwtOptions
} from '@hawcx/oauth-client';

// All options are fully typed
const options: ExchangeCodeOptions = {
  code: 'abc123',
  oauthTokenUrl: 'https://oauth.example.com/token',
  clientId: 'my-app',
  publicKey: process.env.OAUTH_PUBLIC_KEY!,
  audience: 'my-app',  // TypeScript will catch typos!
};

Development

Building

npm install
npm run build

Testing

npm test
npm run test:coverage

Linting

npm run lint
npm run format