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.
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.
- 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.
Authorization in Client Components
The examples below work for both SSR and CSR. Examples are written for Next.js App Router but they are supported by any React meta framework, such as Remix or Gatsby.
The following example uses Clerk's <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 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>
)
}
Use the useAuth()
hook to access the has()
helper in Client Components.
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 function SettingsForm(){
const { has } = useAuth();
const canManageSettings = has({ permission: "org:team_settings:manage" });
if(!canManageSettings) return null;
return (
<form>
{/* Add UI for managing team settings */}
</form>
)
}
The following example uses Clerk's <Protect>
component to only render the form 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 function SettingsLayout(props: PropsWithChildren){
const { has } = auth();
const canAccessSettings = has({ permission: "org:team_settings:read" });
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 type { PropsWithChildren } from "react";
import { auth } from "@clerk/nextjs/server";
export default function SettingsLayout(props: PropsWithChildren){
const { userId } = auth().protect({ permission: "org:team_settings:read" })
return props.children
}
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 function ExampleServerComponent() {
async function myAction(formData: FormData) {
'use server'
const { has } = auth();
const canManage = has({ permission:"org:team_settings:manage" });
// If has() returns false, the user does not have the correct permissions
if(!canManage) return Response.json({ error: "User does not have the correct permissions" }, { status: 403 });
// Add logic for managing team settings
}
return (
<form action={myAction}>
{/* 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 { useAuth } from "@clerk/nextjs";
export const GET = () => {
const { userId, has } = auth();
if(!userId){
return Response.json({ error: "User is not signed in" }, { status: 401 })
}
const canRead = has({ permission: "org:team_settings:read" });
if(!canRead) return Response.json({ error: "User does not have the correct permissions" }, { status: 403 });
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 { useAuth } from "@clerk/nextjs";
export const POST = () => {
const { userId } = auth().protect({
permission: "org:team_settings:manage"
})
return users.createTeam(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);
if (!userId) return res.status(401);
const canRead = has({ permission: "org:team_settings:read" });
if(!canRead) return res.status(403);
return users.getTeams(userId);
}
export const loader: LoaderFunction = async args => {
const { has } = await getAuth(args);
if(has({permission: "org:team_settings:manage"})){
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';
// Initialize Clerk with your Clerk publishable key
const clerk = new Clerk('YOUR_PUBLISHABLE_KEY');
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 type { PropsWithChildren } from "react";
import { Protect } from "@clerk/nextjs";
export default function SettingsLayout(props: PropsWithChildren){
return (
<Protect
condition={has => has({role: "org:admin"}) || has({role: "org:billing_manager"})}
>
{props.children}
</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 type { PropsWithChildren } from "react";
import { auth } from "@clerk/nextjs/server";
export default function SettingsLayout(props: PropsWithChildren){
const { userId } = auth().protect(has => has({ role: "org:admin" }) || has({ role: "org:billing_manager" }))
return props.children
}
Use the useAuth()
hook to access the has()
helper in Client Components.
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();
const canAccessSettings = has({ role: "org:admin" }) || has({ role: "org:billing_manager" })
if(!canAccessSettings) return null;
return (
<form>
{/* Add UI for team settings */}
</form>
)
}
How to add 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';
}
}