docs/docs/00200-core-concepts/00500-authentication/00300-Clerk.md
import { StepByStep, Step, StepText, StepCode } from "@site/src/components/Steps"; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
This guide will walk you through integrating Clerk authentication with your SpacetimeDB React application. You will configure a Clerk application, obtain a JWT from Clerk, and pass it to your SpacetimeDB connection as the authentication token.
We assume you have the following prerequisites in place:
main.tsx).http://localhost:5173 if you are using Vite).AutoLogin pattern in the Auth0 tutorial.Create a file named ClerkTokenProvider.tsx.
</StepText>
<StepCode>
import React, {
createContext,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import { useAuth, RedirectToSignIn } from '@clerk/clerk-react';
const TokenContext = createContext<string | undefined>(undefined);
export function useClerkToken() {
const token = useContext(TokenContext);
if (!token) {
throw new Error('useClerkToken must be used within a ClerkTokenProvider');
}
return token;
}
/**
* ClerkTokenProvider:
* - If signed out: renders Clerk's redirect component.
* - If signed in: loads a Clerk session token (JWT) and provides it via context.
*
* Note:
* - getToken() returns a token suitable for sending to your backend. If you have
* configured a specific JWT template in Clerk, pass its name via
* getToken({ template: "<YOUR_TEMPLATE_NAME>" }).
*/
export function ClerkTokenProvider({
children,
}: {
children: React.ReactNode;
}) {
const { isLoaded, isSignedIn, getToken } = useAuth();
const [token, setToken] = useState<string | null>(null);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
let cancelled = false;
async function run() {
if (!isLoaded) return;
// IMPORTANT: if signed out, clear any cached token
if (!isSignedIn) {
if (!cancelled) setToken(null);
return;
}
try {
// If you use a Clerk JWT template, use:
// const t = await getToken({ template: "<YOUR_TEMPLATE_NAME>" });
const t = await getToken();
if (!t) {
throw new Error('Clerk returned no session token.');
}
if (!cancelled) setToken(t);
} catch (e) {
if (!cancelled) setError(e as Error);
}
}
run();
return () => {
cancelled = true;
};
}, [isLoaded, isSignedIn, getToken]);
const value = useMemo<string | undefined>(() => token ?? undefined, [token]);
if (error) {
return (
<div>
<p>Authentication error</p>
<pre>{error.message}</pre>
</div>
);
}
if (!isLoaded) {
return <p>Loading...</p>;
}
if (!isSignedIn) {
// Sends the user to Clerk sign-in. After sign-in, they return to the app.
return <RedirectToSignIn />;
}
if (!token) {
return <p>Loading...</p>;
}
return (
<TokenContext.Provider value={value}>{children}</TokenContext.Provider>
);
}
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { ClerkProvider } from '@clerk/clerk-react';
import App from './App.tsx';
import { ClerkTokenProvider } from './ClerkTokenProvider.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ClerkProvider publishableKey="<YOUR_CLERK_PUBLISHABLE_KEY>">
<ClerkTokenProvider>
<App />
</ClerkTokenProvider>
</ClerkProvider>
</StrictMode>
);
useClerkToken.DbConnection builder using .withToken(...).This mirrors the Auth0 flow: SpacetimeDB receives a bearer token (JWT) and can validate it server-side. </StepText> <StepCode>
import { useMemo } from 'react';
import { Identity } from 'spacetimedb';
import { SpacetimeDBProvider } from 'spacetimedb/react';
import { DbConnection, ErrorContext } from './module_bindings';
import { useClerkToken } from './ClerkTokenProvider';
const onConnect = (_conn: DbConnection, identity: Identity) => {
console.log(
'Connected to SpacetimeDB with identity:',
identity.toHexString()
);
};
const onDisconnect = () => {
console.log('Disconnected from SpacetimeDB');
};
const onConnectError = (_ctx: ErrorContext, err: Error) => {
console.log('Error connecting to SpacetimeDB:', err);
};
export default function App() {
const token = useClerkToken();
const connectionBuilder = useMemo(() => {
return DbConnection.builder()
.withUri('<YOUR SPACETIMEDB URL>')
.withDatabaseName('<YOUR SPACETIMEDB MODULE NAME>')
.withToken(token)
.onConnect(onConnect)
.onDisconnect(onDisconnect)
.onConnectError(onConnectError);
}, [token]);
return (
<SpacetimeDBProvider connectionBuilder={connectionBuilder}>
<div>
<h1>SpacetimeDB React App</h1>
<p>
You can now use SpacetimeDB in your app with Clerk authentication!
</p>
</div>
</SpacetimeDBProvider>
);
}
import { UserButton } from '@clerk/clerk-react';
export function Header() {
return (
<header
style={{ display: 'flex', justifyContent: 'flex-end', padding: 12 }}
>
<UserButton />
</header>
);
}
You are now set up to use Clerk authentication in your React application. When users access your app, they will be redirected to Clerk for sign-in, a session token (JWT) will be retrieved in the browser, and that token will be used to authenticate your SpacetimeDB connection.
If you are using Clerk JWT templates (recommended for controlling claims/audience/issuer), update the token retrieval line to:
await getToken({ template: '<YOUR_TEMPLATE_NAME>' });
and ensure your SpacetimeDB authentication layer validates the corresponding issuer and signing keys.