Request authentication
A request is considered “authenticated” when the backend can securely identify the user and device that is making the request. Reasons for making authenticated requests to the backend include:
- Associating the user with the action being performed
- Ensuring the user has permission to make the request
- Keeping an audit log of which device the user is performing actions from
To authenticate requests when using a Clerk SDK, you must pass Clerk's short-lived session token to your server. The session token contains cryptographically signed claims about the user's identity and authentication state. Read more about making requests.
Required headers
The following headers are required for Clerk to authenticate a request. It contains information that Clerk uses to determine whether a request is in a signed in or signed out state, or if a handshake must be performed.
Authorization
: This should include the user's session token as a Bearer token.Accept
Host
Origin
Referer
Sec-Fetch-Dest
User-Agent
X-Forwarded-Host
X-Forwarded-Proto
- Alternatively, you can use
CloudFront-Forwarded-Proto
- Alternatively, you can use
Examples
To make authenticated requests, the approach differs based on whether your client and server are on the same origin or different origins.
Same-origin requests
If your client and server are on the same origin (e.g. making an API call to foo.com/api
from JavaScript running on foo.com
), the session token is automatically passed to the backend in a cookie. This means that all requests to same-origin endpoints are authenticated by default.
Vanilla JavaScript
You can use the native browser Fetch API as you normally would and the request will be authenticated.
fetch('/api/foo').then((res) => res.json())
React-based applications
You can use the native browser Fetch API as you normally would and the request will be authenticated. But when a tab loses focus, and data must be fetched in the background, the session token cookie is not automatically included. You'll need to explicitly pass the session token as a Bearer token in the Authorization header.
Use the useAuth() hook's getToken()
method to get the session token. Since getToken()
returns a Promise, you'll need to await its resolution before making the request.
export default async function useFetch() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const authenticatedFetch = async (...args) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return authenticatedFetch
}
import useSWR from 'swr'
export default async function useClerkSWR(url: string) {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const fetcher = async (...args: [RequestInfo]) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return useSWR(url, fetcher)
}
When using Tanstack Query (formerly React Query), you'll need a query function that properly handles errors. The native Fetch API doesn't throw errors for non-200 responses, so you'll need to add explicit error handling.
import { useQuery } from '@tanstack/react-query'
import { useAuth } from '@clerk/nextjs'
// Define your query keys as constants to avoid typos
export const queryKeys = {
foo: ['foo'] as const,
// Add other query keys as needed
}
// Define the response type for type safety
interface FooResponse {
// Add your response type here
id: string
name: string
}
export function useFooQuery() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
return useQuery({
queryKey: queryKeys.foo,
queryFn: async (): Promise<FooResponse> => {
// Use `getToken()` to get the current session token
const token = await getToken()
// Make the request
const response = await fetch('/api/foo', {
headers: {
Authorization: `Bearer ${token}`, // Include the session token as a Bearer token in the Authorization header
'Content-Type': 'application/json',
},
})
if (!response.ok) {
// Include status code and status text in error message
throw new Error(`API Error: ${response.status} ${response.statusText}`)
}
const data = await response.json()
return data as FooResponse
},
// Add common configuration options
retry: 2,
staleTime: 5 * 60 * 1000, // 5 minutes
})
}
// Usage in component:
function MyComponent() {
const { data, isLoading, error } = useFooQuery()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
if (!data) return null
return <div>{data.name}</div>
}
Cross-origin requests
If your client and server are on different origins (e.g. making an API call to a server on api.foo.com
from JavaScript running on a client at foo.com
), the session token needs to be passed as a Bearer token in the Authorization header.
You can retrieve the session token using the getToken()
method. Since getToken()
returns a Promise, you'll need to await its resolution before making the request.
Vanilla JavaScript
In JavaScript applications, use the global Clerk.session
object to access the method.
(async () => {
fetch('/api/foo', {
headers: {
Authorization: `Bearer ${await Clerk.session.getToken()}`,
},
}).then((res) => res.json())
})()
React-based applications
In React-based applications, use the useAuth() hook to access the getToken()
method.
export default async function useFetch() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const authenticatedFetch = async (...args) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return authenticatedFetch
}
import useSWR from 'swr'
import { useAuth } from '@clerk/nextjs'
export default async function useClerkSWR(url) {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const fetcher = async (...args) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return useSWR(url, fetcher)
}
The following example shows how to use Tanstack Query to create an authenticated query.
When using Tanstack Query (formerly React Query), you'll need a query function that properly handles errors. The native Fetch API doesn't throw errors for non-200 responses, so you'll need to add explicit error handling.
import { useQuery } from '@tanstack/react-query'
import { useAuth } from '@clerk/nextjs'
// Define your query keys as constants to avoid typos
export const queryKeys = {
foo: ['foo'] as const,
// Add other query keys as needed
}
// Define the response type for type safety
interface FooResponse {
// Add your response type here
id: string
name: string
}
export function useFooQuery() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
return useQuery({
queryKey: queryKeys.foo,
queryFn: async (): Promise<FooResponse> => {
// Use `getToken()` to get the current session token
const token = await getToken()
// Make the request
const response = await fetch('/api/foo', {
headers: {
Authorization: `Bearer ${token}`, // Include the session token as a Bearer token in the Authorization header
'Content-Type': 'application/json',
},
})
if (!response.ok) {
// Include status code and status text in error message
throw new Error(`API Error: ${response.status} ${response.statusText}`)
}
const data = await response.json()
return data as FooResponse
},
// Add common configuration options
retry: 2,
staleTime: 5 * 60 * 1000, // 5 minutes
})
}
// Usage in component:
function MyComponent() {
const { data, isLoading, error } = useFooQuery()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
if (!data) return null
return <div>{data.name}</div>
}
Feedback
Last updated on