Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/dull-breads-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
116 changes: 116 additions & 0 deletions integration/tests/debug-and-custom-jwt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
import { appConfigs } from '../presets';
import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('Custom JWT templates with skipCache @nextjs', () => {
test.describe.configure({ mode: 'serial' });

let app: Application;
let fakeUser: FakeUser;
let jwtTemplateId: string;
const jwtTemplateName = `e2e-test-${Date.now()}`;

test.beforeAll(async () => {
test.setTimeout(120_000);

app = await appConfigs.next.appRouter
.clone()
.addFile(
'src/middleware.ts',
() => `import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

export const config = {
matcher: ['/((?!.*\\\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};
`,
)
.addFile(
'src/app/api/custom-jwt/route.ts',
() => `import { headers } from 'next/headers';
import { auth } from '@clerk/nextjs/server';

export async function GET() {
const headersList = await headers();
const templateName = headersList.get('x-jwt-template');
const skipCache = headersList.get('x-skip-cache') === 'true';
const { getToken, userId, sessionId } = await auth();
const customToken = await getToken({ template: templateName, skipCache });
return Response.json({
userId,
sessionId,
customToken,
});
}`,
)
.commit();

await app.setup();
await app.withEnv(appConfigs.envs.withEmailCodes);
await app.dev();

const m = createTestUtils({ app });
fakeUser = m.services.users.createFakeUser();
await m.services.users.createBapiUser(fakeUser);

const template = await m.services.clerk.jwtTemplates.create({
name: jwtTemplateName,
claims: { test_claim: 'hello_from_e2e' },
lifetime: 60,
});
jwtTemplateId = template.id;
});

test.afterAll(async () => {
const m = createTestUtils({ app });
if (jwtTemplateId) {
await m.services.clerk.jwtTemplates.delete(jwtTemplateId);
}
await fakeUser.deleteIfExists();
await app.teardown();
});

test('getToken with skipCache returns a fresh custom JWT token on each call', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

const fetchToken = (skipCache: boolean) =>
page.request.get(`${app.serverUrl}/api/custom-jwt`, {
headers: {
'x-jwt-template': jwtTemplateName,
'x-skip-cache': String(skipCache),
},
});

// Without skipCache
const res1 = await fetchToken(false);
expect(res1.status()).toBe(200);
const body1 = await res1.json();
expect(body1.userId).toBeTruthy();
expect(body1.sessionId).toBeTruthy();
expect(body1.customToken).toBeTruthy();

const payload1 = JSON.parse(atob(body1.customToken.split('.')[1]));
expect(payload1.test_claim).toBe('hello_from_e2e');

// With skipCache — should return a valid, freshly issued token
const res2 = await fetchToken(true);
expect(res2.status()).toBe(200);
const body2 = await res2.json();
expect(body2.userId).toBeTruthy();
expect(body2.sessionId).toBeTruthy();
expect(body2.customToken).toBeTruthy();

const payload2 = JSON.parse(atob(body2.customToken.split('.')[1]));
expect(payload2.test_claim).toBe('hello_from_e2e');
expect(payload2.iat).toBeGreaterThanOrEqual(payload1.iat);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a strong enough assertion to know that we got a freshly minted token with skipCache?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved, but defer to jacek

});
});
Loading