Create and revoke a local authentication session
Use the real Clerk backend SDK to create a user and session, verify the issued token's identity and revoke the session. Stop the World, resume it and read the same user and revoked session. No real Clerk account, secret key or Volter platform account is needed.
Use Node 22.6 or newer, npm and an empty app directory. Installation needs the normal
package registry. This example uses admitted @volter/twin-clerk@1.0.2 and the exact
published World dependencies below. Keep the manifest, lockfile and generated service
source together. This is a backend example; it does not walk a browser sign-in screen.
Prepare the app
{
"name": "world-auth-user",
"private": true,
"type": "module",
"dependencies": {
"@clerk/backend": "2.33.3"
},
"devDependencies": {
"@volter/world": "3.0.158",
"@volter/world-core": "3.0.148",
"@volter/world-runtime": "3.0.156",
"@volter/world-console": "3.0.149",
"@volter/twin-clerk": "1.0.2"
}
}Declare the credential names your app reads. The World issues throwaway secret and publishable keys and its public token-verification key when it starts. Leave these values empty; do not copy keys from a real Clerk dashboard.
CLERK_SECRET_KEY=
CLERK_PUBLISHABLE_KEY=
CLERK_JWT_KEY=npm install --no-audit --no-fundCreate auth.mjs. The normal Clerk client uses its vendor API hostname; the World's
injection supplies routing. The SDK's token verification uses the World's public key.
The JWT itself and credential values are never printed.
import { createClerkClient, verifyToken } from '@clerk/backend';
const clerk = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY,
publishableKey: process.env.CLERK_PUBLISHABLE_KEY,
telemetry: { disabled: true },
});
const email = 'ada@example.test';
if (process.argv[2] === 'read') {
const users = await clerk.users.getUserList({ emailAddress: [email] });
for (const user of users.data) {
const sessions = await clerk.sessions.getSessionList({ userId: user.id });
console.log(JSON.stringify({
user: { id: user.id, name: user.firstName, email: user.emailAddresses[0]?.emailAddress },
sessions: sessions.data.map(session => ({ id: session.id, status: session.status })),
}, null, 2));
}
} else {
const user = await clerk.users.createUser({
emailAddress: [email], firstName: 'Ada', lastName: 'Lovelace',
skipPasswordRequirement: true,
});
const session = await clerk.sessions.createSession({ userId: user.id });
const token = await clerk.sessions.getToken(session.id);
const claims = await verifyToken(token.jwt, { jwtKey: process.env.CLERK_JWT_KEY });
console.log(JSON.stringify({
user: { id: user.id, name: user.firstName, email: user.emailAddresses[0]?.emailAddress },
session: { id: session.id, status: session.status },
verifiedIdentity: { userId: claims.sub, sessionId: claims.sid },
}, null, 2));
const revoked = await clerk.sessions.revokeSession(session.id);
const retained = await clerk.sessions.getSession(session.id);
console.log(JSON.stringify({ revoked: revoked.status, retrieved: retained.status }, null, 2));
}Select Clerk and run the backend workflow
./node_modules/.bin/volter world init --name auth-user --twins clerk --source clerk=@volter/twin-clerkReview the detected vendor before booting: this app needs Clerk for user and session state. Keep only Clerk; the app has no other vendor dependency. Init writes the World configuration and reports the installed Clerk source.
./node_modules/.bin/volter world up
./node_modules/.bin/volter world clock set 2026-01-15T12:00:00Z
./node_modules/.bin/volter world run -- node auth.mjsThe client prints Ada's user ID, an active session and the verified token's matching
user and session IDs. It then prints revoked for both the revocation response and a
fresh session lookup. IDs are created for this World, so yours can differ.
This uses Clerk's backend session-creation operation for local app development, not a person's interactive sign-in. Signed-token verification establishes the token's claims; it is separate from reading whether the stored session is active or revoked. This example does not claim an already issued token becomes unverifiable after revocation.
./node_modules/.bin/volter world logThe log shows the user creation, session creation and revocation. Those are stored vendor operations, performed through the SDK rather than fabricated with a handler.
Resume the same user and session
./node_modules/.bin/volter world down
./node_modules/.bin/volter world up
./node_modules/.bin/volter world run -- node auth.mjs read
./node_modules/.bin/volter world downThe read-only client lists Ada and the same revoked session without creating another user. Down stops compute and retains state. Reset would discard the current branch's records and reload default data; use it deliberately before replaying the write path.
For your own application, keep its real SDK and normal credential names and use its usual command inside the World. Check the selected Clerk release for operation-specific gaps before depending on another flow. This walk covers backend users, sessions, signed-token verification, revocation and retained readback. Browser sign-in, organization permissions, MFA, passkeys and enterprise SSO were not exercised.
Continue with inspecting stored changes, using your existing app or recovering a failed call.