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

@passkeyme/auth

v2.0.10

Published

Passkeyme Authentication SDK - Simpler than Firebase Auth

Readme

@passkeyme/auth

A simple, powerful authentication SDK that makes authentication easier than Firebase Auth.

🚀 Features

  • 🔐 Multiple Auth Methods: OAuth (Google, GitHub), Passkeys (WebAuthn), Username/Password
  • � Hosted Authentication: Secure, pre-built auth pages - no UI to build
  • 🔄 Automatic Token Management: Auto-refresh, HTTP interceptors
  • 🏠 Privacy-First: Self-hosted option, no vendor lock-in
  • 📱 Modern: Built-in passkey support, TypeScript-first
  • ⚡ Lightweight: Pure JavaScript, no heavy dependencies

📦 Installation

npm install @passkeyme/auth

🔧 Quick Start

1. Initialize the SDK

import { PasskeymeAuth } from "@passkeyme/auth";

const auth = new PasskeymeAuth({
  appId: "your-passkeyme-app-id",
  redirectUri: "http://localhost:3000/auth/callback",
});

// Initialize on app start
await auth.init();

2. Choose Your Authentication Approach

Approach 1: Hosted Auth Pages ✨ (Recommended)

// Redirects to your branded hosted login page
// User sees all available login methods (OAuth, Passkey, Username/Password)
auth.redirectToLogin();

// With options
auth.redirectToLogin({
  authMethod: "passkey", // Pre-select passkey method
  state: "custom-state", // Custom state for callback
});

Approach 2: Direct OAuth

// Skip hosted page, go directly to OAuth provider
auth.redirectToOAuth("google");
auth.redirectToOAuth("github");
auth.redirectToOAuth("facebook");

// With custom redirect URI
auth.redirectToOAuth("google", "http://localhost:3000/custom/callback");

The Key Difference:

  • redirectToLogin() → Shows your branded page with all auth options
  • redirectToOAuth('google') → Goes directly to Google OAuth (skips hosted page)

Direct OAuth Provider

// Skip login page, go directly to OAuth provider
auth.redirectToOAuth("google");
auth.redirectToOAuth("github");
auth.redirectToOAuth("facebook");

3. Handle Authentication Callback

On your callback page (/auth/callback):

// This handles the authentication return and gets the user
const user = await auth.handleAuthCallback();

if (user) {
  // Redirect to your app
  window.location.href = "/dashboard";
} else {
  // Handle auth failure
  window.location.href = "/login?error=auth_failed";
}

4. Check Authentication Status

// Check if user is logged in
const user = auth.getCurrentUser();

if (user) {
  console.log("Logged in as:", user.email);
} else {
  console.log("Not logged in");
}

// Or check auth state
console.log("Auth state:", auth.state);

// Check on page load
await auth.init(); // This will restore session if available

5. Logout

await auth.logout();

🔗 HTTP Client Integration

Automatically add auth headers to your API calls:

With Axios

import axios from "axios";
import { setupHttpInterceptor } from "@passkeyme/auth";

const apiClient = axios.create({
  baseURL: "https://your-api.com",
});

// Setup automatic token management
const cleanup = setupHttpInterceptor(auth, apiClient);

// Now all requests automatically include auth headers
const response = await apiClient.get("/protected-data");

With Fetch

import { createAuthenticatedFetch } from "@passkeyme/auth";

const authenticatedFetch = createAuthenticatedFetch(auth);

// Use like regular fetch, but with automatic auth headers
const response = await authenticatedFetch("/api/protected-data");

Manual Header Management

import { addAuthHeader } from "@passkeyme/auth";

const headers = await addAuthHeader(auth, {
  "Content-Type": "application/json",
});

fetch("/api/data", { headers });

🎭 Event Handling

Listen to authentication events:

const unsubscribe = auth.addEventListener((event) => {
  switch (event.type) {
    case "login":
      console.log("User logged in:", event.user);
      break;
    case "logout":
      console.log("User logged out");
      break;
    case "token_refresh":
      console.log("Token refreshed");
      break;
    case "error":
      console.error("Auth error:", event.error);
      break;
  }
});

// Clean up when done
unsubscribe();

⚙️ Configuration Options

Basic Configuration

const auth = new PasskeymeAuth({
  appId: "your-app-id", // Required: From Passkeyme dashboard
  redirectUri: "http://localhost:3000/callback", // Optional: Default callback URL
  debug: true, // Optional: Enable debug logging
});

