diff --git a/packages/clerk-js/src/core/resources/Session.test.ts b/packages/clerk-js/src/core/resources/Session.test.ts index b3f5c889d8b..f705632e763 100644 --- a/packages/clerk-js/src/core/resources/Session.test.ts +++ b/packages/clerk-js/src/core/resources/Session.test.ts @@ -73,7 +73,7 @@ describe('Session', () => { updated_at: new Date().getTime(), } as SessionJSON); - const isAuthorized = await session.experimental__checkAuthorization({ permission: 'org:sys_profile:delete' }); + const isAuthorized = await session.checkAuthorization({ permission: 'org:sys_profile:delete' }); expect(isAuthorized).toBe(true); }); @@ -93,7 +93,7 @@ describe('Session', () => { updated_at: new Date().getTime(), } as SessionJSON); - const isAuthorized = await session.experimental__checkAuthorization({ permission: 'org:sys_profile:delete' }); + const isAuthorized = await session.checkAuthorization({ permission: 'org:sys_profile:delete' }); expect(isAuthorized).toBe(false); }); diff --git a/packages/clerk-js/src/core/resources/Session.ts b/packages/clerk-js/src/core/resources/Session.ts index a153414c413..60860feccf3 100644 --- a/packages/clerk-js/src/core/resources/Session.ts +++ b/packages/clerk-js/src/core/resources/Session.ts @@ -75,10 +75,7 @@ export class Session extends BaseResource implements SessionResource { }); }; - /** - * @experimental The method is experimental and subject to change in future releases. - */ - experimental__checkAuthorization: CheckAuthorization = params => { + checkAuthorization: CheckAuthorization = params => { // if there is no active organization user can not be authorized if (!this.lastActiveOrganizationId || !this.user) { return false; @@ -103,18 +100,6 @@ export class Session extends BaseResource implements SessionResource { return activeOrganizationRole === params.role; } - if (params.some) { - return !!params.some.find(permObj => { - if (permObj.permission) { - return activeOrganizationPermissions.includes(permObj.permission); - } - if (permObj.role) { - return activeOrganizationRole === permObj.role; - } - return false; - }); - } - return false; }; diff --git a/packages/clerk-js/src/ui.retheme/common/Gate.tsx b/packages/clerk-js/src/ui.retheme/common/Gate.tsx index 1644b6c7b05..9aefb2801d1 100644 --- a/packages/clerk-js/src/ui.retheme/common/Gate.tsx +++ b/packages/clerk-js/src/ui.retheme/common/Gate.tsx @@ -1,13 +1,29 @@ import { useSession } from '@clerk/shared/react'; -import type { CheckAuthorization } from '@clerk/types'; +import type { CheckAuthorization, MembershipRole, OrganizationPermissionKey } from '@clerk/types'; import type { ComponentType, PropsWithChildren, ReactNode } from 'react'; import React, { useEffect } from 'react'; import { useRouter } from '../router'; -type GateParams = Parameters[0]; +type GateParams = Parameters[0] | ((has: CheckAuthorization) => boolean); type GateProps = PropsWithChildren< - GateParams & { + ( + | { + condition?: never; + role: MembershipRole; + permission?: never; + } + | { + condition?: never; + role?: never; + permission: OrganizationPermissionKey; + } + | { + condition: (has: CheckAuthorization) => boolean; + role?: never; + permission?: never; + } + ) & { fallback?: ReactNode; redirectTo?: string; } @@ -16,15 +32,31 @@ type GateProps = PropsWithChildren< export const useGate = (params: GateParams) => { const { session } = useSession(); + if (!session?.id) { + return { isAuthorizedUser: false }; + } + + /** + * if a function is passed and returns false then throw not found + */ + if (typeof params === 'function') { + if (params(session.checkAuthorization)) { + return { isAuthorizedUser: true }; + } + return { isAuthorizedUser: false }; + } + return { - isAuthorizedUser: session?.experimental__checkAuthorization(params), + isAuthorizedUser: session?.checkAuthorization(params), }; }; export const Gate = (gateProps: GateProps) => { const { children, fallback, redirectTo, ...restAuthorizedParams } = gateProps; - const { isAuthorizedUser } = useGate(restAuthorizedParams); + const { isAuthorizedUser } = useGate( + typeof restAuthorizedParams.condition === 'function' ? restAuthorizedParams.condition : restAuthorizedParams, + ); const { navigate } = useRouter(); diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileNavbar.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileNavbar.tsx index b8300940aa0..03d2ab8cddb 100644 --- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileNavbar.tsx +++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileNavbar.tsx @@ -13,16 +13,12 @@ export const OrganizationProfileNavbar = ( const { organization } = useOrganization(); const { pages } = useOrganizationProfileContext(); - const { isAuthorizedUser: allowMembersRoute } = useGate({ - some: [ - { + const { isAuthorizedUser: allowMembersRoute } = useGate( + has => + has({ permission: 'org:sys_memberships:read', - }, - { - permission: 'org:sys_memberships:manage', - }, - ], - }); + }) || has({ permission: 'org:sys_memberships:manage' }), + ); if (!organization) { return null; diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileRoutes.tsx index bc5ea20e6be..363d1ae9855 100644 --- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -77,7 +77,7 @@ export const OrganizationProfileRoutes = (props: PropsOfComponent @@ -130,8 +130,10 @@ export const OrganizationProfileRoutes = (props: PropsOfComponent + has({ permission: 'org:sys_memberships:read' }) || has({ permission: 'org:sys_memberships:manage' }) + } + redirectTo={isSettingsPageRoot ? '../' : './organization-settings'} > diff --git a/packages/clerk-js/src/ui.retheme/utils/test/mockHelpers.ts b/packages/clerk-js/src/ui.retheme/utils/test/mockHelpers.ts index 8f4d5e38ac0..c509469eff2 100644 --- a/packages/clerk-js/src/ui.retheme/utils/test/mockHelpers.ts +++ b/packages/clerk-js/src/ui.retheme/utils/test/mockHelpers.ts @@ -35,7 +35,7 @@ export const mockClerkMethods = (clerk: LoadedClerk): DeepJestMocked { mockMethodsOf(session, { - exclude: ['experimental__checkAuthorization'], + exclude: ['checkAuthorization'], }); mockMethodsOf(session.user); session.user?.emailAddresses.forEach(m => mockMethodsOf(m)); diff --git a/packages/clerk-js/src/ui/common/Gate.tsx b/packages/clerk-js/src/ui/common/Gate.tsx index 1644b6c7b05..9aefb2801d1 100644 --- a/packages/clerk-js/src/ui/common/Gate.tsx +++ b/packages/clerk-js/src/ui/common/Gate.tsx @@ -1,13 +1,29 @@ import { useSession } from '@clerk/shared/react'; -import type { CheckAuthorization } from '@clerk/types'; +import type { CheckAuthorization, MembershipRole, OrganizationPermissionKey } from '@clerk/types'; import type { ComponentType, PropsWithChildren, ReactNode } from 'react'; import React, { useEffect } from 'react'; import { useRouter } from '../router'; -type GateParams = Parameters[0]; +type GateParams = Parameters[0] | ((has: CheckAuthorization) => boolean); type GateProps = PropsWithChildren< - GateParams & { + ( + | { + condition?: never; + role: MembershipRole; + permission?: never; + } + | { + condition?: never; + role?: never; + permission: OrganizationPermissionKey; + } + | { + condition: (has: CheckAuthorization) => boolean; + role?: never; + permission?: never; + } + ) & { fallback?: ReactNode; redirectTo?: string; } @@ -16,15 +32,31 @@ type GateProps = PropsWithChildren< export const useGate = (params: GateParams) => { const { session } = useSession(); + if (!session?.id) { + return { isAuthorizedUser: false }; + } + + /** + * if a function is passed and returns false then throw not found + */ + if (typeof params === 'function') { + if (params(session.checkAuthorization)) { + return { isAuthorizedUser: true }; + } + return { isAuthorizedUser: false }; + } + return { - isAuthorizedUser: session?.experimental__checkAuthorization(params), + isAuthorizedUser: session?.checkAuthorization(params), }; }; export const Gate = (gateProps: GateProps) => { const { children, fallback, redirectTo, ...restAuthorizedParams } = gateProps; - const { isAuthorizedUser } = useGate(restAuthorizedParams); + const { isAuthorizedUser } = useGate( + typeof restAuthorizedParams.condition === 'function' ? restAuthorizedParams.condition : restAuthorizedParams, + ); const { navigate } = useRouter(); diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx index b8300940aa0..03d2ab8cddb 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileNavbar.tsx @@ -13,16 +13,12 @@ export const OrganizationProfileNavbar = ( const { organization } = useOrganization(); const { pages } = useOrganizationProfileContext(); - const { isAuthorizedUser: allowMembersRoute } = useGate({ - some: [ - { + const { isAuthorizedUser: allowMembersRoute } = useGate( + has => + has({ permission: 'org:sys_memberships:read', - }, - { - permission: 'org:sys_memberships:manage', - }, - ], - }); + }) || has({ permission: 'org:sys_memberships:manage' }), + ); if (!organization) { return null; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx index 7f2baece007..363d1ae9855 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -77,7 +77,7 @@ export const OrganizationProfileRoutes = (props: PropsOfComponent @@ -130,7 +130,9 @@ export const OrganizationProfileRoutes = (props: PropsOfComponent + has({ permission: 'org:sys_memberships:read' }) || has({ permission: 'org:sys_memberships:manage' }) + } redirectTo={isSettingsPageRoot ? '../' : './organization-settings'} > diff --git a/packages/clerk-js/src/ui/utils/test/mockHelpers.ts b/packages/clerk-js/src/ui/utils/test/mockHelpers.ts index 8f4d5e38ac0..c509469eff2 100644 --- a/packages/clerk-js/src/ui/utils/test/mockHelpers.ts +++ b/packages/clerk-js/src/ui/utils/test/mockHelpers.ts @@ -35,7 +35,7 @@ export const mockClerkMethods = (clerk: LoadedClerk): DeepJestMocked { mockMethodsOf(session, { - exclude: ['experimental__checkAuthorization'], + exclude: ['checkAuthorization'], }); mockMethodsOf(session.user); session.user?.emailAddresses.forEach(m => mockMethodsOf(m)); diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 4d8387c7b99..caafd9c7239 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -28,26 +28,10 @@ export type CheckAuthorization = CheckAuthorizationFn; type CheckAuthorizationParams = | { - some: ( - | { - role: MembershipRole; - permission?: never; - } - | { - role?: never; - permission: OrganizationPermissionKey; - } - )[]; - role?: never; - permission?: never; - } - | { - some?: never; role: MembershipRole; permission?: never; } | { - some?: never; role?: never; permission: OrganizationPermissionKey; }; @@ -67,10 +51,7 @@ export interface SessionResource extends ClerkResource { remove: () => Promise; touch: () => Promise; getToken: GetToken; - /** - * @experimental The method is experimental and subject to change in future releases. - */ - experimental__checkAuthorization: CheckAuthorization; + checkAuthorization: CheckAuthorization; clearCache: () => void; createdAt: Date; updatedAt: Date;