docs: Update nextjs user management guide to use getClaims (#44124)

This commit is contained in:
Chris Chinchilla authored and GitHub committed 2026-03-24 21:22:41 +01:00
1 parent fa4a668eeb
commit 8e33378315
7 files changed
+1296 -1401

No files matched your search

@@ -1,10 +1,11 @@
'use client'
import { useCallback, useEffect, useState } from 'react'
import { createClient } from '@/lib/supabase/client'
import { type User } from '@supabase/supabase-js'
import Avatar from './avatar'
export default function AccountForm({ user }: { user: User | null }) {
type Claims = { sub: string; email?: string; [key: string]: unknown }
export default function AccountForm({ claims }: { claims: Claims | null }) {
const supabase = createClient()
const [loading, setLoading] = useState(true)
const [fullname, setFullname] = useState<string | null>(null)
@@ -14,12 +15,17 @@ export default function AccountForm({ user }: { user: User | null }) {
const getProfile = useCallback(async () => {
try {
if (!claims?.sub) {
setLoading(false)
return
}
setLoading(true)
const { data, error, status } = await supabase
.from('profiles')
.select(`full_name, username, website, avatar_url`)
.eq('id', user?.id)
.eq('id', claims.sub)
.single()
if (error && status !== 406) {
@@ -38,11 +44,11 @@ export default function AccountForm({ user }: { user: User | null }) {
} finally {
setLoading(false)
}
}, [user, supabase])
}, [claims, supabase])
useEffect(() => {
getProfile()
}, [user, getProfile])
}, [claims, getProfile])
async function updateProfile({
username,
@@ -55,10 +61,15 @@ export default function AccountForm({ user }: { user: User | null }) {
avatar_url: string | null
}) {
try {
if (!claims?.sub) {
alert('You must be logged in to update your profile')
return
}
setLoading(true)
const { error } = await supabase.from('profiles').upsert({
id: user?.id as string,
id: claims.sub,
full_name: fullname,
username,
website,
@@ -77,7 +88,7 @@ export default function AccountForm({ user }: { user: User | null }) {
return (
<div className="form-widget">
<Avatar
uid={user?.id ?? null}
uid={claims?.sub ?? null}
url={avatar_url}
size={150}
onUpload={(url) => {
@@ -87,7 +98,7 @@ export default function AccountForm({ user }: { user: User | null }) {
/>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="text" value={user?.email} disabled />
<input id="email" type="text" value={claims?.email ?? ''} disabled />
</div>
<div>
<label htmlFor="fullName">Full Name</label>
@@ -121,7 +132,7 @@ export default function AccountForm({ user }: { user: User | null }) {
<button
className="button primary block"
onClick={() => updateProfile({ fullname, username, website, avatar_url })}
disabled={loading}
disabled={loading || !claims?.sub}
>
{loading ? 'Loading ...' : 'Update'}
</button>
@@ -4,9 +4,7 @@ import { createClient } from '@/lib/supabase/server'
export default async function Account() {
const supabase = await createClient()
const {
data: { user },
} = await supabase.auth.getUser()
const { data: claimsData } = await supabase.auth.getClaims()
return <AccountForm user={user} />
return <AccountForm claims={claimsData?.claims ?? null} />
}
@@ -6,11 +6,9 @@ export async function POST(req: NextRequest) {
const supabase = await createClient()
// Check if a user's logged in
const {
data: { user },
} = await supabase.auth.getUser()
const { data: claimsData } = await supabase.auth.getClaims()
if (user) {
if (claimsData?.claims) {
await supabase.auth.signOut()
}
@@ -27,8 +27,13 @@ export async function updateSession(request: NextRequest) {
}
)
// refreshing the auth token
await supabase.auth.getUser()
// Do not run code between createServerClient and
// supabase.auth.getClaims(). A simple mistake could make it very hard to debug
// issues with users being randomly logged out.
// IMPORTANT: If you remove getClaims() and you use server-side rendering
// with the Supabase client, your users may be randomly logged out.
await supabase.auth.getClaims()
return supabaseResponse
}
File diff suppressed because it is too large. Load diff
@@ -9,20 +9,20 @@
"lint": "next lint"
},
"dependencies": {
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.53.0",
"next": "latest",
"react": "19.1.1",
"react-dom": "19.1.1"
"@supabase/ssr": "^0.9.0",
"@supabase/supabase-js": "^2.100.0",
"next": "^16.2.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.11",
"@types/node": "^24.2.0",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"eslint": "^9.32.0",
"eslint-config-next": "15.4.5",
"tailwindcss": "^4.1.11",
"typescript": "^5"
"@tailwindcss/postcss": "^4.2.2",
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"eslint": "^10.1.0",
"eslint-config-next": "16.2.1",
"tailwindcss": "^4.2.2",
"typescript": "^6.0.2"
}
}