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

@framework-cwf/auth

v0.2.2

Published

TypeScript port of the Cognito PKCE auth flow from sniply-barber-fe/js/auth.js, with a Server-Component-friendly <AuthProvider> context.

Downloads

115

Readme

@framework-cwf/auth

TypeScript port of the Cognito Hosted UI + PKCE authorization-code flow used by the existing customer-site demo at sniply-barber-fe/js/auth.js. The package preserves the 11-function public surface so downstream code can import named functions instead of reaching for a window.Auth global.

The package ships two complementary surfaces:

  • Core auth module — the 11 plain-TS functions ported from the demo (initiateLogin, handleCallback, resolveSession, …). Plus a configure() entry point so the module is SSR-safe at import time and an authEvents EventTarget that emits typed lifecycle events.
  • React surface<AuthProvider> (Server Component), useAuth() hook, <AuthCallbackHandler /> for the /auth/callback route, plus the <AuthHydrator> client boundary the provider wraps. See "React surface" below and example/ for copy-paste templates.

Both surfaces are import-safe in Node — no window access at module load — so the package drops cleanly into a Next.js static export.

Installation

Published to GitHub Packages under the @framework-cwf scope. Consumers need an .npmrc pointing the scope at the GitHub Packages registry plus an auth token:

@framework-cwf:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
pnpm add @framework-cwf/auth

Public API

| Function | Behaviour | | ----------------------------- | ------------------------------------------------------------------------------------------------------ | | configure(config) | Provide Cognito + proxy coordinates. Must run before any other function. | | initiateLogin() | Generate a PKCE verifier/challenge, store the return URL, redirect to Cognito's /oauth2/authorize. | | handleCallback() | At /auth/callback: exchange ?code= for tokens, persist them, emit auth:login. | | resolveSession() | Load session, refresh if expired or close to expiry (visible tab only), fetch user + customer profile. | | refreshSession(existing) | Exchange the stored refresh token for a new access token; emit auth:refresh or auth:expired. | | loadSession() | Synchronous read of the current unexpired session, or null. | | saveSession(session) | Persist a session to sessionStorage. | | clearSession() | Wipe session + refresh token + PKCE verifier from storage. | | hasSession() / isAuthed() | Boolean: true if a valid session OR a stored refresh token exists. | | logout() | Emit auth:logout, clear session, redirect to Cognito /logout. | | buildLogoutUrl() | Return the Cognito /logout?client_id=…&logout_uri=… URL string. |

Storage

| Token / value | Storage | Key | | ------------------------------ | ---------------- | -------------------- | | accessToken, idToken, etc. | sessionStorage | customer_session | | refresh_token | localStorage | auth_refresh_token | | PKCE code verifier | localStorage | pkce_verifier | | Return URL after login | sessionStorage | auth_return_url |

The choice of session-vs-local mirrors the demo and is load-bearing for the resume-on-reload UX (refresh tokens survive a tab close; access tokens do not).

Upgrades over the legacy demo

  1. Clock-skew tolerance. The proactive-refresh threshold is bumped from 5 minutes to 5 minutes + 60s, so a device clock running fast won't catch us with an unexpectedly-expired token.
  2. visibilitychange gating. Proactive refresh only fires when document.visibilityState === 'visible'. Backgrounded tabs no longer churn refresh tokens.
  3. Typed event emissions. A module-level authEvents EventTarget emits typed CustomEvents for auth:login, auth:logout, auth:refresh, auth:expired, and auth:error. The T1.C.2 <AuthProvider> subscribes here.

Usage

import {
  authEvents,
  configure,
  initiateLogin,
  resolveSession,
} from "@framework-cwf/auth";

configure({
  cognitoClientId: process.env.COGNITO_CLIENT_ID!,
  cognitoHostedUiDomain: "https://dev-auth-booking.rosenheimbookings.com",
  redirectUri: window.location.origin + "/auth/callback/",
  proxyBaseUrl: process.env.PROXY_BASE_URL,
  businessGuid: process.env.BUSINESS_GUID,
});

authEvents.addEventListener("auth:login", (ev) => {
  console.log("logged in", (ev as CustomEvent).detail);
});

const session = await resolveSession();
if (!session) await initiateLogin();

React surface

The package exports a Server-Component-friendly provider and a hook for reading the resolved session from React.

| Export | Where it runs | Purpose | | ------------------------- | ---------------- | -------------------------------------------------------------------------------------------------- | | <AuthProvider> | Server Component | Outer wrapper. Renders only its children + the hydrator — no browser APIs at SSR time. | | <AuthHydrator> | 'use client' | The actual stateful subtree the provider mounts. Re-exported for advanced compositions. | | useAuth() | 'use client' | Returns { status, session, refresh, logout }. Throws if called outside the provider. | | <AuthCallbackHandler /> | 'use client' | Drop-in component for /auth/callback. Calls handleCallback() then redirects to the return URL. | | AuthContext | 'use client' | Raw React context if you need to compose with another provider. |

status cycles through 'loading' → 'authenticated' | 'unauthenticated' on first mount and updates from the authEvents subscription thereafter:

"use client";
import { useAuth, initiateLogin } from "@framework-cwf/auth";

export function AccountWidget() {
  const { status, session, logout } = useAuth();
  if (status === "loading") return <span aria-busy>Checking sign-in…</span>;
  if (status === "unauthenticated")
    return <button onClick={() => void initiateLogin()}>Members login</button>;
  return (
    <span>
      Hi, {(session?.user as { name?: string } | null)?.name ?? "member"}
      <button onClick={() => void logout()}>Sign out</button>
    </span>
  );
}

Wrap your App Router root layout:

// app/layout.tsx — Server Component
import { AuthProvider } from "@framework-cwf/auth";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <AuthProvider>{children}</AuthProvider>
      </body>
    </html>
  );
}

And drop the callback handler at /auth/callback/page.tsx:

// app/auth/callback/page.tsx
"use client";
import { AuthCallbackHandler } from "@framework-cwf/auth";
export default function CallbackPage() {
  return <AuthCallbackHandler />;
}

Error handling

<AuthProvider> is a Server Component, so it cannot accept function props (RSC props must be serialisable). The hydrator logs auth:error events to console.warn by default. Consumers who want richer handling subscribe to authEvents directly:

"use client";
import { authEvents } from "@framework-cwf/auth";
useEffect(() => {
  const onError = (ev: Event) => {
    const { phase, error } = (ev as CustomEvent).detail;
    track("auth_error", { phase, message: error.message });
  };
  authEvents.addEventListener("auth:error", onError);
  return () => authEvents.removeEventListener("auth:error", onError);
}, []);

Perf-neutral by construction

A page that never calls useAuth() ships zero extra client JS for auth — Next.js tree-shakes unused context consumers, and the hydrator's effects fire after first paint. See example/README.md for the empirical Lighthouse comparison plan (lands with apps/template in T1.G.2).

SSR safety

The module is import-safe in Node — no window, document, localStorage, sessionStorage, or fetch access at top level. loadSession(), hasSession(), clearSession(), and saveSession() no-op in a non-browser context; initiateLogin() and handleCallback() throw if called without a browser environment.

Tests

pnpm --filter @framework-cwf/auth test

The unit suite covers every public function across happy and error paths using stubbed window / storage / fetch globals. An optional integration suite (src/integration.test.ts) exercises the dev Cognito user pool (eu-west-2_ofaIjrHM4); it is automatically skipped unless the COGNITO_* env vars listed at the top of the file are present.