@@ -197,17 +199,19 @@ export const ApiKeysTable = ({
-
- onRevoke(apiKey.id, apiKey.name),
- },
- ]}
- />
- |
+ {canManageAPIKeys && (
+
+ onRevoke(apiKey.id, apiKey.name),
+ },
+ ]}
+ />
+ |
+ )}
))
)}
diff --git a/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts
index a034c2c3b2b..4441750eb65 100644
--- a/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts
+++ b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts
@@ -2,13 +2,26 @@ import { useClerk } from '@clerk/shared/react';
import { useState } from 'react';
import useSWR from 'swr';
-export const useApiKeys = ({ subject, perPage = 5 }: { subject: string; perPage?: number }) => {
+export const useApiKeys = ({
+ subject,
+ perPage = 5,
+ enabled,
+}: {
+ subject: string;
+ perPage?: number;
+ enabled: boolean;
+}) => {
const clerk = useClerk();
+
const cacheKey = {
key: 'api-keys',
subject,
};
- const { data: apiKeys, isLoading, mutate } = useSWR(cacheKey, () => clerk.apiKeys.getAll({ subject }));
+ const {
+ data: apiKeys,
+ isLoading,
+ mutate,
+ } = useSWR(enabled ? cacheKey : null, () => clerk.apiKeys.getAll({ subject }));
const [search, setSearch] = useState('');
const filteredApiKeys = (apiKeys ?? []).filter(key => key.name.toLowerCase().includes(search.toLowerCase()));
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx
index 0ec0041a580..863a173afd3 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx
@@ -116,15 +116,21 @@ export const OrganizationProfileRoutes = () => {
)}
{apiKeysSettings.enabled && (
-
-
-
-
-
-
-
-
-
+
+ has({ permission: 'org:sys_api_keys:read' }) || has({ permission: 'org:sys_api_keys:manage' })
+ }
+ >
+
+
+
+
+
+
+
+
+
+
)}
diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx
index c3929f842c6..a856048aeb7 100644
--- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx
+++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx
@@ -1,6 +1,7 @@
import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/types';
import {
+ canViewOrManageAPIKeys,
disabledAPIKeysFeature,
disabledBillingFeature,
hasPaidOrgPlans,
@@ -99,7 +100,7 @@ const createCustomPages = (
commerce:
!disabledBillingFeature(clerk, environment) &&
(organization ? hasPaidOrgPlans(clerk, environment) : hasPaidUserPlans(clerk, environment)),
- apiKeys: !disabledAPIKeysFeature(clerk, environment),
+ apiKeys: !disabledAPIKeysFeature(clerk, environment) && (organization ? canViewOrManageAPIKeys(clerk) : true),
});
if (isDevelopmentSDK(clerk)) {
diff --git a/packages/clerk-js/src/utils/componentGuards.ts b/packages/clerk-js/src/utils/componentGuards.ts
index d704cdf5997..03770b41936 100644
--- a/packages/clerk-js/src/utils/componentGuards.ts
+++ b/packages/clerk-js/src/utils/componentGuards.ts
@@ -37,3 +37,14 @@ export const hasPaidUserPlans: ComponentGuard = (_, environment) => {
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' })
+ );
+};