Skip to main content
Docs

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.

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.

Note

Your application must be wrapped in a <QueryClientProvider /> component with a configured QueryClient instance. See the Tanstack Query docs for setup instructions.

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.

Note

Your application must be wrapped in a <QueryClientProvider /> component with a configured QueryClient instance. See the Tanstack Query docs for setup instructions.

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

What did you think of this content?

Last updated on