From 5364fe8ff29eef444369a56beb18e7827f1dd33a Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 7 Nov 2025 20:06:20 -0800 Subject: [PATCH 01/19] chore: Use new API keys environment settings --- packages/clerk-js/src/core/clerk.ts | 34 ++++++++++--------- .../src/core/resources/APIKeySettings.ts | 16 +++++++++ .../OrganizationProfileRoutes.tsx | 2 +- .../UserProfile/UserProfileRoutes.tsx | 2 +- .../src/ui/utils/createCustomPages.tsx | 8 +++-- .../clerk-js/src/utils/componentGuards.ts | 29 +++++++++++----- packages/shared/src/types/apiKeysSettings.ts | 8 +++++ 7 files changed, 69 insertions(+), 30 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 72213936304..6baac0ea7b1 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -102,14 +102,14 @@ import type { MountComponentRenderer } from '../ui/Components'; import { ALLOWED_PROTOCOLS, buildURL, - canViewOrManageAPIKeys, completeSignUpFlow, createAllowedRedirectOrigins, createBeforeUnloadTracker, createPageLifecycle, disabledAllBillingFeatures, - disabledAPIKeysFeature, + disabledOrganizationAPIKeysStandaloneFeature, disabledOrganizationsFeature, + disabledUserAPIKeysStandaloneFeature, errorThrower, generateSignatureWithBase, generateSignatureWithCoinbaseWallet, @@ -1233,22 +1233,24 @@ export class Clerk implements ClerkInterface { logger.warnOnce('Clerk: component is in early access and not yet recommended for production use.'); - if (disabledAPIKeysFeature(this, this.environment)) { - if (this.#instanceType === 'development') { - throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { - code: CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE, - }); + if (this.organization) { + if (disabledOrganizationAPIKeysStandaloneFeature(this, this.environment)) { + if (this.#instanceType === 'development') { + throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, { + code: CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE, + }); + } + return; } - return; - } - - if (this.organization && !canViewOrManageAPIKeys(this)) { - if (this.#instanceType === 'development') { - throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, { - code: CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE, - }); + } else { + if (disabledUserAPIKeysStandaloneFeature(this, this.environment)) { + if (this.#instanceType === 'development') { + throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { + code: CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE, + }); + } + return; } - return; } void this.#componentControls.ensureMounted({ preloadHint: 'APIKeys' }).then(controls => diff --git a/packages/clerk-js/src/core/resources/APIKeySettings.ts b/packages/clerk-js/src/core/resources/APIKeySettings.ts index 59997864544..68ccb7584ca 100644 --- a/packages/clerk-js/src/core/resources/APIKeySettings.ts +++ b/packages/clerk-js/src/core/resources/APIKeySettings.ts @@ -6,10 +6,18 @@ import { BaseResource } from './internal'; * @internal */ export class APIKeySettings extends BaseResource implements APIKeysSettingsResource { + /** + * @deprecated + */ enabled: boolean = false; + user_api_keys_enabled: boolean = false; + show_in_user_profile: boolean = false; + orgs_api_keys_enabled: boolean = false; + show_in_org_profile: boolean = false; public constructor(data: APIKeysSettingsJSON | APIKeysSettingsJSONSnapshot | null = null) { super(); + this.fromJSON(data); } @@ -19,6 +27,10 @@ export class APIKeySettings extends BaseResource implements APIKeysSettingsResou } this.enabled = this.withDefault(data.enabled, false); + this.user_api_keys_enabled = this.withDefault(data.user_api_keys_enabled, false); + this.show_in_user_profile = this.withDefault(data.show_in_user_profile, false); + this.orgs_api_keys_enabled = this.withDefault(data.orgs_api_keys_enabled, false); + this.show_in_org_profile = this.withDefault(data.show_in_org_profile, false); return this; } @@ -26,6 +38,10 @@ export class APIKeySettings extends BaseResource implements APIKeysSettingsResou public __internal_toSnapshot(): APIKeysSettingsJSONSnapshot { return { enabled: this.enabled, + user_api_keys_enabled: this.user_api_keys_enabled, + show_in_user_profile: this.show_in_user_profile, + orgs_api_keys_enabled: this.orgs_api_keys_enabled, + show_in_org_profile: this.show_in_org_profile, } as APIKeysSettingsJSONSnapshot; } } diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx index 62f5a72dfc0..a44d2daaab4 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -117,7 +117,7 @@ export const OrganizationProfileRoutes = () => { ) : null} - {apiKeysSettings.enabled && ( + {apiKeysSettings.orgs_api_keys_enabled && apiKeysSettings.show_in_org_profile && ( has({ permission: 'org:sys_api_keys:read' }) || has({ permission: 'org:sys_api_keys:manage' }) diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx index b137da3d65c..79f849a4b14 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx @@ -108,7 +108,7 @@ export const UserProfileRoutes = () => { ) : null} - {apiKeysSettings.enabled && ( + {apiKeysSettings.user_api_keys_enabled && apiKeysSettings.show_in_user_profile && ( diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index edb3c58f558..05ced359a16 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -1,9 +1,9 @@ import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/shared/types'; import { - canViewOrManageAPIKeys, - disabledAPIKeysFeature, + disabledOrganizationAPIKeysFeature, disabledOrganizationBillingFeature, + disabledUserAPIKeysFeature, disabledUserBillingFeature, isValidUrl, } from '../../utils'; @@ -104,7 +104,9 @@ const createCustomPages = ( commerce: organization ? !disabledOrganizationBillingFeature(clerk, environment) && shouldShowBilling : !disabledUserBillingFeature(clerk, environment) && shouldShowBilling, - apiKeys: !disabledAPIKeysFeature(clerk, environment) && (organization ? canViewOrManageAPIKeys(clerk) : true), + apiKeys: organization + ? !disabledOrganizationAPIKeysFeature(clerk, environment) + : !disabledUserAPIKeysFeature(clerk, environment), }); if (isDevelopmentSDK(clerk)) { diff --git a/packages/clerk-js/src/utils/componentGuards.ts b/packages/clerk-js/src/utils/componentGuards.ts index e5f6a6fce74..bb021364563 100644 --- a/packages/clerk-js/src/utils/componentGuards.ts +++ b/packages/clerk-js/src/utils/componentGuards.ts @@ -38,13 +38,24 @@ export const disabledAPIKeysFeature: ComponentGuard = (_, environment) => { return !environment?.apiKeysSettings?.enabled; }; -export const canViewOrManageAPIKeys: ComponentGuard = clerk => { - if (!clerk.session) { - return false; - } - - return ( - clerk.session.checkAuthorization({ permission: 'org:sys_api_keys:read' }) || - clerk.session.checkAuthorization({ permission: 'org:sys_api_keys:manage' }) - ); +export const disabledUserAPIKeysFeature: ComponentGuard = (_, environment) => { + return !environment?.apiKeysSettings?.user_api_keys_enabled || !environment?.apiKeysSettings?.show_in_user_profile; +}; + +export const disabledOrganizationAPIKeysFeature: ComponentGuard = (_, environment) => { + return !environment?.apiKeysSettings?.orgs_api_keys_enabled || !environment?.apiKeysSettings?.show_in_org_profile; +}; + +/** + * For standalone component - only checks if user API keys are enabled (not show_in_profile) + */ +export const disabledUserAPIKeysStandaloneFeature: ComponentGuard = (_, environment) => { + return !environment?.apiKeysSettings?.user_api_keys_enabled; +}; + +/** + * For standalone component - checks if org API keys are enabled (not show_in_profile) + */ +export const disabledOrganizationAPIKeysStandaloneFeature: ComponentGuard = (_, environment) => { + return !environment?.apiKeysSettings?.orgs_api_keys_enabled; }; diff --git a/packages/shared/src/types/apiKeysSettings.ts b/packages/shared/src/types/apiKeysSettings.ts index f5871c1ed2d..7741b1bf78b 100644 --- a/packages/shared/src/types/apiKeysSettings.ts +++ b/packages/shared/src/types/apiKeysSettings.ts @@ -4,10 +4,18 @@ import type { APIKeysSettingsJSONSnapshot } from './snapshots'; export interface APIKeysSettingsJSON extends ClerkResourceJSON { enabled: boolean; + user_api_keys_enabled: boolean; + show_in_user_profile: boolean; + orgs_api_keys_enabled: boolean; + show_in_org_profile: boolean; } export interface APIKeysSettingsResource extends ClerkResource { enabled: boolean; + user_api_keys_enabled: boolean; + show_in_user_profile: boolean; + orgs_api_keys_enabled: boolean; + show_in_org_profile: boolean; __internal_toSnapshot: () => APIKeysSettingsJSONSnapshot; } From f475e0c8e1651a200b56672b4782a7f5ccffc196 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 9 Nov 2025 05:24:07 -0800 Subject: [PATCH 02/19] chore: better component guard naming --- packages/clerk-js/src/core/clerk.ts | 8 ++++---- .../src/ui/utils/createCustomPages.tsx | 8 ++++---- packages/clerk-js/src/utils/componentGuards.ts | 18 ++++++------------ 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 6baac0ea7b1..f1938418b9c 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -107,9 +107,9 @@ import { createBeforeUnloadTracker, createPageLifecycle, disabledAllBillingFeatures, - disabledOrganizationAPIKeysStandaloneFeature, + disabledOrganizationAPIKeysFeature, disabledOrganizationsFeature, - disabledUserAPIKeysStandaloneFeature, + disabledUserAPIKeysFeature, errorThrower, generateSignatureWithBase, generateSignatureWithCoinbaseWallet, @@ -1234,7 +1234,7 @@ export class Clerk implements ClerkInterface { logger.warnOnce('Clerk: component is in early access and not yet recommended for production use.'); if (this.organization) { - if (disabledOrganizationAPIKeysStandaloneFeature(this, this.environment)) { + if (disabledOrganizationAPIKeysFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, { code: CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE, @@ -1243,7 +1243,7 @@ export class Clerk implements ClerkInterface { return; } } else { - if (disabledUserAPIKeysStandaloneFeature(this, this.environment)) { + if (disabledUserAPIKeysFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { code: CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE, diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index 05ced359a16..f6dfa204ba3 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -1,10 +1,10 @@ import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/shared/types'; import { - disabledOrganizationAPIKeysFeature, disabledOrganizationBillingFeature, - disabledUserAPIKeysFeature, + disabledOrganizationProfileAPIKeysFeature, disabledUserBillingFeature, + disabledUserProfileAPIKeysFeature, isValidUrl, } from '../../utils'; import { ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID, USER_PROFILE_NAVBAR_ROUTE_ID } from '../constants'; @@ -105,8 +105,8 @@ const createCustomPages = ( ? !disabledOrganizationBillingFeature(clerk, environment) && shouldShowBilling : !disabledUserBillingFeature(clerk, environment) && shouldShowBilling, apiKeys: organization - ? !disabledOrganizationAPIKeysFeature(clerk, environment) - : !disabledUserAPIKeysFeature(clerk, environment), + ? !disabledOrganizationProfileAPIKeysFeature(clerk, environment) + : !disabledUserProfileAPIKeysFeature(clerk, environment), }); if (isDevelopmentSDK(clerk)) { diff --git a/packages/clerk-js/src/utils/componentGuards.ts b/packages/clerk-js/src/utils/componentGuards.ts index bb021364563..409a53b77d8 100644 --- a/packages/clerk-js/src/utils/componentGuards.ts +++ b/packages/clerk-js/src/utils/componentGuards.ts @@ -39,23 +39,17 @@ export const disabledAPIKeysFeature: ComponentGuard = (_, environment) => { }; export const disabledUserAPIKeysFeature: ComponentGuard = (_, environment) => { - return !environment?.apiKeysSettings?.user_api_keys_enabled || !environment?.apiKeysSettings?.show_in_user_profile; + return !environment?.apiKeysSettings?.user_api_keys_enabled; }; export const disabledOrganizationAPIKeysFeature: ComponentGuard = (_, environment) => { - return !environment?.apiKeysSettings?.orgs_api_keys_enabled || !environment?.apiKeysSettings?.show_in_org_profile; + return !environment?.apiKeysSettings?.orgs_api_keys_enabled; }; -/** - * For standalone component - only checks if user API keys are enabled (not show_in_profile) - */ -export const disabledUserAPIKeysStandaloneFeature: ComponentGuard = (_, environment) => { - return !environment?.apiKeysSettings?.user_api_keys_enabled; +export const disabledUserProfileAPIKeysFeature: ComponentGuard = (_, environment) => { + return !environment?.apiKeysSettings?.user_api_keys_enabled || !environment?.apiKeysSettings?.show_in_user_profile; }; -/** - * For standalone component - checks if org API keys are enabled (not show_in_profile) - */ -export const disabledOrganizationAPIKeysStandaloneFeature: ComponentGuard = (_, environment) => { - return !environment?.apiKeysSettings?.orgs_api_keys_enabled; +export const disabledOrganizationProfileAPIKeysFeature: ComponentGuard = (_, environment) => { + return !environment?.apiKeysSettings?.orgs_api_keys_enabled || !environment?.apiKeysSettings?.show_in_org_profile; }; From 402d1f94fffa213f6c7afae7343318c84efc0654 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 9 Nov 2025 05:37:48 -0800 Subject: [PATCH 03/19] chore: clean up component guards --- packages/clerk-js/src/core/clerk.ts | 41 +++++++++++-------- .../clerk-js/src/utils/componentGuards.ts | 8 ++-- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index f1938418b9c..174ac376198 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -106,6 +106,7 @@ import { createAllowedRedirectOrigins, createBeforeUnloadTracker, createPageLifecycle, + disabledAllAPIKeysFeatures, disabledAllBillingFeatures, disabledOrganizationAPIKeysFeature, disabledOrganizationsFeature, @@ -179,6 +180,7 @@ const CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE = 'cannot_render_organizat const CANNOT_RENDER_ORGANIZATION_MISSING_ERROR_CODE = 'cannot_render_organization_missing'; const CANNOT_RENDER_SINGLE_SESSION_ENABLED_ERROR_CODE = 'cannot_render_single_session_enabled'; const CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE = 'cannot_render_api_keys_disabled'; +const CANNOT_RENDER_API_KEYS_USER_UNAUTHORIZED_ERROR_CODE = 'cannot_render_api_keys_user_unauthorized'; const CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE = 'cannot_render_api_keys_org_unauthorized'; const defaultOptions: ClerkOptions = { polling: true, @@ -1233,24 +1235,31 @@ export class Clerk implements ClerkInterface { logger.warnOnce('Clerk: component is in early access and not yet recommended for production use.'); - if (this.organization) { - if (disabledOrganizationAPIKeysFeature(this, this.environment)) { - if (this.#instanceType === 'development') { - throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, { - code: CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE, - }); - } - return; + if (disabledAllAPIKeysFeatures(this, this.environment)) { + if (this.#instanceType === 'development') { + throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { + code: CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE, + }); } - } else { - if (disabledUserAPIKeysFeature(this, this.environment)) { - if (this.#instanceType === 'development') { - throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { - code: CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE, - }); - } - return; + return; + } + + if (this.organization && disabledOrganizationAPIKeysFeature(this, this.environment)) { + if (this.#instanceType === 'development') { + throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, { + code: CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE, + }); + } + return; + } + + if (disabledUserAPIKeysFeature(this, this.environment)) { + if (this.#instanceType === 'development') { + throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { + code: CANNOT_RENDER_API_KEYS_USER_UNAUTHORIZED_ERROR_CODE, + }); } + return; } void this.#componentControls.ensureMounted({ preloadHint: 'APIKeys' }).then(controls => diff --git a/packages/clerk-js/src/utils/componentGuards.ts b/packages/clerk-js/src/utils/componentGuards.ts index 409a53b77d8..b1063e9e992 100644 --- a/packages/clerk-js/src/utils/componentGuards.ts +++ b/packages/clerk-js/src/utils/componentGuards.ts @@ -34,10 +34,6 @@ export const disabledAllBillingFeatures: ComponentGuard = (_, environment) => { return disabledUserBillingFeature(_, environment) && disabledOrganizationBillingFeature(_, environment); }; -export const disabledAPIKeysFeature: ComponentGuard = (_, environment) => { - return !environment?.apiKeysSettings?.enabled; -}; - export const disabledUserAPIKeysFeature: ComponentGuard = (_, environment) => { return !environment?.apiKeysSettings?.user_api_keys_enabled; }; @@ -46,6 +42,10 @@ export const disabledOrganizationAPIKeysFeature: ComponentGuard = (_, environmen return !environment?.apiKeysSettings?.orgs_api_keys_enabled; }; +export const disabledAllAPIKeysFeatures: ComponentGuard = (_, environment) => { + return disabledUserAPIKeysFeature(_, environment) && disabledOrganizationAPIKeysFeature(_, environment); +}; + export const disabledUserProfileAPIKeysFeature: ComponentGuard = (_, environment) => { return !environment?.apiKeysSettings?.user_api_keys_enabled || !environment?.apiKeysSettings?.show_in_user_profile; }; From 841257d59ffd4f978e9621cc0fa1733b62dea241 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 9 Nov 2025 16:55:07 -0800 Subject: [PATCH 04/19] test: Add granular settings e2e --- .../tests/machine-auth/component.test.ts | 145 +++++++++++++----- 1 file changed, 105 insertions(+), 40 deletions(-) diff --git a/integration/tests/machine-auth/component.test.ts b/integration/tests/machine-auth/component.test.ts index 18be38cd7b8..9422cd5bd13 100644 --- a/integration/tests/machine-auth/component.test.ts +++ b/integration/tests/machine-auth/component.test.ts @@ -1,9 +1,37 @@ +import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; import { appConfigs } from '../../presets'; import type { FakeOrganization, FakeUser } from '../../testUtils'; import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; +const mockAPIKeysEnvironmentSettings = async ( + page: Page, + overrides: Partial<{ + user_api_keys_enabled: boolean; + show_in_user_profile: boolean; + orgs_api_keys_enabled: boolean; + show_in_org_profile: boolean; + }>, +) => { + await page.route('*/**/v1/environment*', async route => { + const response = await route.fetch(); + const json = await response.json(); + const newJson = { + ...json, + api_keys_settings: { + enabled: true, + user_api_keys_enabled: true, + show_in_user_profile: true, + orgs_api_keys_enabled: true, + show_in_org_profile: true, + ...overrides, + }, + }; + await route.fulfill({ response, json: newJson }); + }); +}; + testAgainstRunningApps({ withEnv: [appConfigs.envs.withAPIKeys], withPattern: ['withMachine.next.appRouter'], @@ -214,23 +242,74 @@ testAgainstRunningApps({ expect(clipboardText).toBe(secret); }); - test('component does not render for orgs when user does not have permissions', async ({ page, context }) => { + test('UserProfile API keys page visibility', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); - const fakeMember = u.services.users.createFakeUser(); - const member = await u.services.users.createBapiUser(fakeMember); + 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.services.clerk.organizations.createOrganizationMembership({ - organizationId: fakeOrganization.organization.id, - role: 'org:member', - userId: member.id, - }); + // user_api_keys_enabled: false should hide API keys page + await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: false }); + await u.po.page.goToRelative('/user'); + await u.po.userProfile.waitForMounted(); + await u.po.page.goToRelative('/user#/api-keys'); + await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); + + // show_in_user_profile: false should hide API keys page + await mockAPIKeysEnvironmentSettings(u.page, { show_in_user_profile: false }); + await page.reload(); + await u.po.userProfile.waitForMounted(); + await u.po.page.goToRelative('/user#/api-keys'); + await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); + + // Both enabled should show API keys page + await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: true, show_in_user_profile: true }); + await page.reload(); + await u.po.userProfile.waitForMounted(); + await u.po.page.goToRelative('/user#/api-keys'); + await expect(u.page.locator('.cl-apiKeys')).toBeVisible({ timeout: 5000 }); + }); + + test('OrganizationProfile API keys page visibility', 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: fakeMember.email, password: fakeMember.password }); + await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeAdmin.email, password: fakeAdmin.password }); await u.po.expect.toBeSignedIn(); + // orgs_api_keys_enabled: false should hide API keys page + await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: false }); + await u.po.page.goToRelative('/organization-profile'); + await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); + await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); + + // show_in_org_profile: false should hide API keys page + await mockAPIKeysEnvironmentSettings(u.page, { show_in_org_profile: false }); + await page.reload(); + await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); + await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); + + // Both enabled should show API keys page + await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: true, show_in_org_profile: true }); + await page.reload(); + await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); + await expect(u.page.locator('.cl-apiKeys')).toBeVisible({ timeout: 5000 }); + }); + + test('standalone API keys component in user context based on user_api_keys_enabled', 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: fakeAdmin.email, password: fakeAdmin.password }); + await u.po.expect.toBeSignedIn(); + + // user_api_keys_enabled: false should prevent standalone component from rendering + await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: false }); + let apiKeysRequestWasMade = false; u.page.on('request', request => { if (request.url().includes('/api_keys')) { @@ -238,36 +317,28 @@ testAgainstRunningApps({ } }); - // Check that standalone component is not rendered await u.po.page.goToRelative('/api-keys'); await expect(u.page.locator('.cl-apiKeys-root')).toBeHidden({ timeout: 1000 }); - - // Check that page is not rendered in OrganizationProfile - await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); - await expect(u.page.locator('.cl-apiKeys-root')).toBeHidden({ timeout: 1000 }); - expect(apiKeysRequestWasMade).toBe(false); - await fakeMember.deleteIfExists(); + // user_api_keys_enabled: true should allow standalone component to render + await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: true }); + await page.reload(); + await u.po.apiKeys.waitForMounted(); + await expect(u.page.locator('.cl-apiKeys-root')).toBeVisible(); }); - test('user with read permission can view API keys but not manage them', async ({ page, context }) => { + test('standalone API keys component in org context based on orgs_api_keys_enabled', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); - const fakeViewer = u.services.users.createFakeUser(); - const viewer = await u.services.users.createBapiUser(fakeViewer); - - await u.services.clerk.organizations.createOrganizationMembership({ - organizationId: fakeOrganization.organization.id, - role: 'org:viewer', - userId: viewer.id, - }); - await u.po.signIn.goTo(); await u.po.signIn.waitForMounted(); - await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeViewer.email, password: fakeViewer.password }); + await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeAdmin.email, password: fakeAdmin.password }); await u.po.expect.toBeSignedIn(); + // orgs_api_keys_enabled: false should prevent standalone component from rendering in org context + await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: false }); + let apiKeysRequestWasMade = false; u.page.on('request', request => { if (request.url().includes('/api_keys')) { @@ -275,20 +346,14 @@ testAgainstRunningApps({ } }); - // Check that standalone component is rendered and user can read API keys await u.po.page.goToRelative('/api-keys'); - await u.po.apiKeys.waitForMounted(); - await expect(u.page.getByRole('button', { name: /Add new key/i })).toBeHidden(); - await expect(u.page.getByRole('columnheader', { name: /Actions/i })).toBeHidden(); - - // Check that page is rendered in OrganizationProfile and user can read API keys - await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); - await expect(u.page.locator('.cl-apiKeys')).toBeVisible(); - await expect(u.page.getByRole('button', { name: /Add new key/i })).toBeHidden(); - await expect(u.page.getByRole('columnheader', { name: /Actions/i })).toBeHidden(); - - expect(apiKeysRequestWasMade).toBe(true); + await expect(u.page.locator('.cl-apiKeys-root')).toBeHidden({ timeout: 1000 }); + expect(apiKeysRequestWasMade).toBe(false); - await fakeViewer.deleteIfExists(); + // orgs_api_keys_enabled: true should allow standalone component to render in org context + await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: true }); + await page.reload(); + await u.po.apiKeys.waitForMounted(); + await expect(u.page.locator('.cl-apiKeys-root')).toBeVisible(); }); }); From 3d40d8255515bd70fb25eb30cd023e96383c5fa7 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 9 Nov 2025 17:01:05 -0800 Subject: [PATCH 05/19] chore: use accurate error code --- packages/clerk-js/src/core/clerk.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 174ac376198..31c5a34f73a 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -180,8 +180,8 @@ const CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE = 'cannot_render_organizat const CANNOT_RENDER_ORGANIZATION_MISSING_ERROR_CODE = 'cannot_render_organization_missing'; const CANNOT_RENDER_SINGLE_SESSION_ENABLED_ERROR_CODE = 'cannot_render_single_session_enabled'; const CANNOT_RENDER_API_KEYS_DISABLED_ERROR_CODE = 'cannot_render_api_keys_disabled'; -const CANNOT_RENDER_API_KEYS_USER_UNAUTHORIZED_ERROR_CODE = 'cannot_render_api_keys_user_unauthorized'; -const CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE = 'cannot_render_api_keys_org_unauthorized'; +const CANNOT_RENDER_API_KEYS_USER_DISABLED_ERROR_CODE = 'cannot_render_api_keys_user_disabled'; +const CANNOT_RENDER_API_KEYS_ORG_DISABLED_ERROR_CODE = 'cannot_render_api_keys_org_disabled'; const defaultOptions: ClerkOptions = { polling: true, standardBrowser: true, @@ -1247,7 +1247,7 @@ export class Clerk implements ClerkInterface { if (this.organization && disabledOrganizationAPIKeysFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, { - code: CANNOT_RENDER_API_KEYS_ORG_UNAUTHORIZED_ERROR_CODE, + code: CANNOT_RENDER_API_KEYS_ORG_DISABLED_ERROR_CODE, }); } return; @@ -1256,7 +1256,7 @@ export class Clerk implements ClerkInterface { if (disabledUserAPIKeysFeature(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { - code: CANNOT_RENDER_API_KEYS_USER_UNAUTHORIZED_ERROR_CODE, + code: CANNOT_RENDER_API_KEYS_USER_DISABLED_ERROR_CODE, }); } return; From f2f5eda9f808205cc7c5bf53e33324bcf69267d3 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 9 Nov 2025 17:04:23 -0800 Subject: [PATCH 06/19] chore: use accurate error code --- packages/clerk-js/src/core/clerk.ts | 4 ++-- packages/clerk-js/src/core/warnings.ts | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 31c5a34f73a..b59a646623a 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1246,7 +1246,7 @@ export class Clerk implements ClerkInterface { if (this.organization && disabledOrganizationAPIKeysFeature(this, this.environment)) { if (this.#instanceType === 'development') { - throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenUnauthorized, { + throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForOrgWhenDisabled, { code: CANNOT_RENDER_API_KEYS_ORG_DISABLED_ERROR_CODE, }); } @@ -1255,7 +1255,7 @@ export class Clerk implements ClerkInterface { if (disabledUserAPIKeysFeature(this, this.environment)) { if (this.#instanceType === 'development') { - throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { + throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponentForUserWhenDisabled, { code: CANNOT_RENDER_API_KEYS_USER_DISABLED_ERROR_CODE, }); } diff --git a/packages/clerk-js/src/core/warnings.ts b/packages/clerk-js/src/core/warnings.ts index 1d001112280..ac0621a4160 100644 --- a/packages/clerk-js/src/core/warnings.ts +++ b/packages/clerk-js/src/core/warnings.ts @@ -46,9 +46,11 @@ const warnings = { cannotOpenSignInOrSignUp: 'The SignIn or SignUp modals do not render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, this is no-op.', cannotRenderAPIKeysComponent: - 'The component cannot be rendered when API keys is disabled. Since API keys is disabled, this is no-op.', - cannotRenderAPIKeysComponentForOrgWhenUnauthorized: - 'The component cannot be rendered for an organization unless a user has the required permissions. Since the user does not have the necessary permissions, this is no-op.', + 'The component cannot be rendered when API keys are disabled. Since API keys are disabled, this is no-op.', + cannotRenderAPIKeysComponentForUserWhenDisabled: + 'The component cannot be rendered when user API keys are disabled. Since user API keys are disabled, this is no-op.', + cannotRenderAPIKeysComponentForOrgWhenDisabled: + 'The component cannot be rendered when organization API keys are disabled. Since organization API keys are disabled, this is no-op.', }; type SerializableWarnings = Serializable; From c83dd6ae781b671186d06799e027abee1f077003 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Sun, 9 Nov 2025 17:07:46 -0800 Subject: [PATCH 07/19] chore: add changeset --- .changeset/serious-mugs-flash.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/serious-mugs-flash.md diff --git a/.changeset/serious-mugs-flash.md b/.changeset/serious-mugs-flash.md new file mode 100644 index 00000000000..ae07b87b118 --- /dev/null +++ b/.changeset/serious-mugs-flash.md @@ -0,0 +1,6 @@ +--- +"@clerk/clerk-js": minor +"@clerk/shared": patch +--- + +Added granular API keys settings for user and organization profiles From 0017222e1a5ff50cc1f2d82c58a3ff0183eeb36a Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 04:28:51 -0800 Subject: [PATCH 08/19] chore: remove deprecated enabled option --- packages/clerk-js/src/core/resources/APIKeySettings.ts | 6 ------ packages/shared/src/types/apiKeysSettings.ts | 2 -- 2 files changed, 8 deletions(-) diff --git a/packages/clerk-js/src/core/resources/APIKeySettings.ts b/packages/clerk-js/src/core/resources/APIKeySettings.ts index 68ccb7584ca..88387eb243e 100644 --- a/packages/clerk-js/src/core/resources/APIKeySettings.ts +++ b/packages/clerk-js/src/core/resources/APIKeySettings.ts @@ -6,10 +6,6 @@ import { BaseResource } from './internal'; * @internal */ export class APIKeySettings extends BaseResource implements APIKeysSettingsResource { - /** - * @deprecated - */ - enabled: boolean = false; user_api_keys_enabled: boolean = false; show_in_user_profile: boolean = false; orgs_api_keys_enabled: boolean = false; @@ -26,7 +22,6 @@ export class APIKeySettings extends BaseResource implements APIKeysSettingsResou return this; } - this.enabled = this.withDefault(data.enabled, false); this.user_api_keys_enabled = this.withDefault(data.user_api_keys_enabled, false); this.show_in_user_profile = this.withDefault(data.show_in_user_profile, false); this.orgs_api_keys_enabled = this.withDefault(data.orgs_api_keys_enabled, false); @@ -37,7 +32,6 @@ export class APIKeySettings extends BaseResource implements APIKeysSettingsResou public __internal_toSnapshot(): APIKeysSettingsJSONSnapshot { return { - enabled: this.enabled, user_api_keys_enabled: this.user_api_keys_enabled, show_in_user_profile: this.show_in_user_profile, orgs_api_keys_enabled: this.orgs_api_keys_enabled, diff --git a/packages/shared/src/types/apiKeysSettings.ts b/packages/shared/src/types/apiKeysSettings.ts index 7741b1bf78b..4d7bd41dff6 100644 --- a/packages/shared/src/types/apiKeysSettings.ts +++ b/packages/shared/src/types/apiKeysSettings.ts @@ -3,7 +3,6 @@ import type { ClerkResource } from './resource'; import type { APIKeysSettingsJSONSnapshot } from './snapshots'; export interface APIKeysSettingsJSON extends ClerkResourceJSON { - enabled: boolean; user_api_keys_enabled: boolean; show_in_user_profile: boolean; orgs_api_keys_enabled: boolean; @@ -11,7 +10,6 @@ export interface APIKeysSettingsJSON extends ClerkResourceJSON { } export interface APIKeysSettingsResource extends ClerkResource { - enabled: boolean; user_api_keys_enabled: boolean; show_in_user_profile: boolean; orgs_api_keys_enabled: boolean; From cabbef40b0bd985e5701b9ed053b6445b19ecee7 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 04:42:07 -0800 Subject: [PATCH 09/19] chore: apply suggestions --- .../src/core/resources/APIKeySettings.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/clerk-js/src/core/resources/APIKeySettings.ts b/packages/clerk-js/src/core/resources/APIKeySettings.ts index 88387eb243e..ba3b62930f9 100644 --- a/packages/clerk-js/src/core/resources/APIKeySettings.ts +++ b/packages/clerk-js/src/core/resources/APIKeySettings.ts @@ -6,10 +6,10 @@ import { BaseResource } from './internal'; * @internal */ export class APIKeySettings extends BaseResource implements APIKeysSettingsResource { - user_api_keys_enabled: boolean = false; - show_in_user_profile: boolean = false; - orgs_api_keys_enabled: boolean = false; - show_in_org_profile: boolean = false; + public user_api_keys_enabled: boolean = false; + public show_in_user_profile: boolean = false; + public orgs_api_keys_enabled: boolean = false; + public show_in_org_profile: boolean = false; public constructor(data: APIKeysSettingsJSON | APIKeysSettingsJSONSnapshot | null = null) { super(); @@ -22,10 +22,10 @@ export class APIKeySettings extends BaseResource implements APIKeysSettingsResou return this; } - this.user_api_keys_enabled = this.withDefault(data.user_api_keys_enabled, false); - this.show_in_user_profile = this.withDefault(data.show_in_user_profile, false); - this.orgs_api_keys_enabled = this.withDefault(data.orgs_api_keys_enabled, false); - this.show_in_org_profile = this.withDefault(data.show_in_org_profile, false); + this.user_api_keys_enabled = data.user_api_keys_enabled; + this.show_in_user_profile = data.show_in_user_profile; + this.orgs_api_keys_enabled = data.orgs_api_keys_enabled; + this.show_in_org_profile = data.show_in_org_profile; return this; } From 09a649626e8e2fd2ffe4cf9ba19e5d97fcf9d18b Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Mon, 10 Nov 2025 04:44:06 -0800 Subject: [PATCH 10/19] chore: updated changeset --- .changeset/serious-mugs-flash.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/serious-mugs-flash.md b/.changeset/serious-mugs-flash.md index ae07b87b118..2ba730d7276 100644 --- a/.changeset/serious-mugs-flash.md +++ b/.changeset/serious-mugs-flash.md @@ -1,6 +1,6 @@ --- "@clerk/clerk-js": minor -"@clerk/shared": patch +"@clerk/shared": minor --- -Added granular API keys settings for user and organization profiles +Support granular API keys settings for user and organization profiles From acf15f289511b6a5720ae2e5dec753cb0b61d515 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 04:52:48 -0800 Subject: [PATCH 11/19] chore: fix unit tests --- .../src/core/resources/__tests__/Environment.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts index c1587669887..c042a3716c8 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts @@ -53,7 +53,10 @@ describe('Environment', () => { object: 'environment', id: '', api_keys_settings: { - enabled: false, + orgs_api_keys_enabled: false, + show_in_org_profile: false, + show_in_user_profile: false, + user_api_keys_enabled: false, id: undefined, path_root: '', }, @@ -578,7 +581,10 @@ describe('Environment', () => { expect(environment.__internal_toSnapshot()).toMatchObject({ object: 'environment', api_keys_settings: expect.objectContaining({ - enabled: false, + orgs_api_keys_enabled: false, + show_in_org_profile: false, + show_in_user_profile: false, + user_api_keys_enabled: false, }), auth_config: expect.objectContaining({ single_session_mode: true, From f302cca094a24c41d8a748080536267e994b83d6 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Mon, 10 Nov 2025 04:55:13 -0800 Subject: [PATCH 12/19] chore: remove unused property --- integration/tests/machine-auth/component.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/integration/tests/machine-auth/component.test.ts b/integration/tests/machine-auth/component.test.ts index 9422cd5bd13..2c251392896 100644 --- a/integration/tests/machine-auth/component.test.ts +++ b/integration/tests/machine-auth/component.test.ts @@ -20,7 +20,6 @@ const mockAPIKeysEnvironmentSettings = async ( const newJson = { ...json, api_keys_settings: { - enabled: true, user_api_keys_enabled: true, show_in_user_profile: true, orgs_api_keys_enabled: true, From 24b19d2053a2cf5b2b17608556080e4f764102cb Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 06:03:05 -0800 Subject: [PATCH 13/19] chore: fix unit tests --- .../src/core/resources/__tests__/Environment.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts index c042a3716c8..eb33784c2ef 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts @@ -9,7 +9,10 @@ describe('Environment', () => { expect(environment).toMatchObject({ apiKeysSettings: expect.objectContaining({ - enabled: false, + orgs_api_keys_enabled: false, + show_in_org_profile: false, + show_in_user_profile: false, + user_api_keys_enabled: false, pathRoot: '', }), authConfig: expect.objectContaining({ From 15f2d15119f2701075caef066e6c327462adfdf2 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 11:13:27 -0800 Subject: [PATCH 14/19] chore: remove profile based options --- .../tests/machine-auth/component.test.ts | 39 +++++-------------- .../src/core/resources/APIKeySettings.ts | 6 --- .../resources/__tests__/Environment.test.ts | 6 --- .../OrganizationProfileRoutes.tsx | 13 +++++-- .../UserProfile/UserProfileRoutes.tsx | 4 +- .../src/ui/utils/createCustomPages.tsx | 8 ++-- .../clerk-js/src/utils/componentGuards.ts | 8 ---- packages/shared/src/types/apiKeysSettings.ts | 4 -- packages/shared/src/types/clerk.ts | 18 ++++++++- 9 files changed, 42 insertions(+), 64 deletions(-) diff --git a/integration/tests/machine-auth/component.test.ts b/integration/tests/machine-auth/component.test.ts index 2c251392896..4c0fec6adfd 100644 --- a/integration/tests/machine-auth/component.test.ts +++ b/integration/tests/machine-auth/component.test.ts @@ -9,9 +9,7 @@ const mockAPIKeysEnvironmentSettings = async ( page: Page, overrides: Partial<{ user_api_keys_enabled: boolean; - show_in_user_profile: boolean; orgs_api_keys_enabled: boolean; - show_in_org_profile: boolean; }>, ) => { await page.route('*/**/v1/environment*', async route => { @@ -21,9 +19,7 @@ const mockAPIKeysEnvironmentSettings = async ( ...json, api_keys_settings: { user_api_keys_enabled: true, - show_in_user_profile: true, orgs_api_keys_enabled: true, - show_in_org_profile: true, ...overrides, }, }; @@ -256,15 +252,8 @@ testAgainstRunningApps({ await u.po.page.goToRelative('/user#/api-keys'); await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); - // show_in_user_profile: false should hide API keys page - await mockAPIKeysEnvironmentSettings(u.page, { show_in_user_profile: false }); - await page.reload(); - await u.po.userProfile.waitForMounted(); - await u.po.page.goToRelative('/user#/api-keys'); - await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); - - // Both enabled should show API keys page - await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: true, show_in_user_profile: true }); + // user_api_keys_enabled: true should show API keys page + await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: true }); await page.reload(); await u.po.userProfile.waitForMounted(); await u.po.page.goToRelative('/user#/api-keys'); @@ -285,14 +274,8 @@ testAgainstRunningApps({ await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); - // show_in_org_profile: false should hide API keys page - await mockAPIKeysEnvironmentSettings(u.page, { show_in_org_profile: false }); - await page.reload(); - await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); - await expect(u.page.locator('.cl-apiKeys')).toBeHidden({ timeout: 2000 }); - - // Both enabled should show API keys page - await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: true, show_in_org_profile: true }); + // orgs_api_keys_enabled: true should show API keys page + await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: true }); await page.reload(); await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); await expect(u.page.locator('.cl-apiKeys')).toBeVisible({ timeout: 5000 }); @@ -310,10 +293,9 @@ testAgainstRunningApps({ await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: false }); let apiKeysRequestWasMade = false; - u.page.on('request', request => { - if (request.url().includes('/api_keys')) { - apiKeysRequestWasMade = true; - } + await u.page.route('**/api_keys*', async route => { + apiKeysRequestWasMade = true; + await route.abort(); }); await u.po.page.goToRelative('/api-keys'); @@ -339,10 +321,9 @@ testAgainstRunningApps({ await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: false }); let apiKeysRequestWasMade = false; - u.page.on('request', request => { - if (request.url().includes('/api_keys')) { - apiKeysRequestWasMade = true; - } + await u.page.route('**/api_keys*', async route => { + apiKeysRequestWasMade = true; + await route.abort(); }); await u.po.page.goToRelative('/api-keys'); diff --git a/packages/clerk-js/src/core/resources/APIKeySettings.ts b/packages/clerk-js/src/core/resources/APIKeySettings.ts index ba3b62930f9..52d00e6ad50 100644 --- a/packages/clerk-js/src/core/resources/APIKeySettings.ts +++ b/packages/clerk-js/src/core/resources/APIKeySettings.ts @@ -7,9 +7,7 @@ import { BaseResource } from './internal'; */ export class APIKeySettings extends BaseResource implements APIKeysSettingsResource { public user_api_keys_enabled: boolean = false; - public show_in_user_profile: boolean = false; public orgs_api_keys_enabled: boolean = false; - public show_in_org_profile: boolean = false; public constructor(data: APIKeysSettingsJSON | APIKeysSettingsJSONSnapshot | null = null) { super(); @@ -23,9 +21,7 @@ export class APIKeySettings extends BaseResource implements APIKeysSettingsResou } this.user_api_keys_enabled = data.user_api_keys_enabled; - this.show_in_user_profile = data.show_in_user_profile; this.orgs_api_keys_enabled = data.orgs_api_keys_enabled; - this.show_in_org_profile = data.show_in_org_profile; return this; } @@ -33,9 +29,7 @@ export class APIKeySettings extends BaseResource implements APIKeysSettingsResou public __internal_toSnapshot(): APIKeysSettingsJSONSnapshot { return { user_api_keys_enabled: this.user_api_keys_enabled, - show_in_user_profile: this.show_in_user_profile, orgs_api_keys_enabled: this.orgs_api_keys_enabled, - show_in_org_profile: this.show_in_org_profile, } as APIKeysSettingsJSONSnapshot; } } diff --git a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts index eb33784c2ef..b00f4a99078 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts @@ -10,8 +10,6 @@ describe('Environment', () => { expect(environment).toMatchObject({ apiKeysSettings: expect.objectContaining({ orgs_api_keys_enabled: false, - show_in_org_profile: false, - show_in_user_profile: false, user_api_keys_enabled: false, pathRoot: '', }), @@ -57,8 +55,6 @@ describe('Environment', () => { id: '', api_keys_settings: { orgs_api_keys_enabled: false, - show_in_org_profile: false, - show_in_user_profile: false, user_api_keys_enabled: false, id: undefined, path_root: '', @@ -585,8 +581,6 @@ describe('Environment', () => { object: 'environment', api_keys_settings: expect.objectContaining({ orgs_api_keys_enabled: false, - show_in_org_profile: false, - show_in_user_profile: false, user_api_keys_enabled: false, }), auth_config: expect.objectContaining({ diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx index a44d2daaab4..40600731fa3 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -38,8 +38,15 @@ const OrganizationPaymentAttemptPage = lazy(() => ); export const OrganizationProfileRoutes = () => { - const { pages, isMembersPageRoot, isGeneralPageRoot, isBillingPageRoot, isApiKeysPageRoot, shouldShowBilling } = - useOrganizationProfileContext(); + const { + pages, + isMembersPageRoot, + isGeneralPageRoot, + isBillingPageRoot, + isApiKeysPageRoot, + shouldShowBilling, + apiKeysProps, + } = useOrganizationProfileContext(); const { apiKeysSettings, commerceSettings } = useEnvironment(); const customPageRoutesWithContents = pages.contents?.map((customPage, index) => { @@ -117,7 +124,7 @@ export const OrganizationProfileRoutes = () => { ) : null} - {apiKeysSettings.orgs_api_keys_enabled && apiKeysSettings.show_in_org_profile && ( + {apiKeysSettings.orgs_api_keys_enabled && !apiKeysProps?.hide && ( has({ permission: 'org:sys_api_keys:read' }) || has({ permission: 'org:sys_api_keys:manage' }) diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx index 79f849a4b14..edce7308bed 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx @@ -38,7 +38,7 @@ const PaymentAttemptPage = lazy(() => ); export const UserProfileRoutes = () => { - const { pages, shouldShowBilling } = useUserProfileContext(); + const { pages, shouldShowBilling, apiKeysProps } = useUserProfileContext(); const { apiKeysSettings, commerceSettings } = useEnvironment(); const isAccountPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT; @@ -108,7 +108,7 @@ export const UserProfileRoutes = () => { ) : null} - {apiKeysSettings.user_api_keys_enabled && apiKeysSettings.show_in_user_profile && ( + {apiKeysSettings.user_api_keys_enabled && !apiKeysProps?.hide && ( diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index f6dfa204ba3..05ced359a16 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -1,10 +1,10 @@ import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/shared/types'; import { + disabledOrganizationAPIKeysFeature, disabledOrganizationBillingFeature, - disabledOrganizationProfileAPIKeysFeature, + disabledUserAPIKeysFeature, disabledUserBillingFeature, - disabledUserProfileAPIKeysFeature, isValidUrl, } from '../../utils'; import { ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID, USER_PROFILE_NAVBAR_ROUTE_ID } from '../constants'; @@ -105,8 +105,8 @@ const createCustomPages = ( ? !disabledOrganizationBillingFeature(clerk, environment) && shouldShowBilling : !disabledUserBillingFeature(clerk, environment) && shouldShowBilling, apiKeys: organization - ? !disabledOrganizationProfileAPIKeysFeature(clerk, environment) - : !disabledUserProfileAPIKeysFeature(clerk, environment), + ? !disabledOrganizationAPIKeysFeature(clerk, environment) + : !disabledUserAPIKeysFeature(clerk, environment), }); if (isDevelopmentSDK(clerk)) { diff --git a/packages/clerk-js/src/utils/componentGuards.ts b/packages/clerk-js/src/utils/componentGuards.ts index b1063e9e992..b8379c6d68c 100644 --- a/packages/clerk-js/src/utils/componentGuards.ts +++ b/packages/clerk-js/src/utils/componentGuards.ts @@ -45,11 +45,3 @@ export const disabledOrganizationAPIKeysFeature: ComponentGuard = (_, environmen export const disabledAllAPIKeysFeatures: ComponentGuard = (_, environment) => { return disabledUserAPIKeysFeature(_, environment) && disabledOrganizationAPIKeysFeature(_, environment); }; - -export const disabledUserProfileAPIKeysFeature: ComponentGuard = (_, environment) => { - return !environment?.apiKeysSettings?.user_api_keys_enabled || !environment?.apiKeysSettings?.show_in_user_profile; -}; - -export const disabledOrganizationProfileAPIKeysFeature: ComponentGuard = (_, environment) => { - return !environment?.apiKeysSettings?.orgs_api_keys_enabled || !environment?.apiKeysSettings?.show_in_org_profile; -}; diff --git a/packages/shared/src/types/apiKeysSettings.ts b/packages/shared/src/types/apiKeysSettings.ts index 4d7bd41dff6..2dea9a3412b 100644 --- a/packages/shared/src/types/apiKeysSettings.ts +++ b/packages/shared/src/types/apiKeysSettings.ts @@ -4,16 +4,12 @@ import type { APIKeysSettingsJSONSnapshot } from './snapshots'; export interface APIKeysSettingsJSON extends ClerkResourceJSON { user_api_keys_enabled: boolean; - show_in_user_profile: boolean; orgs_api_keys_enabled: boolean; - show_in_org_profile: boolean; } export interface APIKeysSettingsResource extends ClerkResource { user_api_keys_enabled: boolean; - show_in_user_profile: boolean; orgs_api_keys_enabled: boolean; - show_in_org_profile: boolean; __internal_toSnapshot: () => APIKeysSettingsJSONSnapshot; } diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 859c47c3ae5..7f9259b0103 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -1564,7 +1564,14 @@ export type UserProfileProps = RoutingOptions & { * * @experimental */ - apiKeysProps?: APIKeysProps; + apiKeysProps?: APIKeysProps & { + /** + * Whether to hide the API Keys page. When true, the API Keys page will not be displayed even if API keys are enabled. + * + * @default false + */ + hide?: boolean; + }; }; export type UserProfileModalProps = WithoutRouting; @@ -1600,7 +1607,14 @@ export type OrganizationProfileProps = RoutingOptions & { * * @experimental */ - apiKeysProps?: APIKeysProps; + apiKeysProps?: APIKeysProps & { + /** + * Whether to hide the API Keys page. When true, the API Keys page will not be displayed even if API keys are enabled. + * + * @default false + */ + hide?: boolean; + }; }; export type OrganizationProfileModalProps = WithoutRouting; From 8fcd81601dc18fb4159df0e21fac7f9001954b08 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 11:45:05 -0800 Subject: [PATCH 15/19] chore: add unit tests for profile based visibility --- .../__tests__/OrganizationProfile.test.tsx | 86 +++++++++++++++++++ .../__tests__/UserProfile.test.tsx | 51 +++++++++++ 2 files changed, 137 insertions(+) diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationProfile.test.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationProfile.test.tsx index 028cd9bebfd..3df1503334e 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationProfile.test.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationProfile.test.tsx @@ -378,6 +378,92 @@ describe('OrganizationProfile', () => { expect(fixtures.clerk.billing.getStatements).toHaveBeenCalled(); }); }); + + describe('API Keys visibility', () => { + it('does not include API Keys when hide prop is true', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [ + { + name: 'Org1', + permissions: ['org:sys_api_keys:read'], + }, + ], + }); + }); + + fixtures.environment.apiKeysSettings.orgs_api_keys_enabled = true; + props.setProps({ apiKeysProps: { hide: true } }); + + render(, { wrapper }); + await waitFor(() => expect(screen.queryByText('API keys')).toBeNull()); + }); + + it('includes API Keys when hide prop is false and orgs_api_keys_enabled is true', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [ + { + name: 'Org1', + permissions: ['org:sys_api_keys:read'], + }, + ], + }); + }); + + fixtures.environment.apiKeysSettings.orgs_api_keys_enabled = true; + props.setProps({ apiKeysProps: { hide: false } }); + + render(, { wrapper }); + expect(await screen.findByText('API keys')).toBeDefined(); + }); + + it('includes API Keys when hide prop is not set and orgs_api_keys_enabled is true', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [ + { + name: 'Org1', + permissions: ['org:sys_api_keys:read'], + }, + ], + }); + }); + + fixtures.environment.apiKeysSettings.orgs_api_keys_enabled = true; + + render(, { wrapper }); + expect(await screen.findByText('API keys')).toBeDefined(); + }); + + it('does not include API Keys when orgs_api_keys_enabled is false even if hide is false', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [ + { + name: 'Org1', + permissions: ['org:sys_api_keys:read'], + }, + ], + }); + }); + + fixtures.environment.apiKeysSettings.orgs_api_keys_enabled = false; + props.setProps({ apiKeysProps: { hide: false } }); + + render(, { wrapper }); + await waitFor(() => expect(screen.queryByText('API keys')).toBeNull()); + }); + }); + it('removes member nav item if user is lacking permissions', async () => { const { wrapper } = await createFixtures(f => { f.withOrganizations(); diff --git a/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfile.test.tsx b/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfile.test.tsx index 3cb9884bb50..1b1ea0f6604 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfile.test.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfile.test.tsx @@ -241,4 +241,55 @@ describe('UserProfile', () => { expect(fixtures.clerk.billing.getStatements).toHaveBeenCalled(); }); }); + + describe('API Keys visibility', () => { + it('does not include API Keys when hide prop is true', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withUser({ email_addresses: ['test@clerk.com'] }); + }); + + fixtures.environment.apiKeysSettings.user_api_keys_enabled = true; + props.setProps({ apiKeysProps: { hide: true } }); + + render(, { wrapper }); + await waitFor(() => expect(screen.queryByRole('button', { name: /API keys/i })).toBeNull()); + }); + + it('includes API Keys when hide prop is false and user_api_keys_enabled is true', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withUser({ email_addresses: ['test@clerk.com'] }); + }); + + fixtures.environment.apiKeysSettings.user_api_keys_enabled = true; + props.setProps({ apiKeysProps: { hide: false } }); + + render(, { wrapper }); + const apiKeysElements = await screen.findAllByRole('button', { name: /API keys/i }); + expect(apiKeysElements.length).toBeGreaterThan(0); + }); + + it('includes API Keys when hide prop is not set and user_api_keys_enabled is true', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withUser({ email_addresses: ['test@clerk.com'] }); + }); + + fixtures.environment.apiKeysSettings.user_api_keys_enabled = true; + + render(, { wrapper }); + const apiKeysElements = await screen.findAllByRole('button', { name: /API keys/i }); + expect(apiKeysElements.length).toBeGreaterThan(0); + }); + + it('does not include API Keys when user_api_keys_enabled is false even if hide is false', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withUser({ email_addresses: ['test@clerk.com'] }); + }); + + fixtures.environment.apiKeysSettings.user_api_keys_enabled = false; + props.setProps({ apiKeysProps: { hide: false } }); + + render(, { wrapper }); + await waitFor(() => expect(screen.queryByRole('button', { name: /API keys/i })).toBeNull()); + }); + }); }); From 396464f052edf8240eead6fba9f3a94bd975da17 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 12:24:55 -0800 Subject: [PATCH 16/19] chore: hide nav --- .../OrganizationProfile/OrganizationProfileNavbar.tsx | 5 ++++- .../src/ui/components/UserProfile/UserProfileNavbar.tsx | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx index 5d0ef479f31..95e42951e29 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx @@ -29,6 +29,8 @@ export const OrganizationProfileNavbar = ( }) || has({ permission: 'org:sys_billing:manage' }), ); + const { apiKeysProps } = useOrganizationProfileContext(); + const routes = pages.routes .filter( r => @@ -39,7 +41,8 @@ export const OrganizationProfileNavbar = ( r => r.id !== ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.BILLING || (r.id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.BILLING && allowBillingRoutes), - ); + ) + .filter(r => r.id !== ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.API_KEYS || !apiKeysProps?.hide); if (!organization) { return null; } diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx index 29aa272e0c2..69e50c6d0dd 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { NavBar, NavbarContextProvider } from '@/ui/elements/Navbar'; +import { USER_PROFILE_NAVBAR_ROUTE_ID } from '../../constants'; import { useUserProfileContext } from '../../contexts'; import { localizationKeys } from '../../localization'; import type { PropsOfComponent } from '../../styledSystem'; @@ -9,14 +10,16 @@ import type { PropsOfComponent } from '../../styledSystem'; export const UserProfileNavbar = ( props: React.PropsWithChildren, 'contentRef'>>, ) => { - const { pages } = useUserProfileContext(); + const { pages, apiKeysProps } = useUserProfileContext(); + + const routes = pages.routes.filter(r => r.id !== USER_PROFILE_NAVBAR_ROUTE_ID.API_KEYS || !apiKeysProps?.hide); return ( {props.children} From 1f0884378f0fb345194d636928984b6e9cbfc1ec Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 12:34:50 -0800 Subject: [PATCH 17/19] chore: clean up --- integration/tests/machine-auth/component.test.ts | 2 ++ .../OrganizationProfile/OrganizationProfileNavbar.tsx | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/tests/machine-auth/component.test.ts b/integration/tests/machine-auth/component.test.ts index 4c0fec6adfd..5bdbf1542a9 100644 --- a/integration/tests/machine-auth/component.test.ts +++ b/integration/tests/machine-auth/component.test.ts @@ -301,6 +301,7 @@ testAgainstRunningApps({ await u.po.page.goToRelative('/api-keys'); await expect(u.page.locator('.cl-apiKeys-root')).toBeHidden({ timeout: 1000 }); expect(apiKeysRequestWasMade).toBe(false); + await u.page.unroute('**/api_keys*'); // user_api_keys_enabled: true should allow standalone component to render await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: true }); @@ -329,6 +330,7 @@ testAgainstRunningApps({ await u.po.page.goToRelative('/api-keys'); await expect(u.page.locator('.cl-apiKeys-root')).toBeHidden({ timeout: 1000 }); expect(apiKeysRequestWasMade).toBe(false); + await u.page.unroute('**/api_keys*'); // orgs_api_keys_enabled: true should allow standalone component to render in org context await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: true }); diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx index 95e42951e29..4a8a719cb01 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx @@ -13,7 +13,7 @@ export const OrganizationProfileNavbar = ( props: React.PropsWithChildren, 'contentRef'>>, ) => { const { organization } = useOrganization(); - const { pages } = useOrganizationProfileContext(); + const { apiKeysProps, pages } = useOrganizationProfileContext(); const allowMembersRoute = useProtect( has => @@ -29,8 +29,6 @@ export const OrganizationProfileNavbar = ( }) || has({ permission: 'org:sys_billing:manage' }), ); - const { apiKeysProps } = useOrganizationProfileContext(); - const routes = pages.routes .filter( r => From 3742a8acab8232e1747736a48dc10d5ec8b43934 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 13:07:37 -0800 Subject: [PATCH 18/19] test: unroute mocked apis --- integration/tests/machine-auth/component.test.ts | 12 +++++++++--- package.json | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/integration/tests/machine-auth/component.test.ts b/integration/tests/machine-auth/component.test.ts index 5bdbf1542a9..117d01e80a2 100644 --- a/integration/tests/machine-auth/component.test.ts +++ b/integration/tests/machine-auth/component.test.ts @@ -30,7 +30,7 @@ const mockAPIKeysEnvironmentSettings = async ( testAgainstRunningApps({ withEnv: [appConfigs.envs.withAPIKeys], withPattern: ['withMachine.next.appRouter'], -})('api keys component @machine', ({ app }) => { +})('api keys component @xmachine', ({ app }) => { test.describe.configure({ mode: 'serial' }); let fakeAdmin: FakeUser; @@ -258,6 +258,8 @@ testAgainstRunningApps({ await u.po.userProfile.waitForMounted(); await u.po.page.goToRelative('/user#/api-keys'); await expect(u.page.locator('.cl-apiKeys')).toBeVisible({ timeout: 5000 }); + + await u.page.unrouteAll(); }); test('OrganizationProfile API keys page visibility', async ({ page, context }) => { @@ -279,6 +281,8 @@ testAgainstRunningApps({ await page.reload(); await u.po.page.goToRelative('/organization-profile#/organization-api-keys'); await expect(u.page.locator('.cl-apiKeys')).toBeVisible({ timeout: 5000 }); + + await u.page.unrouteAll(); }); test('standalone API keys component in user context based on user_api_keys_enabled', async ({ page, context }) => { @@ -301,13 +305,14 @@ testAgainstRunningApps({ await u.po.page.goToRelative('/api-keys'); await expect(u.page.locator('.cl-apiKeys-root')).toBeHidden({ timeout: 1000 }); expect(apiKeysRequestWasMade).toBe(false); - await u.page.unroute('**/api_keys*'); // user_api_keys_enabled: true should allow standalone component to render await mockAPIKeysEnvironmentSettings(u.page, { user_api_keys_enabled: true }); await page.reload(); await u.po.apiKeys.waitForMounted(); await expect(u.page.locator('.cl-apiKeys-root')).toBeVisible(); + + await u.page.unrouteAll(); }); test('standalone API keys component in org context based on orgs_api_keys_enabled', async ({ page, context }) => { @@ -330,12 +335,13 @@ testAgainstRunningApps({ await u.po.page.goToRelative('/api-keys'); await expect(u.page.locator('.cl-apiKeys-root')).toBeHidden({ timeout: 1000 }); expect(apiKeysRequestWasMade).toBe(false); - await u.page.unroute('**/api_keys*'); // orgs_api_keys_enabled: true should allow standalone component to render in org context await mockAPIKeysEnvironmentSettings(u.page, { orgs_api_keys_enabled: true }); await page.reload(); await u.po.apiKeys.waitForMounted(); await expect(u.page.locator('.cl-apiKeys-root')).toBeVisible(); + + await u.page.unrouteAll(); }); }); diff --git a/package.json b/package.json index a4df0bbbabe..4db0034ee70 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "test:integration:handshake": "DISABLE_WEB_SECURITY=true E2E_APP_1_ENV_KEY=sessions-prod-1 E2E_SESSIONS_APP_1_HOST=multiple-apps-e2e.clerk.app pnpm test:integration:base --grep @handshake", "test:integration:handshake:staging": "DISABLE_WEB_SECURITY=true E2E_APP_1_ENV_KEY=clerkstage-sessions-prod-1 E2E_SESSIONS_APP_1_HOST=clerkstage-sessions-prod-1-e2e.clerk.app pnpm test:integration:base --grep @handshake", "test:integration:localhost": "pnpm test:integration:base --grep @localhost", - "test:integration:machine": "E2E_APP_ID=withMachine.* pnpm test:integration:base --grep @machine", + "test:integration:machine": "E2E_APP_ID=withMachine.* pnpm test:integration:base --grep @xmachine", "test:integration:nextjs": "E2E_APP_ID=next.appRouter.* pnpm test:integration:base --grep @nextjs", "test:integration:nuxt": "E2E_APP_ID=nuxt.node npm run test:integration:base -- --grep @nuxt", "test:integration:quickstart": "E2E_APP_ID=quickstart.* pnpm test:integration:base --grep @quickstart", From f07c6d525ab802ce4d17a52b2e5804476676d3af Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 10 Nov 2025 13:18:08 -0800 Subject: [PATCH 19/19] remove test script --- integration/tests/machine-auth/component.test.ts | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/tests/machine-auth/component.test.ts b/integration/tests/machine-auth/component.test.ts index 117d01e80a2..df3845fd0ed 100644 --- a/integration/tests/machine-auth/component.test.ts +++ b/integration/tests/machine-auth/component.test.ts @@ -30,7 +30,7 @@ const mockAPIKeysEnvironmentSettings = async ( testAgainstRunningApps({ withEnv: [appConfigs.envs.withAPIKeys], withPattern: ['withMachine.next.appRouter'], -})('api keys component @xmachine', ({ app }) => { +})('api keys component @machine', ({ app }) => { test.describe.configure({ mode: 'serial' }); let fakeAdmin: FakeUser; diff --git a/package.json b/package.json index 4db0034ee70..a4df0bbbabe 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "test:integration:handshake": "DISABLE_WEB_SECURITY=true E2E_APP_1_ENV_KEY=sessions-prod-1 E2E_SESSIONS_APP_1_HOST=multiple-apps-e2e.clerk.app pnpm test:integration:base --grep @handshake", "test:integration:handshake:staging": "DISABLE_WEB_SECURITY=true E2E_APP_1_ENV_KEY=clerkstage-sessions-prod-1 E2E_SESSIONS_APP_1_HOST=clerkstage-sessions-prod-1-e2e.clerk.app pnpm test:integration:base --grep @handshake", "test:integration:localhost": "pnpm test:integration:base --grep @localhost", - "test:integration:machine": "E2E_APP_ID=withMachine.* pnpm test:integration:base --grep @xmachine", + "test:integration:machine": "E2E_APP_ID=withMachine.* pnpm test:integration:base --grep @machine", "test:integration:nextjs": "E2E_APP_ID=next.appRouter.* pnpm test:integration:base --grep @nextjs", "test:integration:nuxt": "E2E_APP_ID=nuxt.node npm run test:integration:base -- --grep @nuxt", "test:integration:quickstart": "E2E_APP_ID=quickstart.* pnpm test:integration:base --grep @quickstart",