Skip to main content
Docs

Force a session token refresh

A user's session token is a short-lived JWT that Clerk refreshes every 60 seconds. However, there are some cases where you might want to force a refresh. For example, if you're retrieving information from the session token that has been updated but the token hasn't refreshed yet, the information is still old and you'll need to force the token to refresh.

There are two recommended approaches to force a user's session token to refresh:

Both perform a network request, but getToken({ skipCache: true }) will only get a new token, while user.reload() will both get a new token and a new User object.

getToken({ skipCache: true })

The getToken() method retrieves the current user's session token. If skipCache is set to true, it will force a new token to be minted.

getToken() is available on Clerk's authentication context, so it is accessible in both client-side and server-side code.

  • Client-side: Access getToken() from the useSession() or useAuth() hooks.
  • Server-side: Access getToken() from the object.

Example

Say you're building a user profile page and want to allow the user to set their birthday, but Clerk doesn't collect birthdays. To store information about a user that Clerk doesn't collect, you can use metadata, which will get stored on the user's User object.

When using metadata, it's recommended to store it in the user's session token to avoid making an API request to Clerk's Backend API when retrieving it, as API requests are slower and are subject to rate limits.

However, when retrieving the user's metadata from the session token, because the session token refreshes every 60 seconds, the metadata may not be up to date if the token hasn't refreshed yet. In this case, you'd want to force a session token refresh after updating the metadata to ensure that when the metadata is retrieved, it is the latest version.

This example requires that the user's birthday has been configured to be stored in the session token like:

{
  "birthday": "{{user.public_metadata.birthday}}"
}

The /update-user-metadata API route uses Clerk's method from the to update the user's birthday metadata.

app/api/update-user-metadata/route.ts
import { auth, clerkClient } from '@clerk/nextjs/server'
import { NextRequest, NextResponse } from 'next/server'

export async function POST(req: NextRequest) {
  // Use `req.json()` to parse the request body
  const { birthday } = await req.json()

  // Use `auth()` to access the current user's ID
  const { userId } = await auth()

  // Protect the route by checking if the user is signed in
  if (!userId) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }

  // Use `clerkClient()` to access the Clerk client
  // The Clerk client gives you access to Clerk Backend API resources,
  // such as the `users` resource, which includes methods for managing users
  const client = await clerkClient()

  // Use the Backend SDK's `updateUserMetadata()` method to update the user's birthday
  await client.users.updateUserMetadata(userId, {
    publicMetadata: {
      birthday,
    },
  })

  return NextResponse.json({ success: true }, { status: 200 })
}

The /user-profile page allows the user to update their birthday. It retrieves the user's birthday from the session token's claims under the birthday key. To update the user's birthday, it calls the /update-user-metadata API route, which handles the metadata update, and then forces a session token refresh using getToken({ skipCache: true }).

app/user-profile/page.tsx
'use client'
import { useState, useEffect } from 'react'
import { useAuth } from '@clerk/nextjs'

export default function Page() {
  // Use `useAuth()` to access the authentication context,
  // including the user's session claims
  const { isLoaded, userId, sessionClaims, getToken } = useAuth()

  const [birthday, setBirthday] = useState('')
  const [status, setStatus] = useState<'loading' | 'success' | 'error' | null>(null)
  const [error, setError] = useState<string | null>(null)

  // Update state once the user's data loads
  useEffect(() => {
    // Retrieve the user's birthday from the session token's claims under the `birthday` key
    setBirthday((sessionClaims?.birthday as string) || '')
  }, [sessionClaims])

  async function updateUserBirthday(birthday: string) {
    try {
      const response = await fetch('/api/update-user-metadata', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ birthday }),
      })

      const result = await response.json()

      if (!response.ok) {
        throw new Error(result.error || 'Failed to update birthday')
      }

      // Force token refresh
      // so that the token is updated with the new birthday metadata
      await getToken({ skipCache: true })

      return { success: true }
    } catch (err: any) {
      return { error: err.message || 'Failed to update birthday' }
    }
  }

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    setStatus('loading')
    setError(null)

    // Check if the birthday has been modified
    if (birthday === (sessionClaims?.birthday || '')) {
      setError('Please enter a new birthday to update')
      setStatus('error')
      return
    }

    try {
      const result = await updateUserBirthday(birthday)

      if (result.error) {
        setError(result.error)
        setStatus('error')
      } else {
        setStatus('success')
      }
    } catch (err: any) {
      setError(err.message || 'Failed to update birthday')
      setStatus('error')
    }
  }

  // Check if Clerk has loaded
  if (!isLoaded) return <div>Loading...</div>

  // Check if the user is authenticated
  if (!userId) return <div>Sign in to view your profile</div>

  return (
    <div style={{ maxWidth: 400, margin: '2rem auto' }}>
      <h1>Update Your Birthday</h1>

      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="birthday">Birthday (YYYY-MM-DD):</label>
          <input
            id="birthday"
            type="date"
            value={birthday}
            onChange={(e) => setBirthday(e.target.value)}
          />
        </div>

        <button type="submit" disabled={status === 'loading'}>
          {status === 'loading' ? 'Updating...' : 'Update Birthday'}
        </button>
      </form>

      {status === 'success' && <p style={{ color: 'green' }}>Birthday updated successfully!</p>}
      {status === 'error' && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  )
}

user.reload()

The user.reload() method refreshes the current user's User object and their session token. It can be accessed from the useUser() hook.

You only need to call user.reload() if you've updated the User object outside of the user.update() method or Clerk hooks; for example, if you made changes through an API endpoint.

Feedback

What did you think of this content?

Last updated on