Custom Domain Configuration

const auth = new PasskeymeAuth({
  appId: "your-app-id",
  apiUrl: "https://auth.yourdomain.com", // Your custom domain
  redirectUri: "https://yourapp.com/callback",
});

Self-Hosted Configuration

const auth = new PasskeymeAuth({
  appId: "your-app-id",
  apiUrl: "http://localhost:8000", // Your self-hosted server
  redirectUri: "http://localhost:3000/callback",
  debug: true, // Helpful for development
});

Storage Configuration

import { PasskeymeAuth, BrowserStorageProvider } from "@passkeyme/auth";

// Default behavior (localStorage)
const auth = new PasskeymeAuth({
  appId: "your-app-id",
});

// Use sessionStorage instead
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  storage: new BrowserStorageProvider(true), // true = use sessionStorage
});

// Custom storage implementation
class CustomStorageProvider {
  async getItem(key) {
    /* your implementation */
  }
  async setItem(key, value) {
    /* your implementation */
  }
  async removeItem(key) {
    /* your implementation */
  }
}

const auth = new PasskeymeAuth({
  appId: "your-app-id",
  storage: new CustomStorageProvider(),
});

State Subscription

// Subscribe to authentication state changes
const unsubscribe = auth.subscribe((state) => {
  console.log("Auth state changed:", state);
  // { user, loading, error, isAuthenticated }
});

// Get current state
const currentState = auth.getState();

// Clean up when done
unsubscribe();

🆚 vs Firebase Auth

| Feature | Passkeyme Auth | Firebase Auth | | -------------------- | -------------------------- | --------------------------- | | Setup Complexity | ✅ Single provider | ❌ Multiple services needed | | Hosted Auth UI | ✅ Pre-built, customizable | ❌ Build your own | | Passkey Support | ✅ Built-in WebAuthn | ❌ Manual implementation | | Self-Hosting | ✅ Full control | ❌ Google-only | | Vendor Lock-in | ✅ Standard JWT | ❌ Firebase-specific | | Privacy | ✅ Your data | ❌ Google's servers | | Bundle Size | ✅ Lightweight | ❌ Heavy SDK |

📋 API Reference

PasskeymeAuth Class

class PasskeymeAuth {
  // Properties
  readonly state: AuthState;

  // Methods
  init(): Promise<void>;
  redirectToLogin(options?: RedirectOptions): void;
  redirectToOAuth(
    provider: "google" | "github" | "facebook",
    options?: RedirectOptions
  ): void;
  handleAuthCallback(): Promise<User | null>;
  logout(): Promise<void>;
  getCurrentUser(): User | null;
  getAccessToken(): Promise<string | null>;
  refreshToken(): Promise<string>;
  addEventListener(listener: AuthEventListener): () => void;
  removeEventListener(listener: AuthEventListener): void;
}

Types

interface User {
  id: string;
  email?: string;
  name?: string;
  picture?: string;
  provider?: string;
  createdAt?: string;
  lastLoginAt?: string;
}

interface AuthState {
  user: User | null;
  loading: boolean;
  error: string | null;
  isAuthenticated: boolean;
}

interface RedirectOptions {
  redirectTo?: string; // Where to go after successful auth
  state?: string; // Custom state parameter
}

🔧 Advanced Usage

Handling the Complete Flow

// 1. App initialization
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  redirectUri: "http://localhost:3000/auth/callback",
});

await auth.init();

// 2. Login button click
document.getElementById("login-btn").addEventListener("click", () => {
  auth.redirectToLogin({ redirectTo: "/dashboard" });
});

// 3. On callback page (/auth/callback)
const user = await auth.handleAuthCallback();
if (user) {
  // Redirect to the intended page
  const urlParams = new URLSearchParams(window.location.search);
  const redirectTo = urlParams.get("redirectTo") || "/";
  window.location.href = redirectTo;
}

🔗 TypeScript Integration

This package is written in TypeScript and provides comprehensive type definitions.

Type Imports

import type {
  PasskeymeConfig,
  User,
  AuthTokens,
  OAuthProvider,
  PasskeyCredential,
  AuthState,
  TokenStorage,
  HttpInterceptor,
} from "@passkeyme/auth";

// Configuration interface
interface PasskeymeConfig {
  appId: string;
  baseUrl?: string; // Default: 'https://auth.passkeyme.com'
  redirectUri: string;
  debug?: boolean; // Enable debug logging
  tokenStorage?: TokenStorage; // Custom token storage
  httpInterceptors?: HttpInterceptor[]; // Custom HTTP interceptors
}

