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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wardauth/sdk-js

v2.1.0

Published

Official JavaScript SDK for WardAuth — passwordless authentication as a service

Readme

@wardauth/sdk-js

Official JavaScript SDK for WardAuth — passwordless authentication as a service.

Works in browsers (Chrome 109+, Firefox 130+, Safari 17+) and Node.js 20+.

Install

npm install @wardauth/sdk-js

Quick start

import { WardAuth } from "@wardauth/sdk-js";

const auth = new WardAuth({
  baseUrl: "https://api.wardauth.com",
  apiKey:  "wardauth_live_...",
});

Authentication methods

Email + Password (zero-knowledge)

The password never leaves the device. It's used to derive an Ed25519 key pair locally via PBKDF2 (600k iterations). The server only sees the public key.

Same email + same password = same key pair on any device. No password sync needed.

// Register
await auth.registerWithPassword({
  email: "[email protected]",
  password: "my-strong-password-123",  // min 12 chars, never sent to server
});

// Sign in (any device — same password = same key)
const session = await auth.signInWithPassword(
  "[email protected]",
  "my-strong-password-123"
);

console.log(session.user.sub);  // "[email protected]"
console.log(session.accessToken);

Passwordless (Ed25519 key pair)

For apps that don't want passwords at all. A random Ed25519 key pair is generated and stored on the device.

// Register (generates key pair automatically)
const reg = await auth.register({ email: "[email protected]" });
console.log(reg.created); // true on first registration

// Sign in (requires the same device or key sync)
const session = await auth.signIn("[email protected]");

Magic link (email)

No local key pair required. The server creates the user on first sendMagicLink if that email is new.

Configure outbound email and magic_link_redirect_url / app_url in tenant settings so the email link lands on your app.

await auth.sendMagicLink("[email protected]");

// Callback page (query string from the redirect):
const params = new URLSearchParams(window.location.search);
const session = await auth.verifyMagicLink(
  params.get("token")!,
  params.get("tenant")!
);

Sign out

await auth.signOut();
// Revokes refresh token on server + clears local session and keys

React

import { WardAuthProvider, useAuth } from "@wardauth/sdk-js/react";

function App() {
  return (
    <WardAuthProvider
      baseUrl="https://api.wardauth.com"
      apiKey="wardauth_live_..."
    >
      <LoginPage />
    </WardAuthProvider>
  );
}

function LoginPage() {
  const { signIn, signOut, user, loading } = useAuth();

  if (loading) return <p>Loading...</p>;

  if (user) return (
    <div>
      <p>Signed in as {user.sub}</p>
      <button onClick={signOut}>Sign out</button>
    </div>
  );

  return (
    <button onClick={() => signIn("[email protected]")}>
      Sign in
    </button>
  );
}

Route guard

import { useRequireAuth } from "@wardauth/sdk-js/react";

function Dashboard() {
  const { ready, user } = useRequireAuth("/login");
  if (!ready) return <p>Redirecting...</p>;
  return <h1>Welcome, {user?.sub}</h1>;
}

MFA hook

import { useMfa } from "@wardauth/sdk-js/react";

function MfaSettings() {
  const { factors, enrollTotp, revokeFactor, loading } = useMfa();
  // ...
}

MFA — TOTP

// Enroll
const { qrCode, factorId } = await auth.mfa.enrollTotp();
// Show qrCode (data:image/png;base64,...) to the user

// Verify with the first code from their authenticator app
const { recoveryCodes } = await auth.mfa.verifyTotpEnrollment(factorId, "123456");
// Show recoveryCodes — displayed only once

// Verify at login time
await auth.mfa.verifyTotp("123456");

MFA — WebAuthn / Passkey

import { startRegistration, startAuthentication } from "@simplewebauthn/browser";

// Register passkey
const options  = await auth.mfa.beginPasskeyRegistration("MacBook Pro");
const response = await startRegistration(options);
await auth.mfa.completePasskeyRegistration(response, "MacBook Pro");

// Authenticate with passkey
const authOpts = await auth.mfa.beginPasskeyAuthentication();
const authResp = await startAuthentication(authOpts);
await auth.mfa.completePasskeyAuthentication(authResp);

MFA — Recovery codes

const { verified, remainingCodes } = await auth.mfa.verifyRecoveryCode("ABCD-EFGH-IJKL");

MFA — Revoke a factor

await auth.mfa.revokeFactor(factorId);

Multi-device (passwordless mode)

// Must be signed in first.
// On a new device: generate a new key pair and attach it to the authenticated account
const { publicKey } = await auth.addDevice();

// List registered keys for current user
const keys = await auth.listDevices();

// Remove one key by id
await auth.removeDevice(keys[0].id);

