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

@edcalderon/auth

v1.4.1

Published

A universal, provider-agnostic authentication package (Web + Next.js + Expo/React Native)

Readme

Auth

npm version npm downloads GitHub Web3

A universal, provider-agnostic authentication orchestration package designed for absolute runtime portability. One abstraction that works flawlessly across React Web (18.x/19.x), Next.js (14/15), and React Native/Expo (SDK 50+).

Swap between Supabase, Firebase, Hybrid, or any custom provider without changing a single line of your UX component code.


📋 Latest Changes (v1.4.1)

Added

  • 📚 Documentation: Added packages/auth/docs/ with five guides: authentik-integration-guide.md, provisioning-model.md, upgrade-migration.md, nextjs-examples.md, cig-reference-map.md.
  • Updated README with documentation table and @edcalderon/auth/authentik subpath listing.

For full version history, see CHANGELOG.md and GitHub releases


🚀 Runtime Support Matrix

| Target Runtime | Engine / Framework | Notes | Supported Flow Semantics | |----------------|--------------------|-------|-----------------| | Web | React, Vite, SPA | Standard web APIs available (window) | popup, redirect | | Server | Next.js Client | Compatible with App Router Contexts | redirect, popup | | Native | Expo/React Native | Clean native bundles, strictly no web assumptions | native |


🏗️ Architecture

The package follows a Single Source of Truth model with a Federated OAuth Strategy:

  • Principal Database (Source of Truth): Supabase anchors user identities, metadata, roles, and RLS policies in PostgreSQL.
  • The Orchestrator (@edcalderon/auth): A thin bridge layer exposing a generic interface (User, AuthClient).

The UI consumes a unified context disconnected entirely from provider implementations.


Installation

npm install @edcalderon/auth
# or
pnpm add @edcalderon/auth

Peer Dependencies

