Skip to content

React SPA Quickstart

Use this guide when your frontend is a browser-only React app and your backend will validate SigID access tokens separately.

Time: 15-25 minutes after you have a SigID application configuration.

What You Will Build

  • a public PKCE login flow in React
  • a SigID provider around your app
  • a sign-in button, callback route, session display, and logout button
  • an authenticated fetch call to your backend

Before You Start

In Dashboard, choose the tenant, open Applications, click Create Application, and configure a public browser application with:

Field Local example
Allowed Callback URL http://localhost:5173/auth/callback
Allowed Logout URL http://localhost:5173
Allowed Web Origin http://localhost:5173
Allowed Origins (CORS) http://localhost:5173
Client type Public PKCE client
Scopes openid profile email projects:read

If another admin owns the workspace, ask them for the issuer URL, client ID, callback URL, allowed origin, scopes, API audience, and tenant ID from Workspace Admin Quickstart.

Your backend API still needs its own audience, tenant, and scope checks. Do not authorize protected backend data from React state alone.

Install

npm install @sigid/client @sigid/react

Configure The Client

Create src/sigid.ts:

import { createSigIdClient } from "@sigid/client";

export const sigid = createSigIdClient({
  baseURL: import.meta.env.VITE_SIGID_ISSUER_URL,
  oauth: {
    clientId: import.meta.env.VITE_SIGID_CLIENT_ID,
    redirectUri: `${window.location.origin}/auth/callback`,
    scopes: (import.meta.env.VITE_SIGID_SCOPES ?? "openid profile email").split(/\s+/),
  },
});

Create .env.local:

VITE_SIGID_ISSUER_URL=http://auth.sigid.localhost:3000
VITE_SIGID_CLIENT_ID=sigid-react-local
VITE_SIGID_SCOPES="openid profile email projects:read"

Mount The Provider

import { SigIdProvider } from "@sigid/react";
import { sigid } from "./sigid";

export function AppRoot({ children }: { children: React.ReactNode }) {
  return <SigIdProvider client={sigid}>{children}</SigIdProvider>;
}

Add Sign-In And Logout

import { SignedIn, SignedOut, SignInButton, SignOutButton, useUser } from "@sigid/react";

export function Home() {
  const { user } = useUser();

  return (
    <>
      <SignedOut>
        <SignInButton returnTo="/account">Sign in with SigID</SignInButton>
      </SignedOut>
      <SignedIn>
        <p>Signed in as {user?.email ?? user?.id}</p>
        <SignOutButton>Sign out</SignOutButton>
      </SignedIn>
    </>
  );
}

Add A Callback Route

Render this component at /auth/callback:

import { useEffect, useState } from "react";
import { useSigId } from "@sigid/react";

export function SigIdCallback() {
  const { handleCallback } = useSigId();
  const [message, setMessage] = useState("Completing sign-in...");

  useEffect(() => {
    handleCallback()
      .then(() => setMessage("Sign-in complete."))
      .catch((error) => setMessage(error instanceof Error ? error.message : String(error)));
  }, [handleCallback]);

  return <p>{message}</p>;
}

Call Your Backend

Use the SDK transport so the backend receives the bearer token:

import { useSigId } from "@sigid/react";

export function ProjectsButton() {
  const { fetchWithAuth } = useSigId();

  async function loadProjects() {
    const response = await fetchWithAuth("/api/projects");
    if (!response.ok) throw new Error(`API failed: ${response.status}`);
    console.log(await response.json());
  }

  return <button onClick={() => void loadProjects()}>Load projects</button>;
}

Your backend must still validate issuer, audience, tenant, expiry, scope, and subject type. Continue with Backend API Quickstart.

Verify

You are successful when:

  • the React app can start hosted login
  • the callback route completes without a state error
  • signed-in UI displays a user or session identifier without displaying tokens
  • logout returns to the signed-out UI
  • backend calls fail closed when the token is missing or invalid

If the backend accepts requests based only on React state, stop and add backend token validation before launch.