From d3ff29f4b70820fb24094ac7d5bfffbcf30ae12d Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 20 Dec 2024 17:23:56 -0500 Subject: [PATCH] done? --- docs/_partials/has-warning.mdx | 2 + docs/customization/user-button.mdx | 2 +- docs/organizations/roles-permissions.mdx | 4 +- docs/organizations/verified-domains.mdx | 2 +- docs/references/astro/clerk-middleware.mdx | 6 +- docs/references/backend/types/auth-object.mdx | 13 ++- docs/references/express/overview.mdx | 2 +- docs/references/ios/get-token.mdx | 2 +- docs/references/javascript/session.mdx | 2 +- docs/references/nextjs/auth.mdx | 16 +-- docs/references/nextjs/clerk-middleware.mdx | 101 +++++++++--------- docs/references/nextjs/get-auth.mdx | 25 +---- docs/references/nextjs/read-session-data.mdx | 1 + 13 files changed, 85 insertions(+), 93 deletions(-) create mode 100644 docs/_partials/has-warning.mdx diff --git a/docs/_partials/has-warning.mdx b/docs/_partials/has-warning.mdx new file mode 100644 index 0000000000..40f9dda9f1 --- /dev/null +++ b/docs/_partials/has-warning.mdx @@ -0,0 +1,2 @@ +> [!WARNING] +> When using `has()` **on the server-side** to check permissions, it only works with **custom permissions**, as [system permissions](/docs/organizations/roles-permissions#system-permissions) are not included in `auth.sessionClaims.orgs_permissions`. To check system permissions, verify the user's role instead. diff --git a/docs/customization/user-button.mdx b/docs/customization/user-button.mdx index 5daee03814..620edfa042 100644 --- a/docs/customization/user-button.mdx +++ b/docs/customization/user-button.mdx @@ -457,7 +457,7 @@ With the above example, the `` menu items will be in the following ## Conditionally render menu items -To conditionally render menu items based on a user's role or permissions, you can use the [`has()`](/docs/references/backend/types/auth-object#has) helper function: +To conditionally render menu items based on a user's role or custom permissions, you can use the [`has()`](/docs/references/backend/types/auth-object#has) helper function: diff --git a/docs/organizations/roles-permissions.mdx b/docs/organizations/roles-permissions.mdx index fc81626742..8d2355b251 100644 --- a/docs/organizations/roles-permissions.mdx +++ b/docs/organizations/roles-permissions.mdx @@ -49,7 +49,7 @@ Permissions grant users privileged access to resources and operations, like crea Clerk has a set of system permissions that power [Clerk's Frontend API](/docs/reference/frontend-api){{ target: '_blank' }} and [organization-related Clerk components](/docs/components/overview#organization-components). They are a baseline set of permissions that Clerk needs to operate functionally. -Clerk’s system permissions consist of the following: +Clerk's system permissions consist of the following: - Manage Organization (`org:sys_profile:manage`) - Delete Organization (`org:sys_profile:delete`) @@ -61,7 +61,7 @@ Clerk’s system permissions consist of the following: You can assign these system permissions to any role. > [!WARNING] -> System permissions are not included in session claims. To do permission-checks on the server-side, you must [create custom permissions](/docs/organizations/create-roles-permissions). +> System permissions are not included in [session claims](/docs/backend-requests/resources/session-tokens#default-session-claims). To do permission-checks on the server-side, you must [create custom permissions](/docs/organizations/create-roles-permissions). ### Custom permissions diff --git a/docs/organizations/verified-domains.mdx b/docs/organizations/verified-domains.mdx index bc45a9d06d..9dfac6a625 100644 --- a/docs/organizations/verified-domains.mdx +++ b/docs/organizations/verified-domains.mdx @@ -33,7 +33,7 @@ Once a domain is added and verified in your organization, the user will have the ## Adding and verifying domains -Domains can be added and verified under an organization by any user with the `org:sys_domains:manage` permission. By default, admins have this permission. The easiest way to add and verify domains is by using Clerk's \[``] component. In the **General** tab, there will be a **Verified domains** section. +Domains can be added and verified under an organization by any user with the `org:sys_domains:manage` permission. By default, admins have this permission. The easiest way to add and verify domains is by using Clerk's [``](/docs/components/organization/organization-switcher) component. In the **General** tab, there will be a **Verified domains** section. Domains can be verified through an email verification code sent to an email that matches the domain. If the user adding the domain already has a verified email using that domain in their account, the domain will be automatically verified. diff --git a/docs/references/astro/clerk-middleware.mdx b/docs/references/astro/clerk-middleware.mdx index ce8b17803a..e5d17f2e5b 100644 --- a/docs/references/astro/clerk-middleware.mdx +++ b/docs/references/astro/clerk-middleware.mdx @@ -56,7 +56,7 @@ export const onRequest = clerkMiddleware((auth, context) => { ### Protect routes based on user authorization status -To protect routes based on user authorization status, use [`auth().has()`](/docs/references/backend/types/auth-object#has){{ target: '_blank' }} to check if the user has the required roles or permissions. +To protect routes based on user authorization status, use [`auth().has()`](/docs/references/backend/types/auth-object#has){{ target: '_blank' }} to check if the user has the required roles or custom permissions. ```tsx {{ filename: 'src/middleware.ts' }} import { clerkMiddleware, createRouteMatcher } from '@clerk/astro/server' @@ -68,8 +68,8 @@ export const onRequest = clerkMiddleware((auth, context) => { // Restrict admin routes to users with specific permissions if ( - (isProtectedRoute(context.request) && !has({ permission: 'org:sys_memberships:manage' })) || - !has({ permission: 'org:sys_domains_manage' }) + (isProtectedRoute(context.request) && !has({ permission: 'org:admin:example1' })) || + !has({ permission: 'org:admin:example2' }) ) { // Add logic to run if the user does not have the required permissions; for example, redirecting to the sign-in page return redirectToSignIn() diff --git a/docs/references/backend/types/auth-object.mdx b/docs/references/backend/types/auth-object.mdx index 95f2e9970d..85f0a68ed8 100644 --- a/docs/references/backend/types/auth-object.mdx +++ b/docs/references/backend/types/auth-object.mdx @@ -82,7 +82,7 @@ The `Auth` object is available on the `request` object in server contexts. Some - [`has()`](#has) - (isAuthorizedParams: [CheckAuthorizationParamsWithCustomPermissions](#check-authorization-params-with-custom-permissions)) => boolean - A function that returns a boolean based on the permission or role provided as parameter. Can be used for authorization. + A function that checks if the user has an organization role or custom permission. --- @@ -96,7 +96,7 @@ The `Auth` object is available on the `request` object in server contexts. Some - [`getToken()`](#get-token) - [`ServerGetToken`](#server-get-token) - A function that returns a promise that resolves to the current user's session token. Can also be used to retrieve a custom JWT template. + A function that gets the current user's [session token](/docs/backend-requests/resources/session-tokens) or a [custom JWT template](/docs/backend-requests/making/jwt-templates). --- @@ -208,16 +208,15 @@ The `ReverificationConfig` type has the following properties: You can use `has()` to check if a user is authorized to access a component. -> [!WARNING] -> When using `has()` on the **server-side**, it checks the `orgs_permissions` property in the user's `auth.sessionClaims`. [**System** permissions](/docs/organizations/roles-permissions#system-permissions) aren't included in session claims and therefore, can't be used to perform authorization checks. **You must use custom permissions instead**. + -In the following example, `has()` is used to check if the user has the `org:team_settings:manage` permission. If the user does not have the permission, `null` is returned and the page is not rendered. +In the following example, `has()` is used to check if the user has the `org:team_settings:manage` permission. If the user does not have the permission, `null` is returned and the page is not rendered. This example is written for Next.js App Router, but it can be adapted to other frameworks by using the appropriate method for accessing the `Auth` object. ```tsx {{ filename: 'app/page.tsx' }} import { auth } from '@clerk/nextjs/server' export default async function Page() { - const { has } = await auth() // Accessing the `auth` object will depend on the framework you're using + const { has } = await auth() const canManage = has({ permission: 'org:team_settings:manage' }) @@ -238,7 +237,7 @@ You can use `has()` to check if a user has verified their credentials within a c ### `getToken()` -`getToken()` retrieves the user's session token or a [custom JWT template](/docs/backend-requests/making/jwt-templates). It has the following signature: +`getToken()` retrieves the current user's [session token](/docs/backend-requests/resources/session-tokens) or a [custom JWT template](/docs/backend-requests/making/jwt-templates). ```typescript const getToken: ServerGetToken diff --git a/docs/references/express/overview.mdx b/docs/references/express/overview.mdx index 6679311fa1..52fcc449fd 100644 --- a/docs/references/express/overview.mdx +++ b/docs/references/express/overview.mdx @@ -102,7 +102,7 @@ const hasPermission = (req, res, next) => { const auth = getAuth(req) // Handle if the user is not authorized - if (!auth.has({ permission: 'org:admin:testpermission' })) { + if (!auth.has({ permission: 'org:admin:example' })) { return res.status(403).send('Forbidden') } diff --git a/docs/references/ios/get-token.mdx b/docs/references/ios/get-token.mdx index 13e3eb3528..7c7f631894 100644 --- a/docs/references/ios/get-token.mdx +++ b/docs/references/ios/get-token.mdx @@ -3,7 +3,7 @@ title: '`getToken()`' description: Use Clerk's iOS SDK to retrieve a token for a JWT template that is defined in the Clerk Dashboard. --- -Retrieves the user's session token for the [default Clerk token](/docs/backend-requests/resources/session-tokens) or the given [JWT template](/docs/backend-requests/making/jwt-templates). +Retrieves the user's [session token](/docs/backend-requests/resources/session-tokens) or a [custom JWT template](/docs/backend-requests/making/jwt-templates). This method uses a cache so a network request will only be made if the token in memory has expired. The TTL for a Clerk token is one minute. diff --git a/docs/references/javascript/session.mdx b/docs/references/javascript/session.mdx index b722862a20..2011e03a06 100644 --- a/docs/references/javascript/session.mdx +++ b/docs/references/javascript/session.mdx @@ -137,7 +137,7 @@ function touch(): Promise ### `getToken()` -Retrieves the user's session token for the default Clerk token or the given template. +Retrieves the current user's [session token](/docs/backend-requests/resources/session-tokens) or a [custom JWT template](/docs/backend-requests/making/jwt-templates). This method uses a cache so a network request will only be made if the token in memory has expired. The TTL for a Clerk token is one minute. diff --git a/docs/references/nextjs/auth.mdx b/docs/references/nextjs/auth.mdx index 2ed0b3ee47..000e1468f8 100644 --- a/docs/references/nextjs/auth.mdx +++ b/docs/references/nextjs/auth.mdx @@ -47,7 +47,7 @@ The following table describes how `auth.protect()` behaves based on user authent - `has?` - `(isAuthorizedParams: CheckAuthorizationParamsWithCustomPermissions) => boolean` - A function that returns a boolean based on the permission or role provided as parameter. Can be used for authorization. See [the dedicated `has()` section](/docs/references/backend/types/auth-object#has) for more information. + A function that checks if the user has an organization role or custom permission. See the [reference](/docs/references/backend/types/auth-object#has) for more information. --- @@ -100,7 +100,7 @@ export default async function Page() { } ``` -## Use `auth()` to retrieve `userId` +## Use `auth()` to protect pages You can use `auth()` to check if a `userId` exists. If it does not, that means there is no user signed in. You can use this information to protect pages, as shown in the following example: @@ -116,6 +116,14 @@ export default async function Page() { } ``` +## Use `auth()` in API routes + +See detailed examples in the [dedicated guide](/docs/references/nextjs/read-session-data). + +## Use `auth()` to check roles and permissions + +You can use `auth()` to check if a user is authorized to access certain parts of your application or even entire routes. See detailed examples in the [dedicated guide](/docs/organizations/verify-user-permissions). + ## Use `auth()` for data fetching When using a Clerk integration, or if you need to send a JWT along to a server, you can use the `getToken()` function that is returned by `auth()`. @@ -143,7 +151,3 @@ export async function GET() { } } ``` - -## Use `auth()` to protect your app - -You can protect certain parts of your application or even entire routes based on a user's authentication and/or authorization status. See detailed examples in the [dedicated guide](/docs/organizations/verify-user-permissions). diff --git a/docs/references/nextjs/clerk-middleware.mdx b/docs/references/nextjs/clerk-middleware.mdx index 20e894831e..2411a67eb9 100644 --- a/docs/references/nextjs/clerk-middleware.mdx +++ b/docs/references/nextjs/clerk-middleware.mdx @@ -112,59 +112,65 @@ There are two methods that you can use: - Use [`auth.protect()`](/docs/references/nextjs/auth#protect) if you want Clerk to return a `404` if the user does not have the role or permission. - Use [`auth().has()`](/docs/references/backend/types/auth-object#has) if you want more control over what your app does based on the authorization status. - - ```tsx {{ filename: 'middleware.ts' }} - import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' - - const isProtectedRoute = createRouteMatcher(['/admin(.*)']) + + + ```tsx {{ filename: 'middleware.ts' }} + import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' + + const isProtectedRoute = createRouteMatcher(['/admin(.*)']) + + export default clerkMiddleware(async (auth, req) => { + // Restrict admin routes to users with specific permissions + if (isProtectedRoute(req)) { + await auth.protect((has) => { + return has({ permission: 'org:admin:example1' }) || has({ permission: 'org:admin:example2' }) + }) + } + }) - export default clerkMiddleware(async (auth, req) => { - // Restrict admin routes to users with specific permissions - if (isProtectedRoute(req)) { - await auth.protect((has) => { - return has({ permission: 'org:admin:example1' }) || has({ permission: 'org:admin:example2' }) - }) + export const config = { + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', + // Always run for API routes + '/(api|trpc)(.*)', + ], } - }) + ``` + - export const config = { - matcher: [ - // Skip Next.js internals and all static files, unless found in search params - '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', - // Always run for API routes - '/(api|trpc)(.*)', - ], - } - ``` + + - ```tsx {{ filename: 'middleware.ts' }} - import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' + ```tsx {{ filename: 'middleware.ts' }} + import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' - const isProtectedRoute = createRouteMatcher(['/admin(.*)']) + const isProtectedRoute = createRouteMatcher(['/admin(.*)']) - export default clerkMiddleware(async (auth, req) => { - const { has, redirectToSignIn } = await auth() - // Restrict admin routes to users with specific permissions - if ( - (isProtectedRoute(req) && !has({ permission: 'org:sys_memberships:manage' })) || - !has({ permission: 'org:sys_domains_manage' }) - ) { - // Add logic to run if the user does not have the required permissions + export default clerkMiddleware(async (auth, req) => { + const { has, redirectToSignIn } = await auth() + // Restrict admin routes to users with specific permissions + if ( + (isProtectedRoute(req) && !has({ permission: 'org:admin:example1' })) || + !has({ permission: 'org:admin:example2' }) + ) { + // Add logic to run if the user does not have the required permissions - return redirectToSignIn() - } - }) + return redirectToSignIn() + } + }) - export const config = { - matcher: [ - // Skip Next.js internals and all static files, unless found in search params - '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', - // Always run for API routes - '/(api|trpc)(.*)', - ], - } - ``` - + export const config = { + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', + // Always run for API routes + '/(api|trpc)(.*)', + ], + } + ``` + + ## Protect multiple groups of routes @@ -183,10 +189,7 @@ export default clerkMiddleware(async (auth, req) => { // Restrict admin routes to users with specific permissions if (isTenantAdminRoute(req)) { await auth.protect((has) => { - return ( - has({ permission: 'org:sys_memberships:manage' }) || - has({ permission: 'org:sys_domains_manage' }) - ) + return has({ permission: 'org:admin:example1' }) || has({ permission: 'org:admin:example2' }) }) } // Restrict organization routes to signed in users diff --git a/docs/references/nextjs/get-auth.mdx b/docs/references/nextjs/get-auth.mdx index 363183a831..1b96d951ca 100644 --- a/docs/references/nextjs/get-auth.mdx +++ b/docs/references/nextjs/get-auth.mdx @@ -26,30 +26,13 @@ The `getAuth()` helper retrieves authentication state from the request object. ## Returns -`getAuth()` returns the [`Auth`](/docs/references/backend/types/auth-object) object. +`getAuth()` returns the `Auth` object. See the [`Auth` reference](/docs/references/backend/types/auth-object) for more information. ## Usage -### Basic usage - -The following example demonstrates how to use `getAuth()` to retrieve authentication information in an API route. - -```tsx {{ filename: 'app/api/example/route.ts' }} -import { getAuth } from '@clerk/nextjs/server' -import type { NextApiRequest, NextApiResponse } from 'next' - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const { userId } = getAuth(req) - - // Add logic that retrieves the data for the API route - - return res.status(200).json({ userId: userId }) -} -``` - ### Protect API routes -It is important to protect your API routes to ensure that only authenticated users can access them. You can do this by checking if the `userId` is present in the `getAuth()` response, like in the following example: +The following example demonstrates how to protect an API route by checking if the `userId` is present in the `getAuth()` response. ```tsx {{ filename: 'app/api/example/route.ts' }} import { getAuth } from '@clerk/nextjs/server' @@ -70,7 +53,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ### Usage with `getToken()` -`getAuth()` returns `getToken()`, which is a method that returns the current user's session token. You can also use this function to retrieve a custom JWT template, like in the following example: +`getAuth()` returns [`getToken()`](/docs/references/backend/types/auth-object#get-token), which is a method that returns the current user's session token or a custom JWT template. ```tsx {{ filename: 'app/api/example/route.ts' }} import { getAuth } from '@clerk/nextjs/server' @@ -90,7 +73,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ### Usage with `clerkClient` -`clerkClient` is used to access the [Backend SDK](/docs/references/backend/overview), which exposes Clerk's Backend API resources. You can use `getAuth()` to pass authentication information that many of the Backend SDK methods require, like in the following example: +`clerkClient` is used to access the [Backend SDK](/docs/references/backend/overview), which exposes Clerk's Backend API resources. You can use `getAuth()` to pass authentication information that many of the Backend SDK methods require, like the user's ID. ```tsx {{ filename: 'app/api/example/route.ts' }} import { clerkClient, getAuth } from '@clerk/nextjs/server' diff --git a/docs/references/nextjs/read-session-data.mdx b/docs/references/nextjs/read-session-data.mdx index b0a89184f3..8fceb5fe56 100644 --- a/docs/references/nextjs/read-session-data.mdx +++ b/docs/references/nextjs/read-session-data.mdx @@ -53,6 +53,7 @@ Under the hood, `currentUser()` uses the [`clerkClient`](/docs/references/backen // Get the userId from auth() -- if null, the user is not signed in const { userId } = await auth() + // Protect the route by checking if the user is signed in if (!userId) { return new NextResponse('Unauthorized', { status: 401 }) }