// User object structure
interface User {
  id: string;
  email: string;
  name?: string;
  avatar?: string;
  emailVerified: boolean;
  createdAt: string;
  lastLoginAt: string;
  metadata?: Record<string, any>;
}

// Authentication tokens
interface AuthTokens {
  accessToken: string;
  refreshToken: string;
  expiresAt: number;
  tokenType: string;
}

Advanced Configuration

import { PasskeymeAuth, LocalStorageTokenStorage } from "@passkeyme/auth";

// Custom token storage implementation
class SecureTokenStorage implements TokenStorage {
  async setTokens(tokens: AuthTokens): Promise<void> {
    // Store in encrypted storage or secure keychain
    await secureStorage.set("auth_tokens", encrypt(tokens));
  }

  async getTokens(): Promise<AuthTokens | null> {
    const encrypted = await secureStorage.get("auth_tokens");
    return encrypted ? decrypt(encrypted) : null;
  }

  async clearTokens(): Promise<void> {
    await secureStorage.remove("auth_tokens");
  }
}

// HTTP interceptor for API calls
const apiInterceptor: HttpInterceptor = {
  request: async (config) => {
    // Add custom headers or modify requests
    config.headers = {
      ...config.headers,
      "X-Client-Version": "1.0.0",
      "X-Client-Platform": "web",
    };
    return config;
  },

  response: async (response) => {
    // Handle responses globally
    if (response.status === 401) {
      // Handle unauthorized responses
      await auth.logout();
      window.location.href = "/login";
    }
    return response;
  },
};

const auth = new PasskeymeAuth({
  appId: "your-app-id",
  redirectUri: "https://yourapp.com/auth/callback",
  debug: process.env.NODE_ENV === "development",
  tokenStorage: new SecureTokenStorage(),
  httpInterceptors: [apiInterceptor],
});

API Client Integration

import { PasskeymeAuth } from "@passkeyme/auth";

class ApiClient {
  private auth: PasskeymeAuth;

  constructor(auth: PasskeymeAuth) {
    this.auth = auth;
  }

  async makeAuthenticatedRequest<T>(
    url: string,
    options: RequestInit = {}
  ): Promise<T> {
    const authHeader = await this.auth.getAuthHeader();

    const response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        Authorization: authHeader,
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      if (response.status === 401) {
        // Try to refresh token
        await this.auth.refreshToken();
        // Retry request with new token
        return this.makeAuthenticatedRequest(url, options);
      }
      throw new Error(`API Error: ${response.status}`);
    }

    return response.json();
  }

  // Type-safe API methods
  async getUserProfile(): Promise<User> {
    return this.makeAuthenticatedRequest<User>("/api/user/profile");
  }

  async updateUserProfile(updates: Partial<User>): Promise<User> {
    return this.makeAuthenticatedRequest<User>("/api/user/profile", {
      method: "PATCH",
      body: JSON.stringify(updates),
    });
  }
}

Framework Integration Examples

Vanilla JavaScript (SPA)

// main.js
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  redirectUri: window.location.origin + "/auth/callback",
});

await auth.init();

// Router logic
if (window.location.pathname === "/auth/callback") {
  const user = await auth.handleAuthCallback();
  if (user) {
    window.location.href = "/dashboard";
  }
} else if (!auth.getCurrentUser() && window.location.pathname !== "/login") {
  window.location.href = "/login";
}

Next.js Pages Router

// pages/auth/callback.js
import { useEffect } from "react";
import { useRouter } from "next/router";
import { auth } from "../lib/auth";

export default function AuthCallback() {
  const router = useRouter();

  useEffect(() => {
    auth.handleAuthCallback().then((user) => {
      if (user) {
        router.push("/dashboard");
      } else {
        router.push("/login?error=auth_failed");
      }
    });
  }, []);

  return <div>Completing authentication...</div>;
}

🛠️ Development

This package is part of the Passkeyme project. For development:

# Install dependencies
npm install

# Build the package
npm run build

# Run tests
npm test

# Development mode (watch)
npm run dev

📄 License

MIT - see LICENSE file for details.

🤝 Support

  • Documentation: https://docs.passkeyme.com
  • Issues: https://github.com/passkeyme/passkeyme/issues
  • Discord: https://discord.gg/passkeyme