Session tokens
When a user is authenticated in your application, Clerk generates a short-lived session token that you can use to authenticate requests to your backend. This token is a JSON Web Token (JWT) that contains information about the user and their session.
Read more about Clerk session tokens and how they work in the guide on how Clerk works.
Default claims
Every generated token has default claims that cannot be overridden by templates. Clerk's default claims include:
Organization claim
The o claim, or organization claim, is only included if the user is part of an organization and that organization is . Its value is an object that contains the following properties:
Decode o.fpm manually
For those that need to decode the o.fpm claim manually, here is how the claim value is computed.
The o.fpm value is a list of comma-separated integers where the position of the integer corresponds to a feature in the same position in the fea claim. So, the first feature in the fea claim corresponds to the first integer in the o.fpm claim, and so on.
Each integer, when converted to binary, represents a bitmask where each bit's position corresponds to a permission in the o.per list, where 0 = not-allowed and 1 = allowed. The first bit position in the bitmask is the rightmost, least significant bit. It corresponds to the first permission in the o.per list, which is the leftmost element. This results in a sort of "reverse" mapping, seen in the example below.
Example
Consider a user with an active organization has role admin. This role has the following feature permissions: dashboard:read, dashboard:manage, teams:read. Therefore, the user's claims are as follows:
- feaclaim has value- o:dashboard,o:teams
- o.perclaim has value- manage,read
- o.fpmclaim has value- 3,2
Now, let's manually decode the o.fpm claim.
The first integer 3 represents the feature-permission mapping for the dashboard feature, since it's the first feature in the fea claim. Then, converting 3 to binary, you get 11, which means both permissions are allowed (dashboard:read and dashboard:manage).
The second integer 2 represents the feature-permission mapping for the teams feature, since it's the second feature in the fea claim. Then, converting 2 to binary, you get 10, which means the second permission is allowed (teams:read). This is because you read bitmask from right to left, so the first bit, 0, corresponds to the first permission in the o.per list (manage) and the second bit, 1, corresponds to the second permission in the o.per list (read).
Actor claim
The act (actor) claim is only included if the user is impersonating another user. It's value is an object that contains the following properties:
Every generated token has default claims that cannot be overridden by templates. Clerk's default claims include:
The following claims are only included if the user is part of an organization and that organization is :
The act (actor) claim is only included if the user is impersonating another user. It's value is an object that contains the following properties:
If you would like to add custom claims to your session token, you can customize it.
You can also create custom tokens using a JWT template.
Size limitations
Clerk stores the session token in a cookie, and most browsers limit cookie size to 4KB. Exceeding this limit will cause the cookie to not be set, which will break your app as Clerk depends on cookies to work properly.
A session token with the default session claims won't run into this issue, as this configuration produces a cookie significantly smaller than 4kb. However, this limitation becomes relevant when implementing a custom session token. In this case, it's recommended to move particularly large claims out of the token and fetch these using a separate API call from your backend.
Claims to monitor for size limits:
- user.organizations
- user.public_metadata
- user.unsafe_metadata
- org.public_metadata
- org_membership.public_metadata
If you add any of these custom claims in your token, use caution to ensure the stored data doesn't exceed the size limit. It's highly recommended to store the extra data in your own database instead of storing it in metadata in the session token. If this isn't an option, you can move particularly large claims like these out of the token and fetch them using a separate API call from your backend.
Example
It's recommended to keep the total size of custom claims in the session token under 1.2KB. The following example shows how to move particularly large claims out of the session token and fetch them using a separate API call from your backend. The limitations of this approach are that if you make this call to Clerk's Backend API frequently, you risk hitting rate limits and it's also slower than making a database query. We highly recommend storing the extra data in your own database instead of storing it in metadata in the session token.
For example, if you were storing several fields in user.public_metadata, like this:
// user.public_metadata
{
  onboardingComplete: true,
  birthday: '2000-01-01',
  country: 'Canada',
  bio: 'This is a bio -- imagine it is 6kb of written info',
}Instead of storing all of that data in the session token, and possibly exceeding the 1.2KB limit, like this:
// Custom claims in the session token
{
  "metadata": "{{user.public_metadata}}"
}You could store only the necessary data in the session token - for example, just the onboardingComplete field - like this:
// Custom claims in the session token
{
  "onboardingComplete": "{{user.public_metadata.onboardingComplete}}"
}Then, when you need to access the other metadata fields, you can fetch them using a separate API call from your backend. The following example uses the getUser() method to access the current user's Backend User object, which includes the publicMetadata field.
If your SDK isn't listed, you can use the comments in the example to help you adapt it to your SDK.
import { auth, clerkClient } from '@clerk/nextjs/server'
export async function GET() {
  // The `Auth` object gives you access to properties like `isAuthenticated` and `userId`
  // Accessing the `Auth` object differs depending on the SDK you're using
  // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object
  const { isAuthenticated, userId } = await auth()
  // Protect the route by checking if the user is signed in
  if (!isAuthenticated) {
    return new NextResponse('Unauthorized', { status: 401 })
  }
  // Initialize the JS Backend SDK
  // This varies depending on the SDK you're using
  // https://clerk.com/docs/js-backend/getting-started/quickstart
  const client = await clerkClient()
  // Use the`getUser()` method to get the Backend User object
  const user = await client.users.getUser(userId)
  // Return the Backend User object
  return NextResponse.json({ user: user }, { status: 200 })
}import { getAuth, clerkClient } from '@clerk/nextjs/server'
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  // Use `getAuth()` to access `isAuthenticated` and the user's ID
  const { isAuthenticated, userId } = getAuth(req)
  // Protect the route by checking if the user is signed in
  if (!isAuthenticated) {
    return res.status(401).json({ error: 'Unauthorized' })
  }
  // Initialize the JS Backend SDK
  const client = await clerkClient()
  // Get the user's full Backend User object
  const user = await client.users.getUser(userId)
  return res.status(200).json({ user })
}import { clerkClient } from '@clerk/astro/server'
export async function GET(context) {
  // The `Auth` object gives you access to properties like `isAuthenticated` and `userId`
  // Accessing the `Auth` object differs depending on the SDK you're using
  // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object
  const { isAuthenticated, userId } = context.locals.auth()
  // Protect the route by checking if the user is signed in
  if (!isAuthenticated) {
    return new Response('Unauthorized', { status: 401 })
  }
  // Initialize the JS Backend SDK
  // This varies depending on the SDK you're using
  // https://clerk.com/docs/js-backend/getting-started/quickstart
  // Use the `getUser()` method to get the Backend User object
  const user = await clerkClient(context).users.getUser(userId)
  // Return the Backend User object
  return new Response(JSON.stringify({ user }))
}import { createClerkClient, getAuth } from '@clerk/express'
import express from 'express'
const app = express()
const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY })
app.get('/user', async (req, res) => {
  // The `Auth` object gives you access to properties like `isAuthenticated` and `userId`
  // Accessing the `Auth` object differs depending on the SDK you're using
  // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object
  const { isAuthenticated, userId } = getAuth(req)
  // Protect the route by checking if the user is signed in
  if (!isAuthenticated) {
    res.status(401).json({ error: 'User not authenticated' })
  }
  // Initialize the JS Backend SDK
  // This varies depending on the SDK you're using
  // https://clerk.com/docs/js-backend/getting-started/quickstart
  // Use the `getUser()` method to get the Backend User object
  const user = await clerkClient.users.getUser(userId)
  // Return the Backend User object
  res.json(user)
})import { redirect } from 'react-router'
import { clerkClient, getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile'
export async function loader(args: Route.LoaderArgs) {
  // The `Auth` object gives you access to properties like `isAuthenticated` and `userId`
  // Accessing the `Auth` object differs depending on the SDK you're using
  // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object
  const { isAuthenticated, userId } = await getAuth(args)
  // Protect the route by checking if the user is signed in
  if (!isAuthenticated) {
    return redirect('/sign-in?redirect_url=' + args.request.url)
  }
  // Initialize the JS Backend SDK
  // This varies depending on the SDK you're using
  // https://clerk.com/docs/js-backend/getting-started/quickstart
  // Use the `getUser()` method to get the Backend User object
  const user = await clerkClient(args).users.getUser(userId)
  // Return the Backend User object
  return {
    user: JSON.stringify(user),
  }
}import { json } from '@tanstack/react-start'
import { createFileRoute } from '@tanstack/react-router'
import { auth, clerkClient } from '@clerk/tanstack-react-start/server'
export const ServerRoute = createFileRoute('/api/example')({
  server: {
    handlers: {
      GET: async () => {
        // The `Auth` object gives you access to properties like `isAuthenticated` and `userId`
        // Accessing the `Auth` object differs depending on the SDK you're using
        // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object
        const { isAuthenticated, userId } = await auth()
        if (!isAuthenticated) {
          return new Response('User not authenticated', {
            status: 404,
          })
        }
        // Initialize the JS Backend SDK
        // This varies depending on the SDK you're using
        // https://clerk.com/docs/js-backend/getting-started/quickstart
        // Use the `getUser()` method to get the Backend User object
        const user = await clerkClient().users.getUser(userId)
        // Return the Backend User object
        return json({ user })
      },
    },
  },
})Validate session tokens
If you're using the middleware provided by our Clerk SDKs, validating session tokens is handled automatically in every request. If you're not using the middleware, you can still use the respective helpers provided by the SDKs to validate the tokens.
To learn how to manually verify a session token, refer to the dedicated guide.
Feedback
Last updated on