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/react-google-auth

v1.1.0

Published

Google OAuth SDK for React

Readme

@greta/react-google-auth

A Google OAuth SDK for React using a popup + postMessage architecture. Includes a self-hostable Express broker that keeps client_secret server-side and does full PKCE.


Architecture

Browser (React app)                    Broker (Express server)
─────────────────────                  ────────────────────────
signInWithOAuth()
  ↓ if in iframe
  open popup ─────────── GET /~oauth/initiate ──→ generate PKCE
                                                   redirect to Google
                         Google OAuth ──────────→
                         GET /~oauth/callback ←── Google redirect
                                                   exchange code for tokens
  ←──────────── postMessage { authorization_response } ─────
  validate state
  fetch Google userinfo
  store in sessionStorage

Key rules:

  • client_secret never reaches the browser — PKCE lives in the broker.
  • State parameter prevents CSRF on both sides.
  • Same-origin broker works out of the box; cross-origin broker requires setting supportedOAuthOrigins.

Quick start

1. Install

npm install @greta/react-google-auth express

2. Start the broker

Managed mode (env vars):

GOOGLE_CLIENT_ID=xxx GOOGLE_CLIENT_SECRET=yyy PORT=3001 node dist/broker/server.js

BYOK mode — pass credentials at call time (useful for multi-tenant apps):

GET /~oauth/initiate?provider=google&state=…&client_id=xxx&client_secret=yyy

Register http://localhost:3001/~oauth/callback as an authorised redirect URI in your Google Cloud project.

3. Wrap your app

import { GoogleAuthProvider } from "@greta/react-google-auth"

const config = {
  oauthBrokerUrl: "http://localhost:3001/~oauth/initiate",
  // If broker is on a different origin, add it here:
  supportedOAuthOrigins: ["http://localhost:3001"],
}

function App() {
  return (
    <GoogleAuthProvider config={config}>
      <YourRoutes />
    </GoogleAuthProvider>
  )
}

Same-origin broker — if you mount the broker on the same domain (e.g. via a reverse-proxy at /~oauth), omit both options entirely and use the defaults.


Components & hooks

<GoogleSignInButton>

Pixel-accurate Google sign-in button following the Google brand guidelines.

import { GoogleSignInButton } from "@greta/react-google-auth"

<GoogleSignInButton
  theme="light"           // "light" | "dark"
  shape="rectangular"    // "rectangular" | "pill"
  variant="standard"     // "standard" | "icon"
  text="Sign in with Google"
  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

import { useGoogleAuth } from "@greta/react-google-auth"

function Profile() {
  const { user, loading, error, 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>
  )
}

| Value | Type | Description | |-------|------|-------------| | user | GoogleUser \| null | Signed-in user from Google userinfo endpoint | | tokens | OAuthTokens \| null | Raw access + refresh tokens | | loading | boolean | true during sign-in / session restore | | error | Error \| null | Last error | | initialized | boolean | true once sessionStorage has been read | | signIn | (opts?) => Promise<void> | Trigger OAuth flow | | signOut | () => void | Clear session |

The GoogleUser shape mirrors the Google userinfo v3 endpoint:

interface GoogleUser {
  sub: string
  email: string
  email_verified: boolean
  name: string
  picture: string
  given_name: string
  family_name: string
}

useGoogleSignIn() — lightweight hook

Use this when you don't need full session management — just a sign-in button with loading/error state.

import { useGoogleSignIn } from "@greta/react-google-auth"

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

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

Returns { signIn, status, loading, error, reset } where status is "idle" | "loading" | "success" | "error".


<RequireAuth>

Render children only when authenticated.

import { RequireAuth, GoogleSignInButton } from "@greta/react-google-auth"

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

| Prop | Description | |------|-------------| | fallback | Shown while initialized is false | | unauthenticated | Shown when initialized but no user | | children | Shown when authenticated |


withGoogleAuth() — higher-order component

import { withGoogleAuth, type WithGoogleAuthProps } from "@greta/react-google-auth"

interface Props extends WithGoogleAuthProps {
  title: string
}

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

export default withGoogleAuth(Header)

// Usage:
<WrappedHeader title="My App" />  // googleAuth is injected automatically

Core API (core.ts)

The pure TypeScript engine — no React dependency. Use directly if you need auth outside a component tree.

import { signInWithOAuth, generateState, isInIframe } from "@greta/react-google-auth"

const result = await signInWithOAuth("google", {
  oauthBrokerUrl: "/~oauth/initiate",
  supportedOAuthOrigins: ["https://your-broker.example.com"],
})

if (result.redirected) {
  // navigating away — nothing to do
} else if (result.error) {
  console.error(result.error)
} else {
  console.log(result.tokens.access_token)
}

Constants

| Name | Value | |------|-------| | EXPECTED_MESSAGE_TYPE | "authorization_response" | | DEFAULT_OAUTH_BROKER_URL | "/~oauth/initiate" | | DEFAULT_SUPPORTED_OAUTH_ORIGINS | [] (falls back to window.location.origin) | | DEFAULT_MOBILE_DEEP_LINK_REDIRECT_URI | "greta://oauth-callback" | | DEFAULT_DESKTOP_LOCALHOST_REDIRECT_URI | "http://127.0.0.1/iframe-oauth/callback" | | POPUP_CHECK_INTERVAL_MS | 500 | | IFRAME_FALLBACK_TIMEOUT_MS | 120000 |


Broker API

GET /~oauth/initiate

| Query param | Required | Description | |-------------|----------|-------------| | provider | Yes | Must be "google" | | state | Yes | CSRF nonce from the browser client | | redirect_uri | No | Override for mobile/desktop apps | | client_id | BYOK | Google OAuth client ID | | client_secret | BYOK | Google OAuth client secret | | mode | No | "web_message" (default) or "redirect" | | …extraParams | No | Forwarded verbatim to Google (e.g. login_hint) |

GET /~oauth/callback

Google redirects here. Handles PKCE code exchange and delivers tokens via postMessage or URL hash depending on mode.


Security notes

  • PKCE (S256) prevents authorization code interception. The code_verifier is generated per-request and stored server-side for ≤5 minutes.
  • State validation — the browser client generates a random state nonce; the broker returns it unchanged; the client checks it matches before accepting tokens.
  • Origin validation — the client only accepts postMessages from supportedOAuthOrigins.
  • client_secret isolation — the browser never sees the secret; it lives only in the broker process.
  • Session cleanup — expired PKCE sessions are purged every 60 seconds.

Building

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

Output layout:

dist/
  index.js          # CJS — React SDK
  index.mjs         # ESM — React SDK
  index.d.ts        # Types
  broker/
    server.js       # CJS — broker (Node 18+)
    server.d.ts