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.ts
import { 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.tsx
export function App() {
// Your backend's own endpoint, reading the AuthKit session cookie.
const { signIn, signOut } = useWorkOSSession();
return (
<JazzProvider
serverUrl="/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.