-
Notifications
You must be signed in to change notification settings - Fork 380
chore(backend,nextjs): [WIP] auth helper improvements and integration tests #6163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 29 commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
69c4655
chore: add api key creation and revokation methods
wobsoriano 792e7a0
chore: add initial api key e2e test with nextjs
wobsoriano d5afc7d
chore: add api keys service and fix misplaced token options
wobsoriano cbd280d
chore: simplify tests and handle revoked keys
wobsoriano 47ce019
chore: remove revoke test forn ow
wobsoriano d082afe
chore: clean up
wobsoriano 5fd1350
chore: remove test script
wobsoriano e327d62
Merge branch 'main' into rob/machine-auth-e2e
wobsoriano 6b025d1
chore: test description updates
wobsoriano 7cef4c4
chore: fix tests
wobsoriano 959f624
chore: handle session token case
wobsoriano ec89710
test
wobsoriano 73d92a1
chore: use api key testing with playwright
wobsoriano aea9fd8
chore: include api key listing
wobsoriano 9693093
chore: log created key
wobsoriano 480d1fb
chore: rerun
wobsoriano ca12300
chore: rerun
wobsoriano 717d557
debug
wobsoriano b7250e5
debug
wobsoriano 5ef2925
debug
wobsoriano bae7cd2
debug
wobsoriano 2089e4e
debug
wobsoriano a0ed9c2
debug
wobsoriano 15e83f4
debug
wobsoriano 746d52e
debug
wobsoriano 5a1b77a
debug
wobsoriano d9cd5a9
debug
wobsoriano 310c4e2
debug
wobsoriano b06204b
clean up
wobsoriano 760435e
debug
wobsoriano 5dc91e5
test middleware
wobsoriano d3f6c05
revert
wobsoriano 2fbbec4
chore: remove test prefixes
wobsoriano 06f65fb
chore: rerun test
wobsoriano 6ad36ee
chore: rerun test
wobsoriano 2106816
chore: debug middleware
wobsoriano ed8b707
retry test
wobsoriano 0957b5d
retry test
wobsoriano 7087882
retry test
wobsoriano e8be4b3
retry test
wobsoriano fd3e62b
retry test
wobsoriano b4b53af
retry test
wobsoriano 8daddc0
retry test
wobsoriano ac5022c
retry test
wobsoriano b257a0a
retry test
wobsoriano 236fe8c
retry test
wobsoriano 0451370
retry test
wobsoriano 92a085e
retry test
wobsoriano f61cdc9
retry test
wobsoriano aeac516
retry test
wobsoriano File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,5 +54,9 @@ | |
"with-whatsapp-phone-code": { | ||
"pk": "", | ||
"sk": "" | ||
}, | ||
"with-api-keys": { | ||
"pk": "", | ||
"sk": "" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
integration/templates/next-app-router/src/app/api/machine/route.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { NextResponse } from 'next/server'; | ||
import { auth } from '@clerk/nextjs/server'; | ||
|
||
export async function GET() { | ||
const { userId } = await auth({ acceptsToken: 'api_key' }); | ||
|
||
if (!userId) { | ||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); | ||
} | ||
|
||
return NextResponse.json({ userId }); | ||
} | ||
|
||
export async function POST() { | ||
const authObject = await auth({ acceptsToken: ['api_key', 'session_token'] }); | ||
|
||
if (!authObject.isAuthenticated) { | ||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); | ||
} | ||
|
||
return NextResponse.json({ userId: authObject.userId }); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { type User } from '@clerk/backend'; | ||
import { expect, test } from '@playwright/test'; | ||
|
||
import { appConfigs } from '../../presets'; | ||
import type { FakeAPIKey, FakeUser } from '../../testUtils'; | ||
import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; | ||
|
||
testAgainstRunningApps({ withEnv: [appConfigs.envs.withAPIKeys] })('auth() with API keys @xnextjs', ({ app }) => { | ||
test.describe.configure({ mode: 'parallel' }); | ||
|
||
let fakeUser: FakeUser; | ||
let fakeBapiUser: User; | ||
let fakeAPIKey: FakeAPIKey; | ||
const u = createTestUtils({ app }); | ||
|
||
test.beforeAll(async () => { | ||
fakeUser = u.services.users.createFakeUser(); | ||
fakeBapiUser = await u.services.users.createBapiUser(fakeUser); | ||
fakeAPIKey = await u.services.users.createFakeAPIKey(fakeBapiUser.id); | ||
}); | ||
|
||
test.afterAll(async () => { | ||
await fakeAPIKey.revoke(); | ||
await fakeUser.deleteIfExists(); | ||
await app.teardown(); | ||
}); | ||
|
||
test('should validate API key', async () => { | ||
const url = new URL('/api/machine', app.serverUrl); | ||
|
||
// No API key provided | ||
const noKeyRes = await fetch(url); | ||
expect(noKeyRes.status).toBe(401); | ||
|
||
// Invalid API key | ||
const invalidKeyRes = await fetch(url, { | ||
headers: { | ||
Authorization: 'Bearer invalid_key', | ||
}, | ||
}); | ||
expect(invalidKeyRes.status).toBe(401); | ||
|
||
// Valid API key | ||
const validKeyRes = await fetch(url, { | ||
headers: { | ||
Authorization: `Bearer ${fakeAPIKey.secret}`, | ||
}, | ||
}); | ||
const apiKeyData = await validKeyRes.json(); | ||
expect(validKeyRes.status).toBe(200); | ||
expect(apiKeyData.userId).toBe(fakeBapiUser.id); | ||
}); | ||
|
||
test('should handle multiple token types', async ({ page, context }) => { | ||
const u = createTestUtils({ app, page, context }); | ||
const url = new URL('/api/machine', app.serverUrl); | ||
|
||
// Sign in to get a session token | ||
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(); | ||
|
||
// GET endpoint (only accepts api_key) | ||
const getRes = await u.page.request.get(url.toString()); | ||
expect(getRes.status()).toBe(401); | ||
|
||
// POST endpoint (accepts both api_key and session_token) | ||
// Test with session token | ||
const postWithSessionRes = await u.page.request.post(url.toString()); | ||
const sessionData = await postWithSessionRes.json(); | ||
expect(postWithSessionRes.status()).toBe(200); | ||
expect(sessionData.userId).toBe(fakeBapiUser.id); | ||
|
||
// Test with API key | ||
const postWithApiKeyRes = await fetch(url, { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `Bearer ${fakeAPIKey.secret}`, | ||
}, | ||
}); | ||
const apiKeyData = await postWithApiKeyRes.json(); | ||
expect(postWithApiKeyRes.status).toBe(200); | ||
expect(apiKeyData.userId).toBe(fakeBapiUser.id); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import type { User } from '@clerk/backend'; | ||
import { expect, test } from '@playwright/test'; | ||
|
||
import type { Application } from '../../models/application'; | ||
import { appConfigs } from '../../presets'; | ||
import type { FakeAPIKey, FakeUser } from '../../testUtils'; | ||
import { createTestUtils } from '../../testUtils'; | ||
|
||
test.describe('auth.protect() with API keys @nextjs', () => { | ||
test.describe.configure({ mode: 'parallel' }); | ||
let app: Application; | ||
let fakeUser: FakeUser; | ||
let fakeBapiUser: User; | ||
let fakeAPIKey: FakeAPIKey; | ||
|
||
test.beforeAll(async () => { | ||
app = await appConfigs.next.appRouter | ||
.clone() | ||
.addFile( | ||
'src/app/api/machine/route.ts', | ||
() => ` | ||
import { NextResponse } from 'next/server'; | ||
import { auth } from '@clerk/nextjs/server'; | ||
|
||
export async function GET() { | ||
const { userId } = await auth.protect({ token: 'api_key' }); | ||
return NextResponse.json({ userId }); | ||
} | ||
|
||
export async function POST() { | ||
const { userId } = await auth.protect({ token: ['api_key', 'session_token'] }); | ||
return NextResponse.json({ userId }); | ||
} | ||
`, | ||
) | ||
.commit(); | ||
|
||
await app.setup(); | ||
await app.withEnv(appConfigs.envs.withAPIKeys); | ||
await app.dev(); | ||
|
||
const u = createTestUtils({ app }); | ||
fakeUser = u.services.users.createFakeUser(); | ||
fakeBapiUser = await u.services.users.createBapiUser(fakeUser); | ||
fakeAPIKey = await u.services.users.createFakeAPIKey(fakeBapiUser.id); | ||
}); | ||
|
||
test.afterAll(async () => { | ||
await fakeAPIKey.revoke(); | ||
await fakeUser.deleteIfExists(); | ||
await app.teardown(); | ||
}); | ||
|
||
test('should validate API key', async () => { | ||
const url = new URL('/api/machine', app.serverUrl); | ||
|
||
// No API key provided | ||
const noKeyRes = await fetch(url); | ||
if (noKeyRes.status !== 401) { | ||
console.log('Unexpected status for "noKeyRes". Status:', noKeyRes.status, noKeyRes.statusText); | ||
const body = await noKeyRes.text(); | ||
console.log(`error body ${body} error body`); | ||
} | ||
expect(noKeyRes.status).toBe(401); | ||
|
||
// Invalid API key | ||
const invalidKeyRes = await fetch(url, { | ||
headers: { | ||
Authorization: 'Bearer invalid_key', | ||
}, | ||
}); | ||
expect(invalidKeyRes.status).toBe(401); | ||
|
||
// Valid API key | ||
const validKeyRes = await fetch(url, { | ||
headers: { | ||
Authorization: `Bearer ${fakeAPIKey.secret}`, | ||
}, | ||
}); | ||
const apiKeyData = await validKeyRes.json(); | ||
expect(validKeyRes.status).toBe(200); | ||
expect(apiKeyData.userId).toBe(fakeBapiUser.id); | ||
}); | ||
|
||
test('should handle multiple token types', async ({ page, context }) => { | ||
const u = createTestUtils({ app, page, context }); | ||
const url = new URL('/api/machine', app.serverUrl); | ||
|
||
// Sign in to get a session token | ||
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(); | ||
|
||
// GET endpoint (only accepts api_key) | ||
const getRes = await u.page.request.get(url.toString()); | ||
expect(getRes.status()).toBe(401); | ||
|
||
// POST endpoint (accepts both api_key and session_token) | ||
// Test with session token | ||
const postWithSessionRes = await u.page.request.post(url.toString()); | ||
const sessionData = await postWithSessionRes.json(); | ||
expect(postWithSessionRes.status()).toBe(200); | ||
expect(sessionData.userId).toBe(fakeBapiUser.id); | ||
|
||
// Test with API key | ||
const postWithApiKeyRes = await fetch(url, { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `Bearer ${fakeAPIKey.secret}`, | ||
}, | ||
}); | ||
const apiKeyData = await postWithApiKeyRes.json(); | ||
expect(postWithApiKeyRes.status).toBe(200); | ||
expect(apiKeyData.userId).toBe(fakeBapiUser.id); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.