Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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.
35 changes: 35 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,41 @@ 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();

// Capture the subject parameter
let capturedSubject: string | null = null;
await u.page.route('**/api_keys*', async route => {
const url = new URL(route.request().url());
capturedSubject = url.searchParams.get('subject');
await route.continue();
});

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

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

await u.page.unrouteAll();
});

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
Loading