Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 6 additions & 0 deletions .changeset/friendly-adults-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@clerk/shared": patch
"@clerk/testing": patch
---

Fixed an issue where API keys in `<UserProfile />` are showing organization API keys.
37 changes: 37 additions & 0 deletions integration/tests/machine-auth/component.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,43 @@ testAgainstRunningApps({
await u.page.unrouteAll();
});

test('UserProfile API keys uses user ID as subject even when organization is active', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

const admin = await u.services.users.getUser({ email: fakeAdmin.email });
expect(admin).toBeDefined();
const userId = admin.id;

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

await u.po.organizationSwitcher.goTo();
await u.po.organizationSwitcher.waitForMounted();
await u.po.organizationSwitcher.waitForAnOrganizationToSelected();

let capturedSubject: string | null = null;
const apiKeyRequestPromise = u.page.waitForRequest(request => {
if (request.url().includes('api_keys')) {
const url = new URL(request.url());
capturedSubject = url.searchParams.get('subject');
return true;
}
return false;
});

await u.po.page.goToRelative('/user');
await u.po.userProfile.waitForMounted();
await u.po.userProfile.switchToAPIKeysTab();

await apiKeyRequestPromise;

// Verify the subject parameter is the user ID, not the organization ID
expect(capturedSubject).toBe(userId);
expect(capturedSubject).not.toBe(fakeOrganization.organization.id);
});

test('standalone API keys component in user context based on user_api_keys_enabled', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

Expand Down
4 changes: 3 additions & 1 deletion packages/shared/src/react/hooks/useAPIKeys.rq.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ export function useAPIKeys<T extends UseAPIKeysParams>(params?: T): UseAPIKeysRe
const isEnabled = (safeValues.enabled ?? true) && clerk.loaded;

return usePagesOrInfinite({
fetcher: clerk.apiKeys?.getAll ? (params: GetAPIKeysParams) => clerk.apiKeys.getAll(params) : undefined,
fetcher: clerk.apiKeys?.getAll
? (params: GetAPIKeysParams) => clerk.apiKeys.getAll({ ...params, subject: safeValues.subject })
: undefined,
config: {
keepPreviousData: safeValues.keepPreviousData,
infinite: safeValues.infinite,
Expand Down
4 changes: 3 additions & 1 deletion packages/shared/src/react/hooks/useAPIKeys.swr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ export function useAPIKeys<T extends UseAPIKeysParams>(params?: T): UseAPIKeysRe
const isEnabled = (safeValues.enabled ?? true) && clerk.loaded;

const result = usePagesOrInfinite({
fetcher: clerk.apiKeys?.getAll ? (params: GetAPIKeysParams) => clerk.apiKeys.getAll(params) : undefined,
fetcher: clerk.apiKeys?.getAll
? (params: GetAPIKeysParams) => clerk.apiKeys.getAll({ ...params, subject: safeValues.subject })
: undefined,
config: {
keepPreviousData: safeValues.keepPreviousData,
infinite: safeValues.infinite,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export const createUserProfileComponentPageObject = (testArgs: { page: EnhancedP
switchToBillingTab: async () => {
await page.getByText(/Billing/i).click();
},
switchToAPIKeysTab: async () => {
await page.getByText(/API keys/i).click();
},
waitForMounted: () => {
return page.waitForSelector('.cl-userProfile-root', { state: 'attached' });
},
Expand Down