Skip to content

Commit 85d7648

Browse files
committed
update
1 parent bd9aed6 commit 85d7648

File tree

6 files changed

+300
-63
lines changed

6 files changed

+300
-63
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
> [!WARNING]
2-
> Metadata is limited to **8KB** maximum.
2+
> Metadata is limited to **8KB** maximum. If you're storing metadata as a custom claim in the session token, it's highly recommended to keep it under **1.2KB**. [Learn more about the session token size limitations](/docs/backend-requests/resources/session-tokens#size-limitations).
33
>
44
> If you use Clerk metadata and modify it server-side, the changes won't appear in the session token until the next refresh. To avoid race conditions, either force a JWT refresh after metadata changes or handle the delay in your application logic.
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
> [!CAUTION]
2-
> The entire session token has a max size of 4KB. Exceeding this size can have adverse effects, including a possible infinite redirect loop for users who exceed this size in Next.js applications.
3-
> It's recommended to move particularly large claims out of the JWT and fetch these using a separate API call from your backend.
4-
> [Learn more](/docs/backend-requests/resources/session-tokens#size-limitations).
2+
> **It's recommended to keep the total size of custom claims in the session token under 1.2KB.** Exceeding this size can have adverse effects, including a possible infinite redirect loop for users who exceed this size in Next.js applications. It's recommended to move particularly large claims out of the JWT and fetch these using a separate API call from your backend. [Learn more](/docs/backend-requests/resources/session-tokens#size-limitations).

docs/backend-requests/custom-session-token.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ This guide will show you how to customize a session token to include additional
3131
<Tab>
3232
For Next.js, the `Auth` object is accessed using the `auth()` helper in App Router apps and the `getAuth()` function in Pages Router apps. [Learn more about using these helpers](/docs/references/nextjs/read-session-data#server-side).
3333

34+
Use the following tabs to see examples of how to use these helpers to access the user's ID in your App Router or Pages Router app.
35+
3436
<CodeBlockTabs options={["App Router", "Pages Router"]}>
3537
```tsx {{ filename: 'app/api/example/route.tsx' }}
3638
import { auth } from '@clerk/nextjs/server'

docs/backend-requests/resources/session-tokens.mdx

Lines changed: 231 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,17 @@ Read more about Clerk session tokens and how they work in [the guide on how Cler
9090
</Tab>
9191
</Tabs>
9292

93+
## Custom claims
94+
9395
If you would like to add custom claims to your session token, you can [customize it](/docs/backend-requests/custom-session-token).
9496

9597
You can also create custom tokens using a [JWT template](/docs/backend-requests/jwt-templates).
9698

9799
## Size limitations
98100

99-
The Clerk session token is stored in a cookie. All modern browsers [limit the maximum size of a cookie to 4kb](https://datatracker.ietf.org/doc/html/rfc2109#section-6.3). Exceeding this limit can have adverse effects, including a possible infinite redirect loop for users who exceed this size in Next.js applications.
101+
The Clerk session token is stored in a cookie. All modern browsers [limit the maximum size of a cookie to **4KB**](https://datatracker.ietf.org/doc/html/rfc2109#section-6.3). Exceeding this limit can have adverse effects, including a possible infinite redirect loop for users who exceed this size in Next.js applications.
100102

101-
A session token with the [default session claims](#default-claims) won't run into this issue, as this configuration produces a cookie significantly smaller than 4kb. However, this limitation becomes relevant when implementing a [custom session token](/docs/backend-requests/custom-session-token). In this case, it's recommended to move particularly large claims out of the token and fetch these using a separate API call from your backend.
103+
A session token with the [default session claims](#default-claims) won't run into this issue, as this configuration produces a cookie significantly smaller than 4KB. However, this limitation becomes relevant when implementing a [custom session token](/docs/backend-requests/custom-session-token). **It's recommended to keep the total size of custom claims in the session token under 1.2KB.**
102104

103105
Claims to monitor for size limits:
104106

@@ -108,11 +110,237 @@ Claims to monitor for size limits:
108110
- `org.public_metadata`
109111
- `org_membership.public_metadata`
110112

111-
If you include any of these claims in your token, use caution to ensure the stored data doesn't exceed the size limit.
113+
If you add any of these custom claims in your token, use caution to ensure the stored data doesn't exceed the size limit. It's recommended to [move particularly large claims like these out of the token](#example) and fetch them using a separate API call from your backend.
112114

113115
> [!NOTE]
114116
> If your application encounters this issue, the Clerk Dashboard will display a warning: **"Some users are exceeding cookie size limits"**. To resolve this, update your [custom session token](/docs/backend-requests/custom-session-token).
115117
118+
### Example
119+
120+
For example, if you were storing several fields in `user.public_metadata`, like this:
121+
122+
```js {{ prettier: false }}
123+
// user.public_metadata
124+
{
125+
onboardingComplete: true,
126+
birthday: '2000-01-01',
127+
country: 'Canada',
128+
bio: 'This is a bio -- imagine it is 6kb of written info',
129+
}
130+
```
131+
132+
Instead of storing all of that data in the session token, and possibly exceeding the 4KB limit, like this:
133+
134+
```json
135+
// Custom claims in the session token
136+
{
137+
"metadata": "{{user.public_metadata}}"
138+
}
139+
```
140+
141+
You could store only the necessary data in the session token, such as only the `onboardingComplete` field, like this:
142+
143+
```json
144+
// Custom claims in the session token
145+
{
146+
"onboardingComplete": "{{user.public_metadata.onboardingComplete}}"
147+
}
148+
```
149+
150+
If you need to access the other fields, you can fetch them using a separate API call from your backend. The following example uses the [`getUser()`](/docs/references/backend/user/get-user) method to access the current user's [Backend `User` object](/docs/references/backend/types/backend-user), which includes the `publicMetadata` field.
151+
152+
<Tabs items={["Next.js", "Astro", "Express", "React Router", "Remix", "Tanstack React Start"]}>
153+
<Tab>
154+
<CodeBlockTabs options={["App Router", "Pages Router"]}>
155+
```tsx {{ filename: 'app/api/example/route.ts' }}
156+
import { auth, clerkClient } from '@clerk/nextjs/server'
157+
158+
export async function GET() {
159+
// Use `auth()` to get the user's ID
160+
const { userId } = await auth()
161+
162+
// Protect the route by checking if the user is signed in
163+
if (!userId) {
164+
return new NextResponse('Unauthorized', { status: 401 })
165+
}
166+
167+
const client = await clerkClient()
168+
169+
// Use the Backend SDK's `getUser()` method to get the Backend User object
170+
const user = await client.users.getUser(userId)
171+
172+
// Return the Backend User object
173+
return NextResponse.json({ user: user }, { status: 200 })
174+
}
175+
```
176+
177+
<Include src="_partials/nextjs/get-auth" />
178+
</CodeBlockTabs>
179+
</Tab>
180+
181+
<Tab>
182+
```tsx {{ filename: 'src/api/example.ts' }}
183+
import { clerkClient } from '@clerk/astro/server'
184+
185+
export async function GET(context) {
186+
// Use `locals.auth()` to get the user's ID
187+
const { userId } = context.locals.auth()
188+
189+
// Protect the route by checking if the user is signed in
190+
if (!userId) {
191+
return new Response('Unauthorized', { status: 401 })
192+
}
193+
194+
// Use the Backend SDK's `getUser()` method to get the Backend User object
195+
const user = await clerkClient(context).users.getUser(userId)
196+
197+
// Return the Backend User object
198+
return new Response(JSON.stringify({ user }))
199+
}
200+
```
201+
</Tab>
202+
203+
<Tab>
204+
```js {{ filename: 'index.js' }}
205+
import { createClerkClient, getAuth } from '@clerk/express'
206+
import express from 'express'
207+
208+
const app = express()
209+
const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY })
210+
211+
app.get('/user', async (req, res) => {
212+
// Use `getAuth()` to get the user's ID
213+
const { userId } = getAuth(req)
214+
215+
// Protect the route by checking if the user is signed in
216+
if (!userId) {
217+
res.status(401).json({ error: 'User not authenticated' })
218+
}
219+
220+
// Use the Backend SDK's `getUser()` method to get the Backend User object
221+
const user = await clerkClient.users.getUser(userId)
222+
223+
// Return the Backend User object
224+
res.json(user)
225+
})
226+
```
227+
</Tab>
228+
229+
<Tab>
230+
```tsx {{ filename: 'src/routes/profile.tsx' }}
231+
import { redirect } from 'react-router'
232+
import { getAuth } from '@clerk/react-router/ssr.server'
233+
import { createClerkClient } from '@clerk/react-router/api.server'
234+
import type { Route } from './+types/profile'
235+
236+
export async function loader(args: Route.LoaderArgs) {
237+
// Use `getAuth()` to get the user's ID
238+
const { userId } = await getAuth(args)
239+
240+
// Protect the route by checking if the user is signed in
241+
if (!userId) {
242+
return redirect('/sign-in?redirect_url=' + args.request.url)
243+
}
244+
245+
// Use the Backend SDK's `getUser()` method to get the Backend User object
246+
const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser(
247+
userId,
248+
)
249+
250+
// Return the Backend User object
251+
return {
252+
user: JSON.stringify(user),
253+
}
254+
}
255+
```
256+
</Tab>
257+
258+
<Tab>
259+
<CodeBlockTabs options={["Loader Function", "Action Function"]}>
260+
```tsx {{ filename: 'routes/profile.tsx' }}
261+
import { LoaderFunction, redirect } from '@remix-run/node'
262+
import { getAuth } from '@clerk/remix/ssr.server'
263+
import { createClerkClient } from '@clerk/remix/api.server'
264+
265+
export const loader: LoaderFunction = async (args) => {
266+
// Use `getAuth()` to get the user's ID
267+
const { userId } = await getAuth(args)
268+
269+
// If there is no userId, then redirect to sign-in route
270+
if (!userId) {
271+
return redirect('/sign-in?redirect_url=' + args.request.url)
272+
}
273+
274+
// Use the Backend SDK's `getUser()` method to get the Backend User object
275+
const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser(
276+
userId,
277+
)
278+
279+
// Return the Backend User object
280+
return { serialisedUser: JSON.stringify(user) }
281+
}
282+
```
283+
284+
```tsx {{ filename: 'routes/profile.tsx' }}
285+
import { ActionFunction, redirect } from '@remix-run/node'
286+
import { getAuth } from '@clerk/remix/ssr.server'
287+
import { createClerkClient } from '@clerk/remix/api.server'
288+
289+
export const action: ActionFunction = async (args) => {
290+
// Use `getAuth()` to get the user's ID
291+
const { userId } = await getAuth(args)
292+
293+
// If there is no userId, then redirect to sign-in route
294+
if (!userId) {
295+
return redirect('/sign-in?redirect_url=' + args.request.url)
296+
}
297+
298+
// Prepare the data for the mutation
299+
const params = { firstName: 'John', lastName: 'Wicker' }
300+
301+
// // Use the Backend SDK's `updateUser()` method to update the Backend User object
302+
const updatedUser = await createClerkClient({
303+
secretKey: process.env.CLERK_SECRET_KEY,
304+
}).users.updateUser(userId, params)
305+
306+
// Return the updated user
307+
return { serialisedUser: JSON.stringify(updatedUser) }
308+
}
309+
```
310+
</CodeBlockTabs>
311+
</Tab>
312+
313+
<Tab>
314+
```tsx {{ filename: 'app/routes/api/example.tsx' }}
315+
import { createClerkClient } from '@clerk/backend'
316+
import { json } from '@tanstack/react-start'
317+
import { createAPIFileRoute } from '@tanstack/react-start/api'
318+
import { getAuth } from '@clerk/tanstack-react-start/server'
319+
320+
export const Route = createAPIFileRoute('/api/example')({
321+
GET: async ({ request, params }) => {
322+
// Use `getAuth()` to get the user's ID
323+
const { userId } = await getAuth(req)
324+
325+
// Protect the route by checking if the user is signed in
326+
if (!userId) {
327+
return json({ error: 'Unauthorized' }, { status: 401 })
328+
}
329+
330+
// Instantiate the Backend SDK
331+
const clerkClient = createClerkClient({ secretKey: import.meta.env.CLERK_SECRET_KEY })
332+
333+
// Use the Backend SDK's `getUser()` method to get the Backend User object
334+
const user = userId ? await clerkClient.users.getUser(userId) : null
335+
336+
// Return the Backend User object
337+
return json({ user })
338+
},
339+
})
340+
```
341+
</Tab>
342+
</Tabs>
343+
116344
## Validate session tokens
117345

118346
If you're using the middleware provided by our Clerk SDKs, validating session tokens is handled automatically in every request. If you're not using the middleware, you can still use the respective helpers provided by the SDKs to validate the tokens.

0 commit comments

Comments
 (0)