useTokenClaims is a client-side React hook that decodes the JWT access token and returns its claims. This is useful for accessing custom claims, standard JWT fields, or WorkOS-specific data embedded in the token.
Usage
'use client' ;
import { useTokenClaims } from '@workos-inc/authkit-nextjs' ;
export function UserInfo () {
const { sub , org_id , role , permissions } = useTokenClaims ();
return (
< div >
< p > User ID: { sub } </ p >
< p > Organization: { org_id } </ p >
< p > Role: { role } </ p >
< p > Permissions: { permissions ?. join ( ', ' ) } </ p >
</ div >
);
}
Signature
function useTokenClaims < T = Record < string , unknown >>() : TokenClaims < T >;
type TokenClaims < T > = Partial < JWTPayload & T >;
Type parameters
T
Record<string, unknown>
default: "Record<string, unknown>"
Type definition for custom claims in your token. This allows you to add type safety for custom fields you’ve added to the JWT. interface CustomClaims {
customField : string ;
subscriptionTier : 'free' | 'pro' | 'enterprise' ;
}
const claims = useTokenClaims < CustomClaims >();
// TypeScript knows about custom fields
console . log ( claims . customField );
console . log ( claims . subscriptionTier );
Return value
Returns an object containing the decoded JWT claims. All fields are optional since the token may not exist or may not contain all claims.
Standard JWT claims
Session ID of the JWT, used to identify the session.
Issuer of the JWT. For WorkOS, this is typically your WorkOS API URL.
Subject of the JWT, representing the user ID.
Audience of the JWT, indicating the intended recipients.
Expiration time of the JWT, represented as a Unix timestamp (seconds since epoch). const { exp } = useTokenClaims ();
if ( exp ) {
const expiresAt = new Date ( exp * 1000 );
console . log ( 'Token expires at:' , expiresAt );
}
Issued at time of the JWT, represented as a Unix timestamp. const { iat } = useTokenClaims ();
if ( iat ) {
const issuedAt = new Date ( iat * 1000 );
console . log ( 'Token issued at:' , issuedAt );
}
JWT ID, a unique identifier for this specific token.
WorkOS-specific claims
Organization ID associated with the JWT.
The user’s role in the organization.
Array of all roles assigned to the user.
Array of permissions granted to the user.
Examples
Basic usage
'use client' ;
import { useTokenClaims } from '@workos-inc/authkit-nextjs' ;
export function TokenInfo () {
const claims = useTokenClaims ();
if ( Object . keys ( claims ). length === 0 ) {
return < div > No token available </ div > ;
}
return (
< div >
< h2 > Token Claims </ h2 >
< pre > { JSON . stringify ( claims , null , 2 ) } </ pre >
</ div >
);
}
Using custom claims
'use client' ;
import { useTokenClaims } from '@workos-inc/authkit-nextjs' ;
interface MyCustomClaims {
department : string ;
employeeId : string ;
accessLevel : number ;
}
export function EmployeeInfo () {
const claims = useTokenClaims < MyCustomClaims >();
return (
< div >
< p > Department: { claims . department } </ p >
< p > Employee ID: { claims . employeeId } </ p >
< p > Access Level: { claims . accessLevel } </ p >
</ div >
);
}
Permission-based UI
'use client' ;
import { useTokenClaims } from '@workos-inc/authkit-nextjs' ;
export function AdminControls () {
const { permissions } = useTokenClaims ();
const canDelete = permissions ?. includes ( 'resources:delete' );
const canEdit = permissions ?. includes ( 'resources:edit' );
const canView = permissions ?. includes ( 'resources:view' );
return (
< div >
{ canView && < button > View Resources </ button > }
{ canEdit && < button > Edit Resources </ button > }
{ canDelete && < button > Delete Resources </ button > }
</ div >
);
}
Token expiration check
'use client' ;
import { useTokenClaims } from '@workos-inc/authkit-nextjs' ;
import { useEffect , useState } from 'react' ;
export function TokenExpirationWarning () {
const { exp } = useTokenClaims ();
const [ timeRemaining , setTimeRemaining ] = useState < number | null >( null );
useEffect (() => {
if ( ! exp ) return ;
const updateTimeRemaining = () => {
const remaining = exp - Math . floor ( Date . now () / 1000 );
setTimeRemaining ( remaining );
};
updateTimeRemaining ();
const interval = setInterval ( updateTimeRemaining , 1000 );
return () => clearInterval ( interval );
}, [ exp ]);
if ( ! timeRemaining || timeRemaining > 300 ) {
return null ; // Don't show warning if more than 5 minutes remain
}
return (
< div className = "warning" >
Your session will expire in { Math . floor ( timeRemaining / 60 ) } minutes
</ div >
);
}
Role-based access
'use client' ;
import { useTokenClaims } from '@workos-inc/authkit-nextjs' ;
export function RoleBasedContent () {
const { role , roles } = useTokenClaims ();
const isAdmin = role === 'admin' || roles ?. includes ( 'admin' );
const isModerator = role === 'moderator' || roles ?. includes ( 'moderator' );
return (
< div >
< h1 > Dashboard </ h1 >
{ /* Everyone sees this */ }
< section >
< h2 > Public Content </ h2 >
</ section >
{ /* Only moderators and admins */ }
{ ( isModerator || isAdmin ) && (
< section >
< h2 > Moderation Tools </ h2 >
</ section >
) }
{ /* Only admins */ }
{ isAdmin && (
< section >
< h2 > Admin Panel </ h2 >
</ section >
) }
</ div >
);
}
Combining with useAccessToken
'use client' ;
import { useTokenClaims , useAccessToken } from '@workos-inc/authkit-nextjs' ;
export function TokenDebugger () {
const { accessToken } = useAccessToken ();
const claims = useTokenClaims ();
const copyToken = () => {
if ( accessToken ) {
navigator . clipboard . writeText ( accessToken );
}
};
return (
< div >
< h2 > Token Information </ h2 >
< div >
< h3 > Raw Token </ h3 >
< button onClick = { copyToken } > Copy Token </ button >
< pre style = { { wordBreak: 'break-all' } } >
{ accessToken ?. substring ( 0 , 50 ) } ...
</ pre >
</ div >
< div >
< h3 > Decoded Claims </ h3 >
< pre > { JSON . stringify ( claims , null , 2 ) } </ pre >
</ div >
</ div >
);
}
How it works
The hook:
Uses useAccessToken internally to get the current access token
Decodes the JWT token using base64url decoding
Parses the payload as JSON
Returns the claims object
Automatically updates when the token changes
Returns an empty object if no token is available or decoding fails
Important notes
This hook only decodes the token, it does not verify its signature. Token verification is handled server-side by WorkOS. Never rely on client-side token claims for security decisions.
The hook uses useMemo internally to avoid unnecessary re-decoding of the same token.
For server-side access to token claims, use the getTokenClaims function which performs the same decoding on the server.
useAccessToken Get the raw access token
useAuth Access authentication context
getTokenClaims Server-side token claims