Skip to main content
Docs

Making authenticated requests

It's important to authenticate requests to ensure that the user is who they say they are and that they have permission to perform the action they are trying to perform.

A request is considered authenticated when the backend can securely identify the user and device that is making the request. This is done by passing the user's session token to the backend. The session token contains cryptographically signed claims about the user's identity and authentication state.

Authenticating a request depends on what type of request you are making. If the request is made from the same origin as the server, Clerk automatically includes the session token in the request. If the request is made from a different origin, you must manually pass the session token in the Authorization header.

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. All requests to same-origin endpoints are authenticated by default; simply make the request as you normally would and the session token will be included.

Use the following tabs to see how to make authenticated same-origin requests using different frameworks.

You can use the native browser Fetch API as you normally would and the request will be authenticated. The cookie holding the session token is automatically included with same-origin requests.

export default async function useFetch() {
  const authenticatedFetch = async (...args) => {
    return fetch(...args).then((res) => res.json())
  }
  return authenticatedFetch
}
import useSWR from 'swr'

export default async function useClerkSWR(url: string) {
  const fetcher = async (...args: [RequestInfo]) => {
    return fetch(...args).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'

// 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() {
  return useQuery({
    queryKey: queryKeys.foo,
    queryFn: async (): Promise<FooResponse> => {
      // Make the authenticated request
      const response = await fetch('/api/foo')

      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>
}

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())

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.

Use the following tabs to see how to access the getToken() method depending on your framework.

Use the useAuth() hook to access the getToken() method.

export default function useFetch() {
  // Use `useAuth()` to access the `getToken()` method
  const { getToken } = useAuth()

  const authenticatedFetch = async (...args) => {
    // Use `getToken()` to get the current session token
    const token = await getToken()

    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 function useClerkSWR(url) {
  // Use `useAuth()` to access the `getToken()` method
  const { getToken } = useAuth()

  const fetcher = async (...args) => {
    // Use `getToken()` to get the current session token
    const token = await getToken()

    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'

// 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>
}

Use the global Clerk.session object to access the getToken() method.

(async () => {
  fetch('/api/foo', {
    headers: {
      Authorization: `Bearer ${await Clerk.session.getToken()}`,
    },
  }).then((res) => res.json())
})()

Feedback

What did you think of this content?

Last updated on