SUPER EARLY WIP — USE AT YOUR OWN RISK
shoo
GitHubCOMING SOON

Use with Convex

Authenticate Convex queries and mutations with Shoo

Shoo ships a Convex adapter that plugs directly into ConvexProviderWithAuth.

Install

bun add @shoojs/react

Configure Convex auth

Add Shoo as a custom JWT provider:

convex/auth.config.ts
export default {
  providers: [
    {
      type: "customJwt",
      issuer: "https://shoo.dev",
      jwks: "https://shoo.dev/.well-known/jwks.json",
      algorithm: "ES256",
    },
  ],
};

To enforce audience checks, add applicationID set to origin:https://your-app-origin.

Wire up the provider

Create a Shoo auth module:

src/shoo.ts
import { createShooConvexAuth } from "@shoojs/react";

export const { useAuth, signIn, signOut } = createShooConvexAuth({
  callbackPath: "/shoo/callback",
});

Then connect it to your Convex provider:

src/main.tsx
import { ConvexProviderWithAuth, ConvexReactClient } from "convex/react";
import { useAuth } from "./shoo";

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);

function Root() {
  return (
    <ConvexProviderWithAuth client={convex} useAuth={useAuth}>
      <App />
    </ConvexProviderWithAuth>
  );
}

Sign in and out

src/App.tsx
import { signIn, signOut } from "./shoo";

function AuthButtons() {
  return (
    <div>
      <button onClick={() => signIn()}>Sign in</button>
      <button onClick={signOut}>Sign out</button>
    </div>
  );
}

Access identity in Convex functions

Use identity.subject as your stable user ID:

convex/users.ts
import { query } from "./_generated/server";

export const viewer = query({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) return null;

    return {
      userId: identity.subject,
      email: identity.email ?? null,
      name: identity.name ?? null,
    };
  },
});

What the adapter provides

createShooConvexAuth returns:

ExportTypeDescription
useAuth() => { isLoading, isAuthenticated, fetchAccessToken }Hook for ConvexProviderWithAuth
signIn(opts?) => Promise<void>Redirect to Shoo sign-in
signOut() => voidClear identity and reload page

The useAuth hook handles the callback exchange on mount, reports isAuthenticated based on the stored identity, and returns the id_token via fetchAccessToken for Convex to use.