Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
34 changes: 18 additions & 16 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1233,22 +1233,24 @@ export class Clerk implements ClerkInterface {

logger.warnOnce('Clerk: <APIKeys /> 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 =>
Expand Down
16 changes: 16 additions & 0 deletions packages/clerk-js/src/core/resources/APIKeySettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -19,13 +27,21 @@ 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;
}

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export const OrganizationProfileRoutes = () => {
</Route>
</Protect>
) : null}
{apiKeysSettings.enabled && (
{apiKeysSettings.orgs_api_keys_enabled && apiKeysSettings.show_in_org_profile && (
<Protect
condition={has =>
has({ permission: 'org:sys_api_keys:read' }) || has({ permission: 'org:sys_api_keys:manage' })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const UserProfileRoutes = () => {
</Switch>
</Route>
) : null}
{apiKeysSettings.enabled && (
{apiKeysSettings.user_api_keys_enabled && apiKeysSettings.show_in_user_profile && (
<Route path={isAPIKeysPageRoot ? undefined : 'api-keys'}>
<Switch>
<Route index>
Expand Down
8 changes: 5 additions & 3 deletions packages/clerk-js/src/ui/utils/createCustomPages.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/shared/types';

import {
canViewOrManageAPIKeys,
disabledAPIKeysFeature,
disabledOrganizationAPIKeysFeature,
disabledOrganizationBillingFeature,
disabledUserAPIKeysFeature,
disabledUserBillingFeature,
isValidUrl,
} from '../../utils';
Expand Down Expand Up @@ -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)) {
Expand Down
29 changes: 20 additions & 9 deletions packages/clerk-js/src/utils/componentGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <APIKeys /> 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 <APIKeys /> component - checks if org API keys are enabled (not show_in_profile)
*/
export const disabledOrganizationAPIKeysStandaloneFeature: ComponentGuard = (_, environment) => {
return !environment?.apiKeysSettings?.orgs_api_keys_enabled;
};
8 changes: 8 additions & 0 deletions packages/shared/src/types/apiKeysSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Loading