Not needed for password mode — the key pair is derived from the password.


Node.js / Backend usage

import { WardAuth, memoryAdapter } from "@wardauth/sdk-js";

const auth = new WardAuth({
  baseUrl: "https://api.wardauth.com",
  apiKey:  "wardauth_live_...",
  storage: memoryAdapter(),  // no localStorage in Node.js
});

// Same API as browser
const session = await auth.signInWithPassword("[email protected]", "password");

Custom storage adapter

// React Native with AsyncStorage
const auth = new WardAuth({
  baseUrl: "...",
  apiKey:  "...",
  storage: {
    get:    (key)      => AsyncStorage.getItem(key),
    set:    (key, val) => AsyncStorage.setItem(key, val),
    remove: (key)      => AsyncStorage.removeItem(key),
  },
});

Built-in adapters:

  • localStorageAdapter — browser default
  • sessionStorageAdapter — cleared on tab close (more secure)
  • memoryAdapter() — in-memory, for SSR / Node.js / tests

Auth state changes

const unsub = auth.onAuthStateChange(event => {
  switch (event.type) {
    case "signed_in":       console.log("Signed in",  event.session.user.sub); break;
    case "signed_out":      console.log("Signed out"); break;
    case "token_refreshed": console.log("Token refreshed"); break;
    case "error":           console.error(event.error); break;
  }
});

unsub(); // unsubscribe

Session management

// Get current session (auto-refreshes if needed)
const session = await auth.getSession();

// Get current user
const user = await auth.getUser();

// Get stored public key
const pubKey = await auth.getPublicKey();

REST API reference

All SDK methods map to these REST endpoints:

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /.well-known/jwks.json | Public keys for access JWT verification (EdDSA) | | GET | /v1/auth/salt?email=... | Get derivation salt for password mode | | POST | /v1/register | Register account (idempotent for same key; conflict for different key on existing account) | | POST | /v1/magic-link/send | Send magic link email (creates user row if new) | | GET | /v1/magic-link/verify?token=...&tenant=... | Verify one-time token → JWT tokens | | POST | /v1/challenge | Request auth challenge (nonce) | | POST | /v1/challenge/verify | Verify signature, get JWT tokens | | POST | /v1/token/refresh | Refresh access token | | POST | /v1/token/revoke | Revoke refresh token (logout) | | POST | /v1/keys | Add a key for authenticated user | | GET | /v1/keys | List keys for authenticated user | | DELETE | /v1/keys/:id | Remove a key for authenticated user | | POST | /v1/mfa/totp/enroll | Start TOTP enrollment | | POST | /v1/mfa/totp/enroll/verify | Confirm TOTP enrollment | | POST | /v1/mfa/totp/verify | Verify TOTP code at login | | POST | /v1/mfa/webauthn/register/begin | Start passkey registration | | POST | /v1/mfa/webauthn/register/complete | Complete passkey registration | | POST | /v1/mfa/webauthn/authenticate/begin | Start passkey authentication | | POST | /v1/mfa/webauthn/authenticate/complete | Complete passkey authentication | | POST | /v1/mfa/recovery/verify | Use a recovery code | | GET | /v1/mfa/factors | List MFA factors | | DELETE | /v1/mfa/factors/:id | Revoke an MFA factor |

Send your tenant API key as X-API-Key or Authorization: Bearer <wardauth_...> (this SDK uses Bearer on public calls). Authenticated user endpoints use X-API-Key plus Authorization: Bearer <access_token>.

Register semantics

  • First registration for an email: 201 with created: true.
  • Retry with same email + same key: success with created: false (idempotent).
  • Existing account with different key: 409 CONFLICT (ACCOUNT_EXISTS_USE_LOGIN pattern).

Security model

  • Password mode: Password never leaves the device. PBKDF2 (600k iterations) derives an Ed25519 seed. Server stores only the public key.
  • Passwordless mode: Random Ed25519 key pair generated on device. Private key stored locally.
  • Both modes: Authentication is challenge-response (server sends nonce, client signs with private key, server verifies with public key).
  • Magic link: One-time emailed token (~10 min, single use); same JWT pair as challenge verify, without a device key pair.
  • Tokens: Access JWTs are EdDSA (Ed25519) — verify on your backend with WARDAUTH_JWKS_URL (public /.well-known/jwks.json on the auth host) or a static WARDAUTH_JWT_PUBLIC_JWK. Refresh tokens stay HS256 (internal to WardAuth). This SDK only decodes the access JWT in the browser for UX (sub, exp); it does not verify signatures.
  • MFA: TOTP, WebAuthn/Passkeys, and recovery codes. All optional, configurable per tenant.

License

MIT