Install peers depending on what adapters you use. (The NPM module avoids forcing packages you won't ship to Native vs Web via strict subpath exports).

# Core requirements
pnpm add react react-dom

# Supabase (Adapter peers)
pnpm add @supabase/supabase-js

# Firebase (Hybrid/Pure peers)
pnpm add firebase

# Expo/Native Only
pnpm add react-native

Supabase SQL Templates

If you want an application-owned user table instead of coupling your identity model to auth.users, copy the reference SQL templates in packages/auth/supabase/ into your Supabase project and apply them with supabase db push.

  • 001_create_app_users.sql: vendor-independent public.users table plus secure server-side OIDC upsert RPC
  • 002_sync_auth_users_to_app_users.sql: optional trigger and backfill for projects using Supabase Auth

Authentik OIDC Client (Canonical)

@edcalderon/auth exports a browser-first Authentik OIDC helper that is decoupled from Supabase and can be used with any backend session strategy.

import {
    isAuthentikConfigured,
    startAuthentikOAuthFlow,
    handleAuthentikCallback,
    readOidcSession,
    clearOidcSession,
    hasPendingAuthentikCallback,
} from "@edcalderon/auth";

if (isAuthentikConfigured()) {
    await startAuthentikOAuthFlow("google", {
        providerSourceSlugs: {
            google: "google",
            discord: "discord",
        },
    });
}

if (hasPendingAuthentikCallback(window.location.search)) {
    const session = await handleAuthentikCallback(window.location.search, {
        onSessionReady: async (claims, tokens) => {
            // Optional hook for API upsert/session handoff.
            console.log(claims.sub, tokens.accessToken);
        },
    });

    console.log("OIDC session", session);
}

const existing = readOidcSession();
if (!existing) {
    clearOidcSession();
}

Required env vars (defaults):

| Var | Description | | --- | --- | | EXPO_PUBLIC_AUTHENTIK_ISSUER | https://<host>/application/o/<app-slug>/ | | EXPO_PUBLIC_AUTHENTIK_CLIENT_ID | OAuth2 provider client ID | | EXPO_PUBLIC_AUTHENTIK_REDIRECT_URI | App redirect URI registered in Authentik |

You can override env key names with envKeys and pass direct values with issuer, clientId, and redirectUri.

Authentik setup checklist:

  1. Configure an OAuth2/OIDC provider in Authentik with PKCE enabled.
  2. Ensure redirect URIs match your app origin/path exactly.
  3. Configure source login slugs (providerSourceSlugs) for each social provider.
  4. Use onSessionReady to hand off claims/tokens to your backend session flow.

Known Authentik 2026.2.1 bug workaround:

  • A production hot-patch may be needed in Authentik flow_manager.py around handle_existing_link to avoid duplicate (user_id, source_id) writes when re-linking existing social identities.
  • Track the upstream Authentik issue and re-apply the patch after container upgrades until a fixed release is available.

Subpath Exports (Crucial for RN/Next.js compatibility)

The package avoids bleeding window or document objects into Expo bundles or bleeding heavy native dependencies into web implementations via strict environment exports:

  • @edcalderon/auth (Shared Core interfaces + Contexts)
  • @edcalderon/auth/supabase
  • @edcalderon/auth/firebase-web
  • @edcalderon/auth/firebase-native
  • @edcalderon/auth/hybrid-web
  • @edcalderon/auth/hybrid-native
  • @edcalderon/auth/authentik (Authentik flow + provisioning kit — docs)

Quick Start (Web & Next.js)

1. Unified React Component UI (Usage)

Your component code is 100% blind to what provider or environment you are using. The signIn orchestration handles translating standard intent into provider actions seamlessly.

"use client";
import { useAuth } from "@edcalderon/auth";

export default function Dashboard() {
    const { user, loading, error, signIn, signOutUser } = useAuth();

    if (loading) return <Spinner />;
    if (error) return <p>Error: {error}</p>;

    if (!user) {
        return (
            <button onClick={() => signIn({ provider: "google", flow: "popup" })}>
                Sign In with Google
            </button>
        );
    }

    return (
        <div>
            <p>Welcome, {user.email}</p>
            <button onClick={signOutUser}>Sign Out</button>
        </div>
    );
}

2. Web3 Crypto Wallets (Wagmi / Solana)

Because the orchestration is provider-blind, you can easily pair it with libraries like wagmi or @solana/wallet-adapter-react.

"use client";
import { useAuth } from "@edcalderon/auth";
import { useWallet } from "@solana/wallet-adapter-react";

export function SolanaLogin() {
    const { signIn } = useAuth();
    const wallet = useWallet();

    const handleWeb3SignIn = () => {
        if (!wallet.connected) return;
        
        signIn({ 
            provider: "web3", 
            web3: { 
                chain: "solana", 
                wallet: wallet.wallet?.adapter // Pass the raw wallet adapter
            } 
        });
    }

    return <button onClick={handleWeb3SignIn}>Sign In with Solana</button>;
}

3. Provider Top-Level App Injectors

Wire the environment appropriate class up at your app root.

Supabase (Web/Native Universal)

"use client";
import { AuthProvider } from "@edcalderon/auth";
import { SupabaseClient } from "@edcalderon/auth/supabase";
import { supabase } from "@/lib/supabase";

export function AppProviders({ children }) {
    // Works perfectly in both web and Next.js out of the box
    const client = new SupabaseClient({ supabase });
    return <AuthProvider client={client}>{children}</AuthProvider>;
}

Hybrid (Firebase UI → Supabase Database Session Bridging for Web)

Perfect if you want Firebase to handle the Google popup, but want to automatically consume the ID Token into Supabase to maintain your DB as the source of truth!

"use client";
import { AuthProvider } from "@edcalderon/auth";
import { HybridWebClient } from "@edcalderon/auth/hybrid-web";
import { supabase } from "@/lib/supabase";
import { auth, signInWithPopup, signOut, GoogleAuthProvider } from "@/lib/firebase";

export function AppProviders({ children }) {
    const client = new HybridWebClient({
        supabase,
        firebaseAuth: auth,
        firebaseMethods: { signInWithPopup, signOut, credentialFromResult: GoogleAuthProvider.credentialFromResult },
        googleProvider: new GoogleAuthProvider(),
    });

    return <AuthProvider client={client}>{children}</AuthProvider>;
}

Quick Start (Expo & React Native)

React Native apps cannot safely utilize Web's window or popup assumptions. Because of the unified typings, your components never have to change, you just wire up the specific native adapters.

Hybrid Strategy Native (expo-auth-session)

Instead of trying to pop up Firebase Web via polyfills, explicitly hand over native execution capabilities down to the adapter utilizing React Native Expo equivalents.

import { AuthProvider } from "@edcalderon/auth";
import { HybridNativeClient } from "@edcalderon/auth/hybrid-native";
import { supabase } from "@/lib/supabase";
import { auth, signInWithCredential } from "firebase/auth"; 
import * as Google from 'expo-auth-session/providers/google'; // Or react-native-google-signin

export function ExpoProviders({ children }) {
    // 1. You provide strictly native capability functions out of your Expo ecosystem
    const nativeGoogleHandler = async (options) => {
        // e.g promptAsync()
        // Exchange credential response for Firebase Native Credentials 
        // Return { credential, idToken }
    };

    const client = new HybridNativeClient({
        supabase,
        firebaseAuth: auth,
        firebaseMethods: { signInWithCredential, signOut },
        oauthHandlers: {
            "google": nativeGoogleHandler
        }
    });

    return <AuthProvider client={client}>{children}</AuthProvider>;
}

Now, clicking signIn({ provider: "google", flow: "native" }) from anywhere inside your Expo app safely triggers nativeGoogleHandler and orchestrates Firebase translation down to Supabase seamlessly behind the scenes!


🔌 API Reference - Extensibility

The AuthClient Interface

The core strength of @edcalderon/auth is that any authentication service can be mapped directly onto the AuthClient type, exposing typed portability out-of-the-box.

type AuthRuntime = "web" | "native" | "server";
type OAuthFlow = "popup" | "redirect" | "native";

export interface Web3SignInOptions {
    chain: "ethereum" | "solana" | "bitcoin";
    wallet?: any;
    message?: string;
    signature?: string;
}

export interface SignInOptions {
    provider?: "google" | "apple" | "github" | "web3" | string;
    flow?: OAuthFlow;
    redirectUri?: string;
    web3?: Web3SignInOptions;
}

export interface AuthClient {
    runtime: AuthRuntime;
    capabilities(): { runtime: AuthRuntime; supportedFlows: OAuthFlow[] };
    
    getUser(): Promise<User | null>;
    signInWithEmail(email: string, password: string): Promise<User>;
    signIn(options: SignInOptions): Promise<void>;
    signOut(): Promise<void>;
    
    onAuthStateChange(callback: (user: User | null) => void): () => void;
    getSessionToken(): Promise<string | null>;
}

The User Type

export interface User {
    id: string;
    email?: string;
    avatarUrl?: string;
    provider?: string;
    providerUserId?: string;
    roles?: string[];
    metadata?: Record<string, any>;
}

License

MIT © Edward