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

@genzzi/oauth

v1.6.1

Published

Official Genzzi OAuth 2.0 + OIDC client SDK for SPAs. Secure, framework-agnostic PKCE flow with React, Vue & Solid bindings.

Readme

@genzzi/oauth

Official Genzzi OAuth 2.0 + OIDC client SDK for SPAs. Secure, framework-agnostic PKCE flow with React, Vue & Solid bindings.

npm version npm downloads License: MIT


Features

  • OAuth 2.0 + OIDC Compliant — Full Authorization Code flow with PKCE (S256)
  • Framework Agnostic Core — Use vanilla JS/TS or bind to React, Vue, or Solid
  • Automatic Token Management — Refresh tokens, expiry detection, and rotation safety
  • CSRF Protection — State validation built into every flow
  • Nonce Support — OIDC replay attack prevention
  • Event Lifecycle — Subscribe to flow_started, token_received, logout, and more
  • TypeScript First — Fully typed with strict compiler settings
  • Zero Runtime Dependencies — Core SDK has zero deps; framework bindings are peer deps
  • Vite Plugin — Dev-time stack trace injection for better error reporting

Installation

# npm
npm install @genzzi/oauth

# yarn
yarn add @genzzi/oauth

# pnpm
pnpm add @genzzi/oauth

Peer Dependencies

Framework bindings require their respective peer dependencies. Install only what you need:

# React
npm install react react-dom

# Vue
npm install vue

# Solid
npm install solid-js

All peer dependencies are optional. The core SDK works without any framework.


Quick Start

1. React (Provider + Hook)

import { GenzziAuthProvider, GenzziButton, useGenzziAuth } from "@genzzi/oauth/react";

function App() {
  return (
    <GenzziAuthProvider>
      <RootLayout />
    </GenzziAuthProvider>
  );
}

function RootLayout() {
  const { isAuthenticated, user, logout, isLoading } = useGenzziAuth();

  if (isLoading) return <div>Loading…</div>;

  return isAuthenticated ? (
    <Dashboard user={user} onLogout={logout} />
  ) : (
    <GenzziButton
      oauthConfig={{ client_id: "my-app-123" }}
      onSuccess={(tokens) => console.log("Logged in!", tokens)}
      onOAuthError={(err) => console.error("Auth failed:", err)}
    />
  );
}

2. Vanilla / Framework-Agnostic

import {
  generatePKCE,
  fetchClientConfig,
  startAuthorizationFlow,
  handleCallback,
} from "@genzzi/oauth";

// Step 1: Start the flow
async function signIn() {
  const { verifier, challenge } = await generatePKCE();
  const clientResponse = await fetchClientConfig("my-app-123");

  startAuthorizationFlow({
    config: { client_id: "my-app-123" },
    clientResponse,
    verifier,
    challenge,
  });
}

// Step 2: Handle the callback (run on your redirect URI page)
async function onMount() {
  const { tokens, userInfo } = await handleCallback();
  console.log("Access token:", tokens.access_token);
  console.log("User:", userInfo);
}

3. Vue & Solid

Vue and Solid bindings follow the same patterns. See Framework Guides below.


Core API Reference

PKCE

| Function | Description | |----------|-------------| | generatePKCE() | Generates a cryptographically secure PKCE verifier + S256 challenge pair | | validatePKCEVerifier(verifier) | Validates a verifier against RFC 7636 requirements | | deriveChallenge(verifier) | Reproduces the S256 challenge from a verifier |

Flow Engine

| Function | Description | |----------|-------------| | discover(authServerUrl?) | Fetches the OIDC discovery document (cached for 1 hour) | | fetchClientConfig(clientId) | Fetches public OAuth config for a registered client | | startAuthorizationFlow(options) | Redirects the user to the Genzzi authorization server | | handleCallback() | Exchanges the authorization code for tokens | | storeTokens(tokens) | Persists tokens to localStorage | | getStoredTokens() | Retrieves stored tokens (returns null if expired) | | clearStoredTokens() | Removes all stored tokens and user info | | refreshAccessToken(refreshToken, tokenEndpoint) | Refreshes an expired access token | | fetchUserInfo(accessToken) | Fetches OIDC UserInfo | | introspectToken(token) | Introspects a token (RFC 7662) | | revokeToken(token, tokenTypeHint?) | Revokes a token (RFC 7009) |

Token & JWT Helpers

| Function | Description | |----------|-------------| | isTokenExpired() | Checks if the stored access token is expired (with 1-minute buffer) | | parseJwt(token) | Safely parses a JWT payload | | validateIdTokenNonce(idToken, expectedNonce) | Validates the nonce claim in an ID token | | getCachedUserInfo() | Returns cached user info from localStorage |

Events

import { onGenzziEvent } from "@genzzi/oauth";

const unsubscribe = onGenzziEvent((event) => {
  console.log(event.type, event.detail);
  // "flow_started" | "redirecting" | "callback_received" | "state_validated"
  // "token_exchanging" | "token_received" | "token_refreshed" | "userinfo_received"
  // "error" | "session_expired" | "logout"
});

// Cleanup
unsubscribe();

React API Reference

GenzziAuthProvider

Wraps your app and manages global auth state.

<GenzziAuthProvider
  tokenEndpoint="https://api.genzzi.in/api/v1/oauth/token" // optional
  onEvent={(event) => analytics.track(event.type)}         // optional
>
  {children}
