Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 17 additions & 13 deletions docs/_partials/billing/checking-permission-using-has-function.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

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

const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })

if (!hasPremiumAccessManage)
return (
Expand Down Expand Up @@ -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) {
Expand All @@ -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 (
Expand All @@ -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' }),
)
</script>

<template>
Expand All @@ -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' }),
)
</script>

<template>
Expand All @@ -135,7 +139,7 @@

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

const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })

if (!hasPremiumAccessManage)
return (
Expand All @@ -161,7 +165,7 @@

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

const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })

if (!hasPremiumAccessManage)
return (
Expand All @@ -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() {
Expand Down Expand Up @@ -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' }),
}
},
})
Expand Down Expand Up @@ -240,7 +244,7 @@

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

const hasPremiumAccessManage = has({ permission: 'premium_access:manage' })
const hasPremiumAccessManage = has({ permission: 'org:premium_access:manage' })

if (!hasPremiumAccessManage)
return (
Expand All @@ -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')
Expand All @@ -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')
Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions docs/_tooltips/authorization-check.mdx
Original file line number Diff line number Diff line change
@@ -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).
17 changes: 9 additions & 8 deletions docs/guides/billing/for-b2b.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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:

Expand All @@ -48,11 +48,12 @@ You can create a pricing page by using the [`<PricingTable />`](/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 [`<Protect>`](/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 [`<Protect>`](/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:<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.
The `has()` method is available for any JavaScript-based framework, while `<Protect>` is a component, and therefore, is only available for React-based frameworks.

The `has()` method is available for any JavaScript framework, while `<Protect>` 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:<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_.

### Example: Using `has()`

Expand All @@ -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)?
Expand All @@ -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:<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.
>
> 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_.

<Tabs items={[ "Plan", "Feature", "Permission"]}>
<Tab>
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/billing/for-b2c.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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).

<Tabs items={[ "Plan", "Feature"]}>
<Tab>
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/billing/free-trials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Let users try paid features before subscribing

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This guide will walk you through how to build a custom flow that handles user im

<Tabs items={["Next.js", "Expo"]}>
<Tab>
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.

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/development/sdk-development/backend-only.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading