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.
The examples assume an existing Vite + React TypeScript app and a router that
serves /auth/callback. The current React SDK requires React 19.2.8 or later.
For another bundler, adapt the environment-variable reads. For a static page,
use the drop-in script.
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
fetchcall to your backend
New Evaluation: Provision A Sandbox¶
A coding agent can create its own sandbox organization, tenant, application, and ordinary fixture user without a dashboard session. Use the agent quickstart and run:
npx @sigid/cli setup --name my-evaluation --framework react \
--redirect-uri http://localhost:5173/auth/callback
Map the returned tenant_issuer, client_id, and tenant_id using
Integration Settings.
The generic env_block is not a complete framework environment file; configure
the API audience and scopes separately before testing the protected API.
Setup derives allowed browser origins from the exact redirect URI;
use your actual preview URL if it differs from the local example. Keep bootstrap
management tokens, client secrets and fixture passwords in the private credential
file. Configure the app with the public issuer/client ID; let the SDK manage its
OAuth session.
For unattended execution, supply the keystore passphrase through the CLI's
--passphrase-file or --passphrase-stdin option.
Use the returned fixture human for real hosted sign-in, callback, signed-in UI,
and logout. The CLI saves its once-only credentials in the owner-only,
gitignored .dev/sandbox-fixture.env; keep that file private. Successful
metadata probes or expected error responses do not prove hosted login works.
Invite or hand off to the human operator after the integration works; activation
of the sandbox for production still requires a human owner at AAL2.
For an existing managed workspace, follow the admin configuration path below.
init requires an existing tenant and dynamic client registration may require
an initial access token; neither replaces sandbox setup. For bootstrap failures,
use the agent quickstart troubleshooting
with the failing phase, CLI/server versions and sanitized problem/request ID.
Before You Start¶
For an existing managed workspace, 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¶
Configure The Client¶
Create src/sigid.ts:
import { createSigIdClient } from "@sigid/client";
const issuer = import.meta.env.VITE_SIGID_ISSUER_URL?.trim();
const clientId = import.meta.env.VITE_SIGID_CLIENT_ID?.trim();
if (!issuer || !clientId) {
throw new Error("Set VITE_SIGID_ISSUER_URL and VITE_SIGID_CLIENT_ID, then restart the dev server.");
}
export const sigid = createSigIdClient({
baseURL: issuer,
oauth: {
clientId,
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="/">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. Configure your router and hosting
fallback so direct navigation to this path loads the React app. The ref guard
prevents React Strict Mode's development effect replay from exchanging the same
single-use code twice. After success, the return link opens your signed-in home:
import { useEffect, useRef, useState } from "react";
import { useSigId } from "@sigid/react";
export function SigIdCallback() {
const { handleCallback } = useSigId();
const [message, setMessage] = useState("Completing sign-in...");
const started = useRef(false);
useEffect(() => {
if (started.current) return;
started.current = true;
handleCallback()
.then(() => setMessage("Sign-in complete."))
.catch((error) => setMessage(error instanceof Error ? error.message : String(error)));
}, [handleCallback]);
return <div><p>{message}</p><a href="/">Return to the app</a></div>;
}
Call Your Backend¶
Use the SDK transport so the backend receives the bearer token:
import { useState } from "react";
import { useSigId } from "@sigid/react";
export function ProjectsButton() {
const { fetchWithAuth } = useSigId();
const [message, setMessage] = useState("");
async function loadProjects() {
setMessage("Loading...");
try {
const response = await fetchWithAuth("/api/projects");
if (!response.ok) throw new Error(`API failed: ${response.status}`);
await response.json(); // pass the result to your project-list UI
setMessage("Projects loaded.");
} catch (error) {
setMessage(error instanceof Error ? error.message : "Could not load projects.");
}
}
return <div>
<button onClick={() => void loadProjects()}>Load projects</button>
<p role="status">{message}</p>
</div>;
}
/api/projects is an application route you must implement. In Vite development,
configure a dev-server proxy for /api to your backend, or use your trusted
backend's absolute URL and configure its CORS to allow the frontend origin and
Authorization header. Never pass a user-supplied URL to fetchWithAuth, since
it attaches credentials. Render loading, success, and failure states in your UI.
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.