</GenzziAuthProvider>

useGenzziAuth()

const {
  isAuthenticated,  // boolean
  isLoading,        // boolean
  accessToken,      // string | null
  user,             // GenzziUserInfo | null
  error,            // GenzziOAuthError | null
  refresh,          // () => Promise<void>
  logout,           // () => Promise<void>
} = useGenzziAuth();

useGenzziUser(accessToken?)

Fetches and caches OIDC UserInfo independently.

const { user, isLoading, error, refetch } = useGenzziUser();

useGenzziEvent(listener)

React hook wrapper around onGenzziEvent.

useGenzziEvent((event) => {
  if (event.type === "token_received") {
    toast.success("Welcome back!");
  }
});

GenzziButton / Button

Pre-styled OAuth sign-in button with automatic PKCE and callback handling.

<GenzziButton
  oauthConfig={{ client_id: "my-app" }}
  variant="primary"        // "primary" | "secondary" | "ghost" | "danger" | "light" | "dark" | "link"
  size="md"                // "sm" | "md" | "lg" | "xl" | "2xl"
  display="logo-text"      // "logo-only" | "text-only" | "logo-text"
  useTailwind={false}      // Use Tailwind classes instead of inline styles
  loading={false}
  disabled={false}
  onSuccess={(tokens) => {}}
  onOAuthError={(err) => {}}
  onEvent={(event) => {}}
>
  Sign in with Genzzi
</GenzziButton>

Configuration

GenzziOAuthConfig

interface GenzziOAuthConfig {
  client_id: string;                          // Required — your app ID
  authServerUrl?: string;                     // Override default server
  scope?: GenzziScope[];                      // Default: ["openid", "profile", "email"]
  response_type?: "code" | "token" | "id_token";
  code_challenge_method?: "S256" | "plain";   // Default: "S256"
  token_endpoint_auth_method?: "none" | "client_secret_post" | "client_secret_basic";
  client_secret?: string;                     // For confidential clients only
  redirect_uri?: string;                      // Must match registered URI
  backend_uri?: string;                       // Your token exchange proxy
  nonce?: string;                             // Auto-generated if omitted
  state?: string;                             // Auto-generated if omitted
  prompt?: "none" | "login" | "consent" | "select_account";
  max_age?: number;                           // Seconds since last auth
  login_hint?: string;                        // Pre-fill email/username
  acr_values?: string;                        // Step-up auth context
  extraParams?: Record<string, string>;       // Custom authorization params
}

Error Handling

All errors are instances of GenzziOAuthError with structured metadata:

try {
  await handleCallback();
} catch (err) {
  if (err instanceof GenzziOAuthError) {
    console.log(err.code);           // "SECURITY_CSRF_DETECTED"
    console.log(err.message);        // Human-readable message
    console.log(err.recoverable);    // Can the user retry?
    console.log(err.statusCode);     // HTTP status (if from API)
    console.log(err.context);        // Additional debug info
  }
}

Install Global Error Reporter

import { installGenzziErrorReporter } from "@genzzi/oauth";
installGenzziErrorReporter(); // Catches uncaught GenzziOAuthErrors

Vite Plugin

Enhances GenzziButton components with stack traces for better dev error reporting.

// vite.config.ts
import { genzziDevPlugin } from "@genzzi/oauth";

export default {
  plugins: [genzziDevPlugin(), react()],
};

Framework Guides

React

See Quick Start — React above.

Vue

import { createGenzziAuth } from "@genzzi/oauth/vue";

const auth = createGenzziAuth({ client_id: "my-app" });
app.use(auth);

// In components
const { isAuthenticated, user, logout } = useGenzziAuth();

Solid

import { GenzziAuthProvider, useGenzziAuth } from "@genzzi/oauth/solid";

function App() {
  return (
    <GenzziAuthProvider>
      <RootLayout />
    </GenzziAuthProvider>
  );
}

TypeScript

The SDK is written in TypeScript with strict settings. All types are exported:

import type {
  GenzziOAuthConfig,
  GenzziTokenResponse,
  GenzziUserInfo,
  GenzziOAuthError,
  GenzziEvent,
  GenzziEventType,
  GenzziButtonProps,
  // ...and more
} from "@genzzi/oauth";

tsconfig.json Recommendation

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true
  }
}

Security

  • PKCE S256 is enforced by default for all public clients.
  • State validation prevents CSRF attacks on every callback.
  • Nonce validation prevents replay attacks in OIDC flows.
  • Refresh token rotation detection automatically clears sessions on reuse.
  • No client secrets are required for SPAs (public client flow).

Browser Support

Requires a modern browser with:

  • crypto.getRandomValues & crypto.subtle.digest (PKCE)
  • fetch API
  • localStorage / sessionStorage

HTTPS or localhost is required for the Web Crypto API.


Scripts

| Command | Description | |---------|-------------| | npm run build | Build all targets with tsup | | npm run dev | Watch mode development build | | npm run typecheck | Run tsc --noEmit | | npm run lint | ESLint on src/**/*.ts,tsx | | npm test | Run Vitest in watch mode | | npm run test:ci | Run Vitest once (CI) |


Contributing

Contributions are welcome! Please open an issue or pull request on GitHub.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feat/amazing-feature)
  3. Commit your changes (git commit -m 'feat: add amazing feature')
  4. Push to the branch (git push origin feat/amazing-feature)
  5. Open a Pull Request

License

MIT © Genzzi