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

@questlabs/auth-js

v1.0.1

Published

OAuth SDK for React — supports Google, Apple, and more

Readme

@questlabs/auth-js

OAuth SDK for React — supports Google (and more providers in future). Includes a self-hostable Express broker that keeps client_secret server-side with full PKCE.


Architecture

Browser (React app)                    Broker (Express server)
─────────────────────                  ────────────────────────
signInWithOAuth()
  ↓ if standalone
  full page redirect ── GET /~oauth/initiate ──→ generate PKCE
                                                  redirect to Google
                        Google OAuth ──────────→
                        GET /~oauth/callback ←── Google redirect
                                                  exchange code for tokens
  ←─────────── redirect back to app with #tokens ───────────

  ↓ if in iframe (builder)
  open popup ──────── GET /~oauth/initiate ──→ generate PKCE
                                                redirect to Google
                       GET /~oauth/callback ←── Google redirect
                       redirect to /~oauth/relay on app's own origin
  ←──────── window.opener.postMessage { authorization_response } ──
  validate state → tokens returned directly

Key rules:

  • client_secret never reaches the browser — PKCE lives in the broker.
  • State parameter prevents CSRF on both sides.
  • Iframe/builder flow uses a relay page at the app's own origin so window.opener is preserved.

Quick start

1. Install

npm install @questlabs/auth-js

2. Use the hosted broker

A broker is already running at https://oauth.greta.sh. No setup needed — it's the default.

To self-host:

GOOGLE_CLIENT_ID=xxx GOOGLE_CLIENT_SECRET=yyy PORT=3001 node node_modules/@questlabs/auth-js/dist/broker/server.js

Register https://oauth.greta.sh/~oauth/callback (or your broker's callback URL) as an authorised redirect URI in your Google Cloud project.

3. Sign in

import { createGretaAuth } from "@questlabs/auth-js"

const auth = createGretaAuth()
// Custom broker:
// const auth = createGretaAuth({ oauthBrokerUrl: "http://localhost:3001/~oauth/initiate" })

const result = await auth.signInWithOAuth("google", {
  redirect_uri: window.location.origin,
})

if (result.tokens) {
  console.log(result.tokens.access_token)
} else if (result.redirected) {
  // standalone flow — page navigated away, handle on return with useGretaAuthCallback
} else {
  console.error(result.error)
}

Components & hooks

useGretaAuthCallback() — handle redirect return

Call this on the page the user lands on after Google redirects back (redirect/standalone flow).

import { useGretaAuthCallback } from "@questlabs/auth-js"

function App() {
  useGretaAuthCallback({
    onUser: (user, accessToken) => {
      // user = { sub, email, name, picture, ... }
      // persist session here
    },
  })
}

<GoogleSignInButton>

import { GoogleSignInButton } from "@questlabs/auth-js"

<GoogleSignInButton
  theme="light"
  shape="rectangular"
  onSuccess={(tokens) => console.log(tokens)}
  onError={(err) => console.error(err)}
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | theme | "light" \| "dark" | "light" | Button colour scheme | | shape | "rectangular" \| "pill" | "rectangular" | Border radius | | variant | "standard" \| "icon" | "standard" | Show label or icon only | | text | string | "Sign in with Google" | Button label | | onSuccess | (tokens: OAuthTokens) => void | — | Called on success | | onError | (error: Error) => void | — | Called on failure | | config | GretaAuthConfig | — | Override broker URL / origins | | options | SignInWithOAuthOptions | — | redirect_uri, extraParams | | disabled | boolean | — | Disable the button |


useGoogleAuth() — session context

Wrap your app in <GoogleAuthProvider> first.

import { GoogleAuthProvider, useGoogleAuth } from "@questlabs/auth-js"

function App() {
  return (
    <GoogleAuthProvider config={{ oauthBrokerUrl: "https://oauth.greta.sh/~oauth/initiate" }}>
      <YourRoutes />
    </GoogleAuthProvider>
  )
}

function Profile() {
  const { user, loading, signIn, signOut, initialized } = useGoogleAuth()

  if (!initialized) return <p>Loading…</p>
  if (!user) return <button onClick={() => signIn()}>Sign in</button>

  return (
    <div>
      <img src={user.picture} alt={user.name} />
      <p>{user.email}</p>
      <button onClick={signOut}>Sign out</button>
    </div>
  )
}

useGoogleSignIn() — lightweight hook

import { useGoogleSignIn } from "@questlabs/auth-js"

function LoginPage() {
  const { signIn, status, error } = useGoogleSignIn({
    onSuccess: (tokens) => saveTokens(tokens),
    onError: (err) => alert(err.message),
  })

  return (
    <button onClick={() => signIn()} disabled={status === "loading"}>
      {status === "loading" ? "Signing in…" : "Sign in"}
    </button>
  )
}

<RequireAuth>

import { RequireAuth, GoogleSignInButton } from "@questlabs/auth-js"

<RequireAuth
  fallback={<p>Loading…</p>}
  unauthenticated={<GoogleSignInButton onSuccess={...} />}
>
  <Dashboard />
</RequireAuth>

withGoogleAuth() — higher-order component

import { withGoogleAuth, type WithGoogleAuthProps } from "@questlabs/auth-js"

function Header({ title, googleAuth }: { title: string } & WithGoogleAuthProps) {
  return (
    <header>
      <h1>{title}</h1>
      {googleAuth.user && <button onClick={googleAuth.signOut}>Sign out</button>}
    </header>
  )
}

export default withGoogleAuth(Header)

Core API

import { createGretaAuth } from "@questlabs/auth-js"

const auth = createGretaAuth({
  oauthBrokerUrl: "https://oauth.greta.sh/~oauth/initiate",
  supportedOAuthOrigins: ["https://oauth.greta.sh"],
})

const result = await auth.signInWithOAuth("google", {
  redirect_uri: window.location.origin,
})

Broker API

GET /~oauth/initiate

| Query param | Required | Description | |-------------|----------|-------------| | provider | Yes | e.g. "google" | | state | Yes | CSRF nonce from the browser | | redirect_uri | No | App origin for redirect flow | | response_mode | No | "web_message" for iframe/popup flow | | …extraParams | No | Forwarded to provider (e.g. login_hint) |

GET /~oauth/callback

Google redirects here. Exchanges code for tokens via PKCE, then either redirects back to the app (redirect flow) or to the relay page (iframe flow).


Security

  • PKCE (S256)code_verifier generated per-request, stored server-side for ≤5 minutes.
  • State validation — client generates nonce, broker echoes it, client verifies before accepting tokens.
  • Origin validation — client only accepts postMessages from supportedOAuthOrigins.
  • client_secret isolation — never sent to the browser.
  • Session cleanup — expired PKCE sessions purged every 60 seconds.

Building

npm run build       # emit dist/
npm run dev         # watch mode
npm run type-check  # tsc --noEmit