Skip to content

Commit 2c037ed

Browse files
Update permission creation steps and clarify org: prefix behavior (#2645)
Co-authored-by: Alexis Aguilar <[email protected]>
1 parent 4631e57 commit 2c037ed

File tree

16 files changed

+123
-60
lines changed

16 files changed

+123
-60
lines changed

docs/_partials/billing/checking-permission-using-has-function.mdx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
if (!isLoaded) return <div>Loading...</div>
99

10-
const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
10+
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
1111

1212
if (!hasPremiumAccessManage)
1313
return (
@@ -48,7 +48,7 @@
4848
const managePremiumContentDiv = document.getElementById('manage-premium-content')
4949

5050
const hasPremiumAccessManage = clerk.session.checkAuthorization({
51-
permission: 'premium_access:manage',
51+
permission: 'org:premium_access:manage',
5252
})
5353

5454
if (!hasPremiumAccessManage) {
@@ -70,7 +70,7 @@
7070
export default async function ManagePremiumContentPage() {
7171
const { has } = await auth()
7272

73-
const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
73+
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
7474

7575
if (!hasPremiumAccessManage)
7676
return (
@@ -89,7 +89,9 @@
8989
import { useAuth } from '@clerk/nuxt/composables'
9090
9191
const { isLoaded, has } = useAuth()
92-
const hasPremiumAccessManage = computed(() => has.value?.({ permission: 'premium_access:manage' }))
92+
const hasPremiumAccessManage = computed(() =>
93+
has.value?.({ permission: 'org:premium_access:manage' }),
94+
)
9395
</script>
9496
9597
<template>
@@ -111,7 +113,9 @@
111113
import { computed } from 'vue'
112114
113115
const { isLoaded, has } = useAuth()
114-
const hasPremiumAccessManage = computed(() => has.value?.({ permission: 'premium_access:manage' }))
116+
const hasPremiumAccessManage = computed(() =>
117+
has.value?.({ permission: 'org:premium_access:manage' }),
118+
)
115119
</script>
116120
117121
<template>
@@ -135,7 +139,7 @@
135139

136140
if (!isLoaded) return <div>Loading...</div>
137141

138-
const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
142+
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
139143

140144
if (!hasPremiumAccessManage)
141145
return (
@@ -161,7 +165,7 @@
161165

162166
if (!isLoaded) return <div>Loading...</div>
163167

164-
const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
168+
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
165169

166170
if (!hasPremiumAccessManage)
167171
return (
@@ -181,7 +185,7 @@
181185

182186
export const loader = async (args: LoaderFunctionArgs) => {
183187
const { has } = await getAuth(args)
184-
return { hasPremiumAccessManage: has({ permission: 'premium_access:manage' }) }
188+
return { hasPremiumAccessManage: has({ permission: 'org:premium_access:manage' }) }
185189
}
186190

187191
export default function ManagePremiumContentPage() {
@@ -212,7 +216,7 @@
212216
},
213217
loader: async ({ context }) => {
214218
return {
215-
hasPremiumAccessManage: context.auth.has({ permission: 'premium_access:manage' }),
219+
hasPremiumAccessManage: context.auth.has({ permission: 'org:premium_access:manage' }),
216220
}
217221
},
218222
})
@@ -240,7 +244,7 @@
240244

241245
if (!isLoaded) return <div>Loading...</div>
242246

243-
const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
247+
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
244248

245249
if (!hasPremiumAccessManage)
246250
return (
@@ -264,7 +268,7 @@
264268
router.get('/manage-premium-content', requireAuth(), async (req, res) => {
265269
const { has } = getAuth(req)
266270

267-
const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
271+
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
268272

269273
if (hasPremiumAccessManage) {
270274
res.send('Our Exclusive Content')
@@ -286,7 +290,7 @@
286290
fastify.get('/manage-premium-content', async (request: FastifyRequest, reply: FastifyReply) => {
287291
const { has } = getAuth(request)
288292

289-
const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
293+
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
290294

291295
if (hasPremiumAccessManage) {
292296
reply.send('Our Exclusive Content')
@@ -323,7 +327,7 @@
323327
return new Response('Unauthorized', { status: 401 })
324328
}
325329

326-
const hasPremiumAccessManage = user.has({ permission: 'premium_access:manage' })
330+
const hasPremiumAccessManage = user.has({ permission: 'org:premium_access:manage' })
327331

328332
if (!hasPremiumAccessManage) {
329333
return new Response(
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Authorization checks are checks you perform in your code to determine the access rights and privileges of a user, ensuring they have the necessary permissions to perform specific actions or access certain content. Learn more about [authorization checks](/docs/guides/secure/authorization-checks).

docs/guides/billing/for-b2b.mdx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ Clerk billing for B2B SaaS allows you to create plans and manage subscriptions *
1212

1313
## Create a plan
1414

15-
Subscription plans are what your customers subscribe to. There is no limit to the number of plans you can create. If your Clerk instance has existing custom permissions, the corresponding features from those permissions will automatically be added to the free plan for orgs. This ensures that organization members get the same set of custom permissions when billing is enabled, because all organizations start on the free plan.
15+
Subscription plans are what your customers subscribe to. There is no limit to the number of plans you can create. If your Clerk instance has existing [custom permissions](/docs/guides/organizations/roles-and-permissions), the corresponding features from those permissions will automatically be added to the free plan for orgs. This ensures that organization members get the same set of custom permissions when billing is enabled, because all organizations start on the free plan.
1616

17-
To create a plan, navigate to the [**Plans**](https://dashboard.clerk.com/last-active?path=billing/plans) page in the Clerk Dashboard. Here, you can create, edit, and delete plans. To setup B2B billing, select the **Plans for Organizations** tab and select **Add Plan**. When creating a plan, you can also create features for the plan; see the next section for more information.
17+
To create a plan, navigate to the [**Plans**](https://dashboard.clerk.com/last-active?path=billing/plans) page in the Clerk Dashboard. Here, you can create, edit, and delete plans. To setup B2B billing, select the **Plans for Organizations** tab and select **Add Plan**. When creating a plan, you can also create [features](/docs/guides/secure/features) for the plan; see the next section for more information.
1818

1919
> [!TIP]
2020
> What is the **Publicly available** option?
@@ -25,7 +25,7 @@ To create a plan, navigate to the [**Plans**](https://dashboard.clerk.com/last-a
2525
2626
## Add features to a plan
2727

28-
Features make it easy to give entitlements to your plans. You can add any number of features to a plan.
28+
[Features](/docs/guides/secure/features) make it easy to give entitlements to your plans. You can add any number of features to a plan.
2929

3030
You can add a feature to a plan when you are creating a plan. To add it after a plan is created:
3131

@@ -48,11 +48,12 @@ You can create a pricing page by using the [`<PricingTable />`](/docs/reference/
4848

4949
## Control access with features, plans, and permissions
5050

51-
You can use Clerk's features, plans, and permissions to gate access to content using [**authorization checks**](/docs/guides/secure/authorization-checks). There are a few ways to do this, but the recommended approach is to either use the [`has()`](/docs/reference/backend/types/auth-object#has) method or the [`<Protect>`](/docs/reference/components/control/protect) component.
51+
You can use Clerk's features, plans, and permissions to gate access to content using [authorization checks](!authorization-check). There are a few ways to do this, but the recommended approach is to either use the [`has()`](/docs/reference/backend/types/auth-object#has) method or the [`<Protect>`](/docs/reference/components/control/protect) component.
5252

53-
Permission-based authorization checks link with feature-based authorization checks. This means that if you are checking a custom permission, it will only work if the feature part of the permission key (`org:<feature>:<permission>`) **is a feature included in the organization's active plan**. For example, say you want to check if an organization member has the custom permission `org:teams:manage`, where `teams` is the feature. Before performing the authorization check, you need to ensure that the user's organization is subscribed to a plan that has the `teams` feature. If the user's organization is not subscribed to a plan that has the `teams` feature, the authorization check will always return `false`, even if the user has the custom permission.
53+
The `has()` method is available for any JavaScript-based framework, while `<Protect>` is a component, and therefore, is only available for React-based frameworks.
5454

55-
The `has()` method is available for any JavaScript framework, while `<Protect>` is only available for React-based frameworks.
55+
> [!IMPORTANT]
56+
> Permission-based authorization checks link with feature-based authorization checks. This means that if you are checking a custom permission, it will only work if the feature part of the permission key (`org:<feature>:<permission>`) **is a feature included in the organization's active plan**. For example, say you want to check if an organization member has the custom permission `org:teams:manage`, where `teams` is the feature. Before performing the authorization check, you need to ensure that the user's organization is subscribed to a plan that has the `teams` feature. If the user's organization is not subscribed to a plan that has the `teams` feature, the authorization check will always return `false`, _even if the user has the custom permission_.
5657
5758
### Example: Using `has()`
5859

@@ -68,7 +69,7 @@ Or a **feature**:
6869
const hasPremiumAccess = has({ feature: 'widgets' })
6970
```
7071

71-
The [`has()`](/docs/reference/backend/types/auth-object#has) method checks if the organization has been granted a specific type of access control (role, permission, feature, or plan) and returns a boolean value. It is available on the [`auth` object](/docs/reference/backend/types/auth-object) on the server. Depending on the framework you are using, you will access the `auth` object differently.
72+
The [`has()`](/docs/reference/backend/types/auth-object#has) method is a server-side helper that checks if the organization has been granted a specific type of access control (role, permission, feature, or plan) and returns a boolean value. `has()` is available on the [`auth` object](/docs/reference/backend/types/auth-object), which you will access differently [depending on the framework you are using](/docs/reference/backend/types/auth-object#how-to-access-the-auth-object).
7273

7374
> [!TIP]
7475
> Why aren't custom permissions appearing in the session token (JWT) or in API responses (including the result of the `has()` check)?
@@ -77,7 +78,7 @@ The [`has()`](/docs/reference/backend/types/auth-object#has) method checks if th
7778
>
7879
> Custom permissions will only appear in the session token (JWT) and in API responses (including the result of the `has()` check) if the feature part of the permission key (`org:<feature>:<permission>`) **is a feature included in the organization's active plan**. If the feature is not part of the plan, the `has()` check for permissions using that feature will return `false`, and those permissions will not be represented in the session token.
7980
>
80-
> For example, say you want to check if an organization member has the custom permission `org:teams:manage`, where `teams` is the feature. The user's organization must be subscribed to a plan that has the `teams` feature for authorization checks to work. If the user's organization is not subscribed to a plan that has the `teams` feature, the authorization check will always return `false`, even if the user has the custom permission.
81+
> For example, say you want to check if an organization member has the custom permission `org:teams:manage`, where `teams` is the feature. The user's organization must be subscribed to a plan that has the `teams` feature for authorization checks to work. If the user's organization is not subscribed to a plan that has the `teams` feature, the authorization check will always return `false`, _even if the user has the custom permission_.
8182
8283
<Tabs items={[ "Plan", "Feature", "Permission"]}>
8384
<Tab>

docs/guides/billing/for-b2c.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ To create a plan, navigate to the [**Plans**](https://dashboard.clerk.com/last-a
2525
2626
## Add features to a plan
2727

28-
Features make it easy to give entitlements to your plans. You can add any number of features to a plan.
28+
[Features](/docs/guides/secure/features) make it easy to give entitlements to your plans. You can add any number of features to a plan.
2929

3030
You can add a feature to a plan when you are creating a plan. To add it after a plan is created:
3131

@@ -66,7 +66,7 @@ Or a **feature**:
6666
const hasPremiumAccess = has({ feature: 'widgets' })
6767
```
6868

69-
The [`has()`](/docs/reference/backend/types/auth-object#has) method checks if the user has been granted a specific type of access control (role, permission, feature, or plan) and returns a boolean value. It is available on the [`auth` object](/docs/reference/backend/types/auth-object) on the server. Depending on the framework you are using, you will access the `auth` object differently.
69+
The [`has()`](/docs/reference/backend/types/auth-object#has) method is a server-side helper that checks if the organization has been granted a specific type of access control (role, permission, feature, or plan) and returns a boolean value. `has()` is available on the [`auth` object](/docs/reference/backend/types/auth-object), which you will access differently [depending on the framework you are using](/docs/reference/backend/types/auth-object#how-to-access-the-auth-object).
7070

7171
<Tabs items={[ "Plan", "Feature"]}>
7272
<Tab>

docs/guides/billing/free-trials.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Let users try paid features before subscribing
55

66
<Include src="_partials/billing/billing-experimental" />
77

8-
Free trials let your users explore paid features for a limited time for free, helping them build confidence in a purchase decision. With Clerk Billing, you can turn on free trials for any plan, or set the same trial period across all your plans.
8+
Free trials let your users explore paid [features](/docs/guides/secure/features) for a limited time for free, helping them build confidence in a purchase decision. With Clerk Billing, you can turn on free trials for any plan, or set the same trial period across all your plans.
99

1010
## Enable free trials
1111

docs/guides/development/custom-flows/account-updates/user-impersonation.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This guide will walk you through how to build a custom flow that handles user im
1313

1414
<Tabs items={["Next.js", "Expo"]}>
1515
<Tab>
16-
The following example builds a dashboard that is only accessible to users with the `org:admin:impersonate` permission. To use this example, you must first [create the custom `org:admin:impersonate` permission](/docs/guides/organizations/roles-and-permissions#custom-permissions). Or you can modify the [authorization checks](/docs/guides/secure/authorization-checks) to fit your use case.
16+
The following example builds a dashboard that is only accessible to users with the `org:admin:impersonate` permission. To use this example, you must first [create the custom `org:admin:impersonate` permission](/docs/guides/organizations/roles-and-permissions#custom-permissions). Or you can modify the [authorization checks](!authorization-check) to fit your use case.
1717

1818
In the dashboard, the user will see a list of the application's users. When the user chooses to impersonate a user, they will be signed in as that user and redirected to the homepage.
1919

docs/guides/development/sdk-development/backend-only.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ You can manually create a wrapper library around the [BAPI OpenAPI](/docs/refere
152152

153153
const hasPermission = (context, next) => {
154154
const auth = getAuth(context)
155-
if (!auth.has({ permission: 'permission' })) {
155+
if (!auth.has({ permission: 'org:feature:permission' })) {
156156
context.status = 403
157157
return
158158
}

0 commit comments

Comments
 (0)