diff --git a/docs/_partials/billing/checking-permission-using-has-function.mdx b/docs/_partials/billing/checking-permission-using-has-function.mdx
index 3ed45b6aba..4d6fcb513c 100644
--- a/docs/_partials/billing/checking-permission-using-has-function.mdx
+++ b/docs/_partials/billing/checking-permission-using-has-function.mdx
@@ -7,7 +7,7 @@
if (!isLoaded) return
Loading...
- const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
if (!hasPremiumAccessManage)
return (
@@ -48,7 +48,7 @@
const managePremiumContentDiv = document.getElementById('manage-premium-content')
const hasPremiumAccessManage = clerk.session.checkAuthorization({
- permission: 'premium_access:manage',
+ permission: 'org:premium_access:manage',
})
if (!hasPremiumAccessManage) {
@@ -70,7 +70,7 @@
export default async function ManagePremiumContentPage() {
const { has } = await auth()
- const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
if (!hasPremiumAccessManage)
return (
@@ -89,7 +89,9 @@
import { useAuth } from '@clerk/nuxt/composables'
const { isLoaded, has } = useAuth()
- const hasPremiumAccessManage = computed(() => has.value?.({ permission: 'premium_access:manage' }))
+ const hasPremiumAccessManage = computed(() =>
+ has.value?.({ permission: 'org:premium_access:manage' }),
+ )
@@ -111,7 +113,9 @@
import { computed } from 'vue'
const { isLoaded, has } = useAuth()
- const hasPremiumAccessManage = computed(() => has.value?.({ permission: 'premium_access:manage' }))
+ const hasPremiumAccessManage = computed(() =>
+ has.value?.({ permission: 'org:premium_access:manage' }),
+ )
@@ -135,7 +139,7 @@
if (!isLoaded) return Loading...
- const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
if (!hasPremiumAccessManage)
return (
@@ -161,7 +165,7 @@
if (!isLoaded) return Loading...
- const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
if (!hasPremiumAccessManage)
return (
@@ -181,7 +185,7 @@
export const loader = async (args: LoaderFunctionArgs) => {
const { has } = await getAuth(args)
- return { hasPremiumAccessManage: has({ permission: 'premium_access:manage' }) }
+ return { hasPremiumAccessManage: has({ permission: 'org:premium_access:manage' }) }
}
export default function ManagePremiumContentPage() {
@@ -212,7 +216,7 @@
},
loader: async ({ context }) => {
return {
- hasPremiumAccessManage: context.auth.has({ permission: 'premium_access:manage' }),
+ hasPremiumAccessManage: context.auth.has({ permission: 'org:premium_access:manage' }),
}
},
})
@@ -240,7 +244,7 @@
if (!isLoaded) return Loading...
- const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
if (!hasPremiumAccessManage)
return (
@@ -264,7 +268,7 @@
router.get('/manage-premium-content', requireAuth(), async (req, res) => {
const { has } = getAuth(req)
- const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
if (hasPremiumAccessManage) {
res.send('Our Exclusive Content')
@@ -286,7 +290,7 @@
fastify.get('/manage-premium-content', async (request: FastifyRequest, reply: FastifyReply) => {
const { has } = getAuth(request)
- const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })
if (hasPremiumAccessManage) {
reply.send('Our Exclusive Content')
@@ -323,7 +327,7 @@
return new Response('Unauthorized', { status: 401 })
}
- const hasPremiumAccessManage = user.has({ permission: 'premium_access:manage' })
+ const hasPremiumAccessManage = user.has({ permission: 'org:premium_access:manage' })
if (!hasPremiumAccessManage) {
return new Response(
diff --git a/docs/_tooltips/authorization-check.mdx b/docs/_tooltips/authorization-check.mdx
new file mode 100644
index 0000000000..d4bb690dd3
--- /dev/null
+++ b/docs/_tooltips/authorization-check.mdx
@@ -0,0 +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).
diff --git a/docs/guides/billing/for-b2b.mdx b/docs/guides/billing/for-b2b.mdx
index cbd19bf43c..acc80ea25f 100644
--- a/docs/guides/billing/for-b2b.mdx
+++ b/docs/guides/billing/for-b2b.mdx
@@ -12,9 +12,9 @@ Clerk billing for B2B SaaS allows you to create plans and manage subscriptions *
## Create a plan
-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.
+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.
-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.
+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.
> [!TIP]
> What is the **Publicly available** option?
@@ -25,7 +25,7 @@ To create a plan, navigate to the [**Plans**](https://dashboard.clerk.com/last-a
## Add features to a plan
-Features make it easy to give entitlements to your plans. You can add any number of features to a plan.
+[Features](/docs/guides/secure/features) make it easy to give entitlements to your plans. You can add any number of features to a plan.
You can add a feature to a plan when you are creating a plan. To add it after a plan is created:
@@ -48,11 +48,12 @@ You can create a pricing page by using the [``](/docs/reference/
## Control access with features, plans, and permissions
-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 [``](/docs/reference/components/control/protect) component.
+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 [``](/docs/reference/components/control/protect) component.
-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::`) **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.
+The `has()` method is available for any JavaScript-based framework, while `` is a component, and therefore, is only available for React-based frameworks.
-The `has()` method is available for any JavaScript framework, while `` is only available for React-based frameworks.
+> [!IMPORTANT]
+> 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::`) **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_.
### Example: Using `has()`
@@ -68,7 +69,7 @@ Or a **feature**:
const hasPremiumAccess = has({ feature: 'widgets' })
```
-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.
+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).
> [!TIP]
> 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
>
> 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::`) **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.
>
-> 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.
+> 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_.
diff --git a/docs/guides/billing/for-b2c.mdx b/docs/guides/billing/for-b2c.mdx
index 8738f43400..7670447fb8 100644
--- a/docs/guides/billing/for-b2c.mdx
+++ b/docs/guides/billing/for-b2c.mdx
@@ -25,7 +25,7 @@ To create a plan, navigate to the [**Plans**](https://dashboard.clerk.com/last-a
## Add features to a plan
-Features make it easy to give entitlements to your plans. You can add any number of features to a plan.
+[Features](/docs/guides/secure/features) make it easy to give entitlements to your plans. You can add any number of features to a plan.
You can add a feature to a plan when you are creating a plan. To add it after a plan is created:
@@ -66,7 +66,7 @@ Or a **feature**:
const hasPremiumAccess = has({ feature: 'widgets' })
```
-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.
+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).
diff --git a/docs/guides/billing/free-trials.mdx b/docs/guides/billing/free-trials.mdx
index eaebebdcb4..3a258dbd55 100644
--- a/docs/guides/billing/free-trials.mdx
+++ b/docs/guides/billing/free-trials.mdx
@@ -5,7 +5,7 @@ description: Let users try paid features before subscribing
-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.
+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.
## Enable free trials
diff --git a/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx b/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx
index 9929feb8c0..9529495049 100644
--- a/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx
+++ b/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx
@@ -13,7 +13,7 @@ This guide will walk you through how to build a custom flow that handles user im
- 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.
+ 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.
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.
diff --git a/docs/guides/development/sdk-development/backend-only.mdx b/docs/guides/development/sdk-development/backend-only.mdx
index 9993bb10e9..7ebe95f963 100644
--- a/docs/guides/development/sdk-development/backend-only.mdx
+++ b/docs/guides/development/sdk-development/backend-only.mdx
@@ -152,7 +152,7 @@ You can manually create a wrapper library around the [BAPI OpenAPI](/docs/refere
const hasPermission = (context, next) => {
const auth = getAuth(context)
- if (!auth.has({ permission: 'permission' })) {
+ if (!auth.has({ permission: 'org:feature:permission' })) {
context.status = 403
return
}
diff --git a/docs/guides/organizations/roles-and-permissions.mdx b/docs/guides/organizations/roles-and-permissions.mdx
index d3101f4c8d..95f4750062 100644
--- a/docs/guides/organizations/roles-and-permissions.mdx
+++ b/docs/guides/organizations/roles-and-permissions.mdx
@@ -5,6 +5,8 @@ metadata:
title: B2B/B2C roles and permissions with Clerk Organizations
---
+
+
Clerk supports modeling your own custom role and permissions to control access to resources within your application when you use [organizations](/docs/guides/organizations/overview).
## Roles
@@ -18,9 +20,6 @@ For each instance, there are currently two default roles:
- **Admin (`org:admin`)** - Offers full access to organization resources. Members with the admin role have all of the [system permissions](#system-permissions). They can fully manage the organization and organization memberships.
- **Member (`org:member`)** - Offers limited access to organization resources. Access to organization resources is limited to the "Read members" and "Read billing" permissions only, by default. They cannot manage the organization and organization memberships, but they can view information about other members in it.
-> [!NOTE]
-> If you enabled organizations for your application before December 2023, the **Admin** role is `admin` and the **Member** role is `basic_member`, instead of `org:admin` and `org:member`, respectively.
-
### The **Creator** role
When a user creates a new organization, they're automatically added as its first member and assigned the organization's designated **Creator** role. By default, that role is `org:admin`.
@@ -35,7 +34,7 @@ The **Creator** role must _at least_ have the following [system permissions](#sy
To reassign the **Creator** role:
-1. In the Clerk Dashboard, navigate to [**Roles**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
+1. In the Clerk Dashboard, navigate to [**Roles & Permissions**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
1. [Create a new role](#custom-roles) or use an existing role from the list.
1. Ensure that **Manage members**, **Read members**, and **Delete organization** system permissions are selected for the role.
1. Open the three dots icon for the role.
@@ -49,26 +48,23 @@ You cannot delete an organization role if it's used as the organization's **Defa
To reassign the **Default** role:
-1. In the Clerk Dashboard, navigate to [**Roles**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
+1. In the Clerk Dashboard, navigate to [**Roles & Permissions**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
1. [Create a new role](#custom-roles) or use an existing role from the list.
1. Open the three dots icon for the role.
1. From the dropdown, select the **Set as Default role** option.
### Custom roles
-> [!WARNING]
-> Custom roles require a [paid plan](/pricing){{ target: '_blank' }} and the Enhanced B2B SaaS Add-on for production use, but free to use in development mode so that you can try out what works for you. See the [pricing](/pricing){{ target: '_blank' }} page for more information.
-
You can create up to 10 custom organization roles per application instance to meet your application needs. If you need more than 10 roles, [contact support](/contact/support){{ target: '_blank' }}.
Custom roles can be granted permissions and access. For example, you can create a new role of **Billing** (`org:billing`) which can be used to group users who belong to a specific department of the organization and have permission to manage credit card information, invoices, and other resources related to billing.
To create a new role:
-1. In the Clerk Dashboard, navigate to [**Roles**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles)
-1. Select **Create new role**.
-1. Give the role a name, a key to reference it by in the format `org:`, and a description.
-1. Select **Create role**.
+1. In the Clerk Dashboard, navigate to [**Roles & Permissions**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles)
+1. Select **Add role**.
+1. Give the role a name, a key to reference it by, and a description. The final key will follow the format `org:`.
+1. Select **Save**.
### Change a user's role
@@ -85,13 +81,13 @@ To change a user's role in the Clerk Dashboard:
You cannot delete a role that is still assigned to members of an organization. Change the members to a different role before completing the following steps.
-1. In the Clerk Dashboard, navigate to [**Roles**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
+1. In the Clerk Dashboard, navigate to [**Roles & Permissions**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
1. Select the three dots icon next to the role.
1. Select **Delete role**.
## Permissions
-Permissions grant users privileged access to resources and operations, like creating and deleting. Clerk supports two types of permissions: System and Custom.
+Permissions grant users privileged access to resources and operations, like creating and deleting. Clerk supports two types of permissions: **system permissions** and **custom permissions**.
### System permissions
@@ -115,18 +111,14 @@ You can assign these system permissions to any role.
### Custom permissions
-> [!WARNING]
-> Custom permissions require a [paid plan](/pricing){{ target: '_blank' }} and the Enhanced B2B SaaS Add-on for production use, but free to use in development mode so that you can try out what works for you. See the [pricing](/pricing){{ target: '_blank' }} page for more information.
-
-When creating a new permission, follow the format `org::`. You can then assign the permission to an existing role.
-
-For example, you could create a new permission called **Create invoices** (`org:invoices:create`) which allows only users with this permission to edit invoices. Then, you could assign this permission to a role, or multiple roles, such as **Billing** (`org:billing`) or **Sales** (`org:sales`).
+Custom permissions let you define fine-tuned access control within your organization. Each permission is tied to a feature, and can be assigned to one or more roles. To create a custom permission, you must first create a role (e.g. **sales**) and a feature within that role (e.g. **invoices**). Once both exist, you can define specific permissions (e.g. **create**) related to that feature.
To create a new permission:
-1. In the Clerk Dashboard, navigate to [**Permissions**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
-1. Select **Create new permission**.
-1. Give the permission a name, a key to reference it by in the format `org::`, and a description.
+1. In the Clerk Dashboard, navigate to [**Roles & Permissions**](https://dashboard.clerk.com/last-active?path=organizations-settings/roles).
+1. [Create a new role](#custom-roles) or use an existing role from the list.
+1. Under **Custom permissions**, select **Create permission** under the feature you want to create the permission for. If there are no features, you'll need to create a new one first. Select **Create feature** and fill in the required fields. Once finished, the newly created feature will appear in the list, and the **Create permission** button will appear.
+1. Give the permission a name, a key to reference it by, and a description. The final key will follow the format `org::`.
> [!NOTE]
> Common permission values could be:
>
@@ -138,6 +130,8 @@ To create a new permission:
> For example, you could create a new permission called **Create invoices** (`org:invoices:create`) which allows only users with this permission to edit invoices. Then, you could assign this permission to a role, or multiple roles, such as **Billing** (`org:billing`) or **Sales** (`org:sales`).
1. Select **Create permission**.
+You can also create a custom permission by navigating to the [**Features**](https://dashboard.clerk.com/last-active?path=features) tab in the Clerk Dashboard.
+
## Verify a user's role or permission
It's best practice to always verify whether or not a user is **authorized** to access sensitive information, important content, or exclusive features. **Authorization** is the process of determining the access rights and privileges of a user, ensuring they have the necessary permissions to perform specific actions. To perform authorization checks using a user's role or permission, see the [guide on authorizing users](/docs/guides/secure/authorization-checks).
diff --git a/docs/guides/secure/authorization-checks.mdx b/docs/guides/secure/authorization-checks.mdx
index 0cfa21da6e..93dbbb36f7 100644
--- a/docs/guides/secure/authorization-checks.mdx
+++ b/docs/guides/secure/authorization-checks.mdx
@@ -41,7 +41,7 @@ This guide will show you how to implement authorization checks in order to prote
- Note: Using `has()` **on the server-side** to check permissions works only with **custom permissions**, as [system permissions](/docs/guides/organizations/roles-and-permissions#system-permissions) aren't included in the session token claims. To check system permissions, verify the user's role instead.
- Checking for a role or permission depends on the user having an [active organization](!active-organization). Without an active organization, the authorization checks will likely always evaluate to false by default.
- If you would like to perform role-based authorization checks **without** using Clerk's organizations feature, see [the Role Based Access Control (RBAC) guide](/docs/guides/secure/basic-rbac).
-- 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::`) **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.
+- If you have both organizations and billing enabled, a permission check will only work if the feature part of the permission key (`org::`) **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 not, the authorization check will always return `false`, _even if the user has the custom permission_.
* Be cautious when doing authorization checks in layouts, as these don't re-render on navigation, meaning the user session won't be checked on every route change. [Read more in the Next.js docs](https://nextjs.org/docs/app/building-your-application/authentication#layouts-and-auth-checks).
diff --git a/docs/guides/secure/features.mdx b/docs/guides/secure/features.mdx
new file mode 100644
index 0000000000..6b841f47a4
--- /dev/null
+++ b/docs/guides/secure/features.mdx
@@ -0,0 +1,13 @@
+---
+title: Features
+description: Grant access to your privileged content and data using features.
+---
+
+Features are specific capabilities or functionalities in your application that you want to control access to. They are used in [authorization checks](!authorization-check).
+
+Features can be used in two ways:
+
+- With Clerk's [organizations](/docs/guides/organizations/overview) feature to create custom permissions. Custom permissions are always tied to a feature, and are formatted as `org::`. For example, you could create a feature called **invoices** and then create a new permission called **create invoices**. The custom permission's key would be `org:invoices:create`. Learn more about [custom permissions](/docs/guides/organizations/roles-and-permissions).
+- With Clerk's billing feature to create features specific to a subscription plan. See the [billing docs](/docs/guides/billing/overview) for more information.
+
+To manage your features, navigate to the [**Features**](https://dashboard.clerk.com/~/features) page in the Clerk Dashboard.
diff --git a/docs/manifest.json b/docs/manifest.json
index dce06c2c30..665b25aa2b 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -551,6 +551,10 @@
"title": "Restricting access",
"href": "/docs/guides/secure/restricting-access"
},
+ {
+ "title": "Features",
+ "href": "/docs/guides/secure/features"
+ },
{
"title": "Authorization checks",
"href": "/docs/guides/secure/authorization-checks"
diff --git a/docs/reference/components/control/protect.mdx b/docs/reference/components/control/protect.mdx
index 25f2751fec..409f8885b4 100644
--- a/docs/reference/components/control/protect.mdx
+++ b/docs/reference/components/control/protect.mdx
@@ -9,14 +9,14 @@ The [``](/docs/reference/components/control/protect) component protec
- authentication: whether the user is signed-in or not.
- authorization: whether the user has been granted a specific type of access control (role, permission, feature, or plan)
-`` **always** performs authentication checks. To perform authorization checks, you can pass different props, like `role`, `permission`, `feature`, or `plan`.
+`` **always** performs authentication checks. To perform [authorization checks](!authorization-check), you can pass different props, like `role`, `permission`, `feature`, or `plan`.
`` accepts a `fallback` prop that will be rendered if the user fails the authentication or authorization checks.
`` can be used both client-side and server-side (in Server Components).
> [!CAUTION]
-> This component only **visually hides** its children when the current user is not authorized. The contents of its children remain accessible via the browser's source code even if the user fails the authorization check. Do not use this component to hide sensitive information that should be completely inaccessible to unauthorized users. For truly sensitive data, perform [authorization checks](/docs/guides/secure/authorization-checks) on the server before sending the data to the client.
+> This component only **visually hides** its children when the current user is not authorized. The contents of its children remain accessible via the browser's source code even if the user fails the authorization check. Do not use this component to hide sensitive information that should be completely inaccessible to unauthorized users. For truly sensitive data, perform [authorization checks](!authorization-check) on the server before sending the data to the client.
## Usage
diff --git a/docs/reference/components/control/signed-in.mdx b/docs/reference/components/control/signed-in.mdx
index 66b65cced8..322ce54e5b 100644
--- a/docs/reference/components/control/signed-in.mdx
+++ b/docs/reference/components/control/signed-in.mdx
@@ -9,7 +9,7 @@ sdk: astro, chrome-extension, expo, nextjs, nuxt, react, react-router, remix, ta
The `` component offers authentication checks as a cross-cutting concern. Any children components wrapped by a `` component will be rendered only if there's a user with an active session signed in your application.
> [!CAUTION]
-> This component only **visually hides** its children when the current user is not authenticated. The contents of its children remain accessible via the browser's source code even if the user fails the authentication check. Do not use this component to hide sensitive information that should be completely inaccessible to unauthorized users. For truly sensitive data, perform [authorization checks](/docs/guides/secure/authorization-checks) on the server before sending the data to the client.
+> This component only **visually hides** its children when the current user is not authenticated. The contents of its children remain accessible via the browser's source code even if the user fails the authentication check. Do not use this component to hide sensitive information that should be completely inaccessible to unauthorized users. For truly sensitive data, perform [authorization checks](!authorization-check) on the server before sending the data to the client.
## Example
diff --git a/docs/reference/express/get-auth.mdx b/docs/reference/express/get-auth.mdx
index 66cec00ef8..fcee613720 100644
--- a/docs/reference/express/get-auth.mdx
+++ b/docs/reference/express/get-auth.mdx
@@ -84,7 +84,7 @@ app.listen(PORT, () => {
})
```
-For more examples on how to use `getAuth()` to perform authorization checks, see the [dedicated guide](/docs/guides/secure/authorization-checks).
+For more examples on how to use `getAuth()` to perform [authorization checks](!authorization-check), see the [dedicated guide](/docs/guides/secure/authorization-checks).
### Example: Protect a route based on token type
diff --git a/docs/reference/fastify/get-auth.mdx b/docs/reference/fastify/get-auth.mdx
index 7ff8c0a6ae..fa6056bd46 100644
--- a/docs/reference/fastify/get-auth.mdx
+++ b/docs/reference/fastify/get-auth.mdx
@@ -29,7 +29,7 @@ The `getAuth()` helper retrieves the current user's authentication state from th
-For examples on how to use `getAuth()` to perform authorization checks, see the [dedicated guide](/docs/guides/secure/authorization-checks).
+For examples on how to use `getAuth()` to perform [authorization checks](!authorization-check), see the [dedicated guide](/docs/guides/secure/authorization-checks).
### Example: Protect a route based on token type
diff --git a/docs/reference/javascript/session.mdx b/docs/reference/javascript/session.mdx
index 5c805fcdbb..08a8f3f1b6 100644
--- a/docs/reference/javascript/session.mdx
+++ b/docs/reference/javascript/session.mdx
@@ -201,7 +201,7 @@ await clerk.session.attemptSecondFactorVerification({
### `checkAuthorization()`
-Checks if the user is [authorized for the specified role or permission](/docs/guides/secure/authorization-checks) or requires the user to [reverify their credentials](/docs/guides/secure/reverification) if their last verification is older than allowed.
+Checks if the user is [authorized for the specified role, permission, feature, or plan](/docs/guides/secure/authorization-checks) or requires the user to [reverify their credentials](/docs/guides/secure/reverification) if their last verification is older than allowed.
```ts
function checkAuthorization(param: CheckAuthorizationParams): boolean
@@ -210,20 +210,52 @@ function checkAuthorization(param: CheckAuthorizationParams): boolean
#### `CheckAuthorizationParams`
```typescript
-type CheckAuthorizationParams =
+type WithReverification = T & {
+ /**
+ * The reverification configuration to check for. This feature is currently in public beta. **It is not recommended for production use.**
+ */
+ reverification?: ReverificationConfig
+}
+
+type CheckAuthorizationParams = WithReverification<
| {
- role: ClerkDefaultRole | CustomRole
+ /**
+ * The [role](https://clerk.com/docs/guides/organizations/roles-and-permissions) to check for.
+ */
+ role: OrganizationCustomRoleKey
+ /**
+ * The [permission](https://clerk.com/docs/guides/organizations/roles-and-permissions) to check for.
+ */
permission?: never
+ /**
+ * The [feature](https://clerk.com/docs/guides/billing/overview) to check for.
+ */
+ feature?: never
+ /**
+ * The [plan](https://clerk.com/docs/guides/billing/overview) to check for.
+ */
+ plan?: never
+ }
+ | {
+ role?: never
+ permission: OrganizationPermissionKey
+ feature?: never
+ plan?: never
}
| {
role?: never
- permission: ClerkDefaultPermission | CustomPermission
+ permission?: never
+ feature: Autocomplete<`user:${string}` | `org:${string}`>
+ plan?: never
}
| {
role?: never
permission?: never
- reverification: ReverficationConfig
+ feature?: never
+ plan: Autocomplete<`user:${string}` | `org:${string}`>
}
+ | { role?: never; permission?: never; feature?: never; plan?: never }
+>
```
@@ -241,6 +273,20 @@ type CheckAuthorizationParams =
---
+ - `feature`
+ - `string`
+
+ Accepts [feature](/docs/guides/secure/features) key.
+
+ ---
+
+ - `plan`
+ - `string`
+
+ Accepts [plan](/docs/guides/billing/overview) key.
+
+ ---
+
- `reverification?`
- [ReverificationConfig](/docs/reference/backend/types/auth-object#reverification-config)