Verify the active user's permissions in an organization
In general, you should always verify whether or not a user is authorized to access sensitive information, important content, or exclusive features. The most secure way to implement authorization is by checking the active user's role or permissions. It's recommended to use permission-based authorization over role-based authorization, as it's more flexible, easier to manage, and more secure.
Clerk enables two broad approaches to role and permissions-based authorization:
- If you would like to immediately prevent unauthorized users from accessing content, you can:
- Use the
<Protect>
component to prevent content from rendering if the active user is unauthorized. - Call
auth.protect()
to throw a404
error if the active user is unauthorized.
- Use the
- If you would like more control over the response when a user is unauthorized, you can:
- Call the
has()
helper, which returnsfalse
if the active user lacks the role or permissions you're checking for. You can choose how your app responds instead of immediately preventing content from rendering or throwing an error.
- Call the
Authorization in Client Components
The following examples work for both SSR and CSR.
The following example uses the <Protect>
component to only render the form for users with the correct permission. The example uses the fallback
prop to render a different UI if the user is not authorized.
'use client'
import { Protect } from '@clerk/nextjs'
export default function SettingsForm() {
return (
<Protect
permission="org:team_settings:manage"
fallback={<p>You are not allowed to see this section.</p>}
>
<form>{/* Add UI for managing team settings */}</form>
</Protect>
)
}
The following example uses has()
to inspect a user's permissions granularly. If the user doesn't have the permission, has()
returns false
, causing the component to return null
instead of rendering its children.
'use client'
import { useAuth } from '@clerk/nextjs'
export default function SettingsForm() {
const { has } = useAuth()
if (!has) return null
// Check if the user is authorized
const canManageSettings = has({ permission: 'org:team_settings:manage' })
// If has() returns false, the user does not have the correct permissions
// You can choose how your app responds. This example returns null.
if (!canManageSettings) return null
// If the user is both authenticated and authorized, move forward with your logic
return <form>{/* Add UI for managing team settings */}</form>
}
The following example uses the <Protect>
component to only render the layout for users with the correct permission. If the user is not authorized, the component will not render its children.
import type { PropsWithChildren } from 'react'
import { Protect } from '@clerk/nextjs'
export default function SettingsLayout(props: PropsWithChildren) {
return <Protect permission="org:team_settings:read">{props.children}</Protect>
}
The following example uses has()
to inspect a user's permissions granularly. If the user doesn't have the correct permission, has()
returns false
, causing the component to return null
instead of rendering its children.
import type { PropsWithChildren } from 'react'
import { auth } from '@clerk/nextjs/server'
export default async function SettingsLayout(props: PropsWithChildren) {
const { has } = await auth()
// Check if the user is authorized
const canAccessSettings = has({ permission: 'org:team_settings:read' })
// If has() returns false, the user does not have the correct permissions
// You can choose how your app responds. This example returns null.
if (!canAccessSettings) return null
return props.children
}
The following example uses auth.protect()
to protect a RSC from unauthenticated and unauthorized access.
- If the user is not authenticated,
auth.protect()
will redirect the user to the sign-in route. - If the user is authenticated but is not authorized (as in, does not have the
org:team_settings:read
permission),auth.protect()
will throw a404
error. - If the user is both authenticated and authorized,
auth.protect()
will return the user'suserId
.
import { auth } from '@clerk/nextjs/server'
export default async function Page() {
const { userId } = await auth.protect({ permission: 'org:team_settings:read' })
return <p>{userId} is authorized to access this page.</p>
}
The following example uses has()
to inspect a user's permissions granularly in a Next.js Server Action. If the user doesn't have the correct permission, has()
returns false
, causing the Server Action to return a 403
error.
import { auth } from '@clerk/nextjs/server'
export default async function ExampleServerComponent() {
async function myServerAction(formData: FormData) {
'use server'
const { has } = await auth()
// Check if the user is authorized
const canManage = has({ permission: 'org:team_settings:manage' })
// If has() returns false, the user does not have the correct permissions
// You can choose how your app responds. This example returns a 403 error.
if (!canManage)
return Response.json({ error: 'User does not have the correct permissions' }, { status: 403 })
// If the user is both authenticated and authorized, move forward with your logic
return users.getTeams(userId)
}
return (
<form action={myServerAction}>
{/* Add UI for managing team settings */}
<button type="submit">Submit</button>
</form>
)
}
The following example demonstrates how to use has()
in a Next.js Route Handler.
The example:
- uses the
userId
returned fromauth()
to check if the user is signed in. If the user is not authenticated, the Route Handler will return a401
error. - uses
has()
to check if the user has the correct permission. If the user is not authorized,has()
will return false, causing the Route Handler to return a403
error.
import { auth } from '@clerk/nextjs/server'
export const GET = async () => {
const { userId, has } = await auth()
// Check if the user is authenticated
if (!userId) {
return Response.json({ error: 'User is not signed in' }, { status: 401 })
}
// Check if the user is authorized
const canRead = has({ permission: 'org:team_settings:read' })
// If has() returns false, the user does not have the correct permissions
// You can choose how your app responds. This example returns a 403 error.
if (!canRead)
return Response.json({ error: 'User does not have the correct permissions' }, { status: 403 })
// If the user is both authenticated and authorized, move forward with your logic
return users.getTeams(userId)
}
The following example uses auth.protect()
to protect a Next.js Route Handler from unauthenticated and unauthorized access.
- If the user is not authenticated nor authorized (as in, does not have the
org:team_settings:manage
permission),auth.protect()
will throw a404
error. - If the user is both authenticated and authorized,
auth.protect()
will return the user'suserId
.
import { auth } from '@clerk/nextjs/server'
export const GET = async () => {
const { userId } = await auth.protect({
permission: 'org:team_settings:manage',
})
return Response.json({ userId })
}
Use the getAuth()
helper to access the has()
helper in a Next.js Pages Router application.
The following example:
- uses the
userId
returned fromgetAuth()
to check if the user is signed in. If the user is not authenticated, the route will return a401
error. - uses
has()
to check if the user has the correct permission. If the user is not authorized,has()
will return false, causing the route to return a403
error.
import { getAuth } from '@clerk/nextjs/server'
export default async function handler(req: NextApiRequest) {
const { userId, has } = await getAuth(req)
// Check if the user is authenticated
if (!userId) return res.status(401)
// Check if the user is authorized
const canRead = has({ permission: 'org:team_settings:read' })
// If has() returns false, the user does not have the correct permissions
// You can choose how your app responds. This example returns a 403 error.
if (!canRead) return res.status(403)
// If the user is both authenticated and authorized, move forward with your logic
return users.getTeams(userId)
}
Authorization in Remix Loaders
The following example uses the has()
helper to check if the user has the correct permission. If the user is not authorized, has()
will return false, causing the loader to redirect the user to the /request-access
route.
export const loader: LoaderFunction = async (args) => {
const { has } = await getAuth(args)
if (has({ permission: 'org:team_settings:manage' }) === false) {
return redirect('/request-access')
}
return {}
}
export default function Settings() {
return (
<div>
<h1>Settings Page</h1>
</div>
)
}
Authorization in JavaScript
If you are not using React or any of the meta-frameworks we support, you can use the Clerk JavaScript SDK. The following example demonstrates how to use the checkAuthorization()
method to check if a user is authorized.
import { Clerk } from '@clerk/clerk-js'
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(clerkPubKey)
await clerk.load()
// Check if the user is authenticated
if (clerk.user) {
// Check if the user is authorized
const canManageSettings = clerk.session.checkAuthorization({
permission: 'org:team_settings:manage',
})
}
You can pass a role
the same way you can pass a permission
in all the examples above.
The following example uses <Protect>
's condition
prop to conditionally render its children if the user has the correct role.
import { Protect } from '@clerk/nextjs'
export default function Page() {
return (
<Protect
condition={(has) => has({ role: 'org:admin' }) || has({ role: 'org:billing_manager' })}
>
<p>Admin settings</p>
</Protect>
)
}
The following example uses auth.protect()
to protect a RSC from unauthenticated and unauthorized access.
- If the user is not authenticated,
auth.protect()
will redirect the user to the sign-in route. - If the user is authenticated but is not authorized (as in, does not have the
org:admin
ororg:billing_manager
role),auth.protect()
will throw a404
error. - If the user is both authenticated and authorized,
auth.protect()
will return the user'suserId
.
import { auth } from '@clerk/nextjs/server'
export default async function Page() {
const { userId } = await auth.protect(
(has) => has({ role: 'org:admin' }) || has({ role: 'org:billing_manager' }),
)
return <p>{userId} is authorized to access this page.</p>
}
The following example uses has()
to inspect a user's roles granularly. If the user doesn't have the correct role, has()
returns false
, causing the component to return null
instead of rendering its children.
'use client'
import { useAuth } from '@clerk/nextjs'
export function SettingsForm() {
const { has } = useAuth()
// Check if the user is authorized
const canAccessSettings = has({ role: 'org:admin' }) || has({ role: 'org:billing_manager' })
// If has() returns false, the user does not have the correct permissions
// You can choose how your app responds. This example returns null.
if (!canAccessSettings) return null
// If the user is both authenticated and authorized, move forward with your logic
return <form>{/* Add UI for team settings */}</form>
}
Add custom types for roles and permissions
In order to enhance typesafety in your project, you can define a global ClerkAuthorization
interface, which defines the acceptable values for roles and permissions.
In the example below, ClerkAuthorization
is defined with the default roles that Clerk provides.
export {}
declare global {
interface ClerkAuthorization {
permission: ''
role: 'org:admin' | 'org:member'
}
}
Because Clerk supports custom roles and permissions, you can modify ClerkAuthorization
to align with the roles and permissions configured in your Clerk application. See how in the example below, the default Clerk roles org:admin
and org:member
are replaced with custom roles org:super_admin
, org:teacher
, and org:student
.
export {}
declare global {
interface ClerkAuthorization {
permission: 'org:quiz:create' | 'org:quiz:grade' | 'org:quiz:read' | 'org:quiz:fill'
role: 'org:super_admin' | 'org:teacher' | 'org:student'
}
}
Feedback
Last updated on