WorkOS
Learn how to sign users in with WorkOS AuthKit and connect its session to Jazz.WorkOS AuthKit hosts the sign-in pages. Run its server-side flow in the
backend that hosts Jazz: the callback stores a sealed session cookie, and authenticate loads it.
server.tsimport { WorkOS } from "@workos-inc/node";import { createJazz, remote } from "jazz-tools/backend";const workos = new WorkOS(process.env.WORKOS_API_KEY, {clientId: WORKOS_CLIENT_ID,});const cookiePassword = process.env.WORKOS_COOKIE_PASSWORD!;// GET /auth/callback: exchange the code and set the `wos_session` cookie.const { sealedSession } = await workos.userManagement.authenticateWithCode({code,clientId: WORKOS_CLIENT_ID,session: { sealSession: true, cookiePassword },});export const jazz = createJazz({app,permissions,store: remote({ appId, url, secret }),authenticate: async (request) => {const sessionData = readCookie(request, "wos_session");if (!sessionData) return null;const session = await workos.userManagement.loadSealedSession({ sessionData, cookiePassword }).authenticate();return session.authenticated? { userId: session.user.id, claims: { role: session.role ?? "" } }: null;},});
WorkOS access tokens inside the session are short-lived. Refresh an expired session once
(session.refresh()) and write the new cookie on your next HTTP response; the example shows one
way to share the refreshed session with sync sockets, which can't set cookies.
If your backend receives WorkOS access tokens instead (for example, from a native app sending
Authorization: Bearer), use fromJWT. Their role, permissions and org_id claims reach your
policies as session.claims:
authenticate: fromJWT({jwksUrl: `https://api.workos.com/sso/jwks/${WORKOS_CLIENT_ID}`,issuer: `https://api.workos.com/user_management/${WORKOS_CLIENT_ID}`,// WorkOS access tokens carry no `aud`; the issuer already names your client id.audience: false,}),
Client
Ask your backend who is signed in, and pass that to Jazz:
App.tsxexport function App() {// Your backend's own endpoint, reading the AuthKit session cookie.const { signIn, signOut } = useWorkOSSession();return (<JazzProviderserverUrl="/jazz"signedOut={<button onClick={() => void signIn()}>Sign in</button>}><YourApp signOut={signOut} /></JazzProvider>);}// Jazz asks your backend's `authenticate` who is signed in on start and on// focus; call `reauthenticate()` from `useJazzAuth()` after your own sign-out.
For a full working example, see the WorkOS chat example.
See Other / Custom for the general bindings, guests, and backend request contexts.