Verify OAuth access tokens in your React Router application with Clerk
When building a resource server that needs to accept and verify issued by Clerk, it's crucial to verify these tokens on your backend to ensure the request is coming from an authenticated client.
Clerk's React Router SDK provides a built-in getAuth() function that supports token validation via the acceptsToken parameter. This lets you specify which type(s) of token your loader or action should accept in your React Router application.
By default, acceptsToken is set to session_token, which means OAuth tokens will not be accepted unless explicitly configured. You can pass either a single token type or an array of token types to acceptsToken. To learn more about the supported token types, see the getAuth() parameters documentation.
Below are two examples of verifying OAuth access tokens in a React Router route module using Clerk's SDK:
Example 1: Accepting a single token type
In the following example, the acceptsToken parameter is set to only accept oauth_tokens.
- If the token is invalid or missing,
getAuth()will returnfalseforisAuthenticated, and the request will be rejected with a401response. - If the token is valid,
userId(the user who authorized the OAuth application) is returned along with the token's grantedscopesfor use in the application logic.
import { getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/api.example'
export async function loader(args: Route.LoaderArgs) {
const authObject = await getAuth(args, {
acceptsToken: 'oauth_token',
})
// If isAuthenticated is false, the token is invalid
if (!authObject.isAuthenticated) {
throw new Response('Unauthorized', { status: 401 })
}
const { userId, scopes } = authObject
return Response.json({ userId, scopes })
}In the following example, the acceptsToken parameter allows both session_tokens and oauth_tokens.
- If the token is invalid or missing,
getAuth()will returnfalseforisAuthenticated. CheckisAuthenticatedbefore reading the other properties, likeuserId. - If the token is an
oauth_token, the code checks that it includes the required'profile'scope. If not, the request is rejected with a401response. - If the token is valid and the required scope is present,
isAuthenticatedistrueanduserIdis returned. Because the'profile'scope grants access to the user's identity, this example uses theuserIdto fetch and return the user's profile data.
import { getAuth, clerkClient } from '@clerk/react-router/server'
import type { Route } from './+types/api.example'
export async function action(args: Route.ActionArgs) {
// Accept both session_token and oauth_token types
const authObject = await getAuth(args, {
acceptsToken: ['session_token', 'oauth_token'],
})
// If isAuthenticated is false, the token is invalid
if (!authObject.isAuthenticated) {
throw new Response('Unauthorized', { status: 401 })
}
// Check if the token is an oauth_token and if it doesn't have the required scope
if (authObject.tokenType === 'oauth_token' && !authObject.scopes.includes('profile')) {
throw new Response('OAuth token missing the "profile" scope', { status: 401 })
}
// The 'profile' scope grants access to the user's identity,
// so fetch and return the authenticated user's profile data
const user = await clerkClient(args).users.getUser(authObject.userId)
return Response.json({ id: user.id, firstName: user.firstName, lastName: user.lastName })
}Feedback
Last updated on