[{"data":1,"prerenderedAt":886},["ShallowReactive",2],{"navigation_docs":3,"-playbooks-auth-better-auth":80,"-playbooks-auth-better-auth-surround":881},[4,20,45],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":6},"Getting Started",false,"\u002Fgetting-started","1.getting-started",[10,15],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":21,"icon":6,"path":22,"stem":23,"children":24,"page":6},"Guide","\u002Fguide","2.guide",[25,30,35,40],{"title":26,"path":27,"stem":28,"icon":29},"Configuration","\u002Fguide\u002Fconfiguration","2.guide\u002F1.configuration","i-lucide-settings",{"title":31,"path":32,"stem":33,"icon":34},"Composables","\u002Fguide\u002Fcomposables","2.guide\u002F2.composables","i-lucide-code",{"title":36,"path":37,"stem":38,"icon":39},"Authentication","\u002Fguide\u002Fauthentication","2.guide\u002F3.authentication","i-lucide-key-round",{"title":41,"path":42,"stem":43,"icon":44},"Types","\u002Fguide\u002Ftypes","2.guide\u002F4.types","i-lucide-braces",{"title":46,"icon":6,"path":47,"stem":48,"children":49,"page":6},"Playbooks","\u002Fplaybooks","3.playbooks",[50,55,60,65,70,75],{"title":51,"path":52,"stem":53,"icon":54},"Local Postgres & PostgREST","\u002Fplaybooks\u002Flocal-postgrest","3.playbooks\u002F1.local-postgrest","i-simple-icons-docker",{"title":56,"path":57,"stem":58,"icon":59},"Coming from Supabase","\u002Fplaybooks\u002Fcoming-from-supabase","3.playbooks\u002F2.coming-from-supabase","i-lucide-arrow-right-left",{"title":61,"path":62,"stem":63,"icon":64},"Auth with nuxt-auth-utils","\u002Fplaybooks\u002Fauth-nuxt-auth-utils","3.playbooks\u002F3.auth-nuxt-auth-utils","i-lucide-key",{"title":66,"path":67,"stem":68,"icon":69},"Auth with Better Auth","\u002Fplaybooks\u002Fauth-better-auth","3.playbooks\u002F4.auth-better-auth","i-lucide-shield",{"title":71,"path":72,"stem":73,"icon":74},"Auth with Sidebase Nuxt Auth","\u002Fplaybooks\u002Fauth-sidebase","3.playbooks\u002F5.auth-sidebase","i-lucide-user-round-check",{"title":76,"path":77,"stem":78,"icon":79},"Auth with Clerk","\u002Fplaybooks\u002Fauth-clerk","3.playbooks\u002F6.auth-clerk","i-lucide-circle-user-round",{"id":81,"title":66,"body":82,"description":874,"extension":875,"links":876,"meta":877,"navigation":878,"path":67,"seo":879,"stem":68,"__hash__":880},"docs\u002F3.playbooks\u002F4.auth-better-auth.md",{"type":83,"value":84,"toc":866},"minimark",[85,96,106,111,123,157,161,468,489,493,587,590,594,597,662,750,757,761,775,834,838,862],[86,87,88,95],"p",{},[89,90,94],"a",{"href":91,"rel":92},"https:\u002F\u002Fwww.better-auth.com",[93],"nofollow","Better Auth"," owns the sign-in endpoints, so your code doesn't run at the moment of sign-in. Instead, you derive the PostgREST token from the Better Auth session. A small server utility does it, a middleware keeps it fresh on every request, and one route lets the browser sync right after signing in or out.",[97,98,99,100,105],"note",{},"This guide was checked against Better Auth's published types, not run end to end. Better Auth's own setup for Nuxt is in ",[89,101,104],{"href":102,"rel":103},"https:\u002F\u002Fwww.better-auth.com\u002Fdocs\u002Fintegrations\u002Fnuxt",[93],"its docs",".",[107,108,110],"h2",{"id":109},"_1-before-you-start","1. Before you start",[86,112,113,114,118,119,122],{},"You need a Better Auth server instance (",[115,116,117],"code",{},"auth",") that your server code can import, for example from ",[115,120,121],{},"server\u002Futils\u002Fauth.ts",", and the JWT secret for the module:",[124,125,131],"pre",{"className":126,"code":127,"filename":128,"language":129,"meta":130,"style":130},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Must equal PostgREST's jwt-secret (PGRST_JWT_SECRET)\nNUXT_POSTGREST_JWT_SECRET=replace-with-a-secret-of-at-least-32-characters\n",".env","bash","",[115,132,133,142],{"__ignoreMap":130},[134,135,138],"span",{"class":136,"line":137},"line",1,[134,139,141],{"class":140},"sHwdD","# Must equal PostgREST's jwt-secret (PGRST_JWT_SECRET)\n",[134,143,145,149,153],{"class":136,"line":144},2,[134,146,148],{"class":147},"sTEyZ","NUXT_POSTGREST_JWT_SECRET",[134,150,152],{"class":151},"sMK4o","=",[134,154,156],{"class":155},"sfazB","replace-with-a-secret-of-at-least-32-characters\n",[107,158,160],{"id":159},"_2-turn-the-session-into-a-token","2. Turn the session into a token",[124,162,167],{"className":163,"code":164,"filename":165,"language":166,"meta":130,"style":130},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import type { H3Event } from 'h3'\n\n\u002F** Make the PostgREST token cookie match the current Better Auth session. *\u002F\nexport async function syncPostgrestToken(event: H3Event) {\n  const session = await auth.api.getSession({ headers: event.headers })\n\n  if (!session) {\n    clearPostgrestToken(event)\n    return\n  }\n\n  setPostgrestToken(\n    event,\n    await mintJwt(\n      { role: 'authenticated', sub: session.user.id },\n      { expiresAt: session.session.expiresAt },\n    ),\n  )\n}\n","server\u002Futils\u002Fpostgrest-token.ts","ts",[115,168,169,199,205,211,247,299,304,325,337,343,349,354,363,372,383,425,448,456,462],{"__ignoreMap":130},[134,170,171,175,178,181,184,187,190,193,196],{"class":136,"line":137},[134,172,174],{"class":173},"s7zQu","import",[134,176,177],{"class":173}," type",[134,179,180],{"class":151}," {",[134,182,183],{"class":147}," H3Event",[134,185,186],{"class":151}," }",[134,188,189],{"class":173}," from",[134,191,192],{"class":151}," '",[134,194,195],{"class":155},"h3",[134,197,198],{"class":151},"'\n",[134,200,201],{"class":136,"line":144},[134,202,204],{"emptyLinePlaceholder":203},true,"\n",[134,206,208],{"class":136,"line":207},3,[134,209,210],{"class":140},"\u002F** Make the PostgREST token cookie match the current Better Auth session. *\u002F\n",[134,212,214,217,221,224,228,231,235,238,241,244],{"class":136,"line":213},4,[134,215,216],{"class":173},"export",[134,218,220],{"class":219},"spNyl"," async",[134,222,223],{"class":219}," function",[134,225,227],{"class":226},"s2Zo4"," syncPostgrestToken",[134,229,230],{"class":151},"(",[134,232,234],{"class":233},"sHdIc","event",[134,236,237],{"class":151},":",[134,239,183],{"class":240},"sBMFI",[134,242,243],{"class":151},")",[134,245,246],{"class":151}," {\n",[134,248,250,253,256,259,262,265,267,270,272,275,278,281,284,286,289,291,294,296],{"class":136,"line":249},5,[134,251,252],{"class":219},"  const",[134,254,255],{"class":147}," session",[134,257,258],{"class":151}," =",[134,260,261],{"class":173}," await",[134,263,264],{"class":147}," auth",[134,266,105],{"class":151},[134,268,269],{"class":147},"api",[134,271,105],{"class":151},[134,273,274],{"class":226},"getSession",[134,276,230],{"class":277},"swJcz",[134,279,280],{"class":151},"{",[134,282,283],{"class":277}," headers",[134,285,237],{"class":151},[134,287,288],{"class":147}," event",[134,290,105],{"class":151},[134,292,293],{"class":147},"headers",[134,295,186],{"class":151},[134,297,298],{"class":277},")\n",[134,300,302],{"class":136,"line":301},6,[134,303,204],{"emptyLinePlaceholder":203},[134,305,307,310,313,316,319,322],{"class":136,"line":306},7,[134,308,309],{"class":173},"  if",[134,311,312],{"class":277}," (",[134,314,315],{"class":151},"!",[134,317,318],{"class":147},"session",[134,320,321],{"class":277},") ",[134,323,324],{"class":151},"{\n",[134,326,328,331,333,335],{"class":136,"line":327},8,[134,329,330],{"class":226},"    clearPostgrestToken",[134,332,230],{"class":277},[134,334,234],{"class":147},[134,336,298],{"class":277},[134,338,340],{"class":136,"line":339},9,[134,341,342],{"class":173},"    return\n",[134,344,346],{"class":136,"line":345},10,[134,347,348],{"class":151},"  }\n",[134,350,352],{"class":136,"line":351},11,[134,353,204],{"emptyLinePlaceholder":203},[134,355,357,360],{"class":136,"line":356},12,[134,358,359],{"class":226},"  setPostgrestToken",[134,361,362],{"class":277},"(\n",[134,364,366,369],{"class":136,"line":365},13,[134,367,368],{"class":147},"    event",[134,370,371],{"class":151},",\n",[134,373,375,378,381],{"class":136,"line":374},14,[134,376,377],{"class":173},"    await",[134,379,380],{"class":226}," mintJwt",[134,382,362],{"class":277},[134,384,386,389,392,394,396,399,402,405,408,410,412,414,417,419,422],{"class":136,"line":385},15,[134,387,388],{"class":151},"      {",[134,390,391],{"class":277}," role",[134,393,237],{"class":151},[134,395,192],{"class":151},[134,397,398],{"class":155},"authenticated",[134,400,401],{"class":151},"'",[134,403,404],{"class":151},",",[134,406,407],{"class":277}," sub",[134,409,237],{"class":151},[134,411,255],{"class":147},[134,413,105],{"class":151},[134,415,416],{"class":147},"user",[134,418,105],{"class":151},[134,420,421],{"class":147},"id",[134,423,424],{"class":151}," },\n",[134,426,428,430,433,435,437,439,441,443,446],{"class":136,"line":427},16,[134,429,388],{"class":151},[134,431,432],{"class":277}," expiresAt",[134,434,237],{"class":151},[134,436,255],{"class":147},[134,438,105],{"class":151},[134,440,318],{"class":147},[134,442,105],{"class":151},[134,444,445],{"class":147},"expiresAt",[134,447,424],{"class":151},[134,449,451,454],{"class":136,"line":450},17,[134,452,453],{"class":277},"    )",[134,455,371],{"class":151},[134,457,459],{"class":136,"line":458},18,[134,460,461],{"class":277},"  )\n",[134,463,465],{"class":136,"line":464},19,[134,466,467],{"class":151},"}\n",[86,469,470,472,473,476,477,480,481,484,485,488],{},[115,471,274],{}," returns ",[115,474,475],{},"{ session, user }"," or ",[115,478,479],{},"null",". On Nuxt 5 the event is a web-standard request, so pass ",[115,482,483],{},"event.req.headers"," instead of ",[115,486,487],{},"event.headers",". The token expires when the session does, and Better Auth extends sessions as the user stays active, so the token follows that extension because it's re-minted on each request.",[107,490,492],{"id":491},"_3-run-it-on-every-request","3. Run it on every request",[124,494,497],{"className":163,"code":495,"filename":496,"language":166,"meta":130,"style":130},"export default defineEventHandler(async (event) => {\n  \u002F\u002F Better Auth's own endpoints don't need a token\n  if (event.path.startsWith('\u002Fapi\u002Fauth')) return\n\n  await syncPostgrestToken(event)\n})\n","server\u002Fmiddleware\u002Fpostgrest-token.ts",[115,498,499,525,530,563,567,580],{"__ignoreMap":130},[134,500,501,503,506,509,511,514,516,518,520,523],{"class":136,"line":137},[134,502,216],{"class":173},[134,504,505],{"class":173}," default",[134,507,508],{"class":226}," defineEventHandler",[134,510,230],{"class":147},[134,512,513],{"class":219},"async",[134,515,312],{"class":151},[134,517,234],{"class":233},[134,519,243],{"class":151},[134,521,522],{"class":219}," =>",[134,524,246],{"class":151},[134,526,527],{"class":136,"line":144},[134,528,529],{"class":140},"  \u002F\u002F Better Auth's own endpoints don't need a token\n",[134,531,532,534,536,538,540,543,545,548,550,552,555,557,560],{"class":136,"line":207},[134,533,309],{"class":173},[134,535,312],{"class":277},[134,537,234],{"class":147},[134,539,105],{"class":151},[134,541,542],{"class":147},"path",[134,544,105],{"class":151},[134,546,547],{"class":226},"startsWith",[134,549,230],{"class":277},[134,551,401],{"class":151},[134,553,554],{"class":155},"\u002Fapi\u002Fauth",[134,556,401],{"class":151},[134,558,559],{"class":277},")) ",[134,561,562],{"class":173},"return\n",[134,564,565],{"class":136,"line":213},[134,566,204],{"emptyLinePlaceholder":203},[134,568,569,572,574,576,578],{"class":136,"line":249},[134,570,571],{"class":173},"  await",[134,573,227],{"class":226},[134,575,230],{"class":277},[134,577,234],{"class":147},[134,579,298],{"class":277},[134,581,582,585],{"class":136,"line":301},[134,583,584],{"class":151},"}",[134,586,298],{"class":147},[86,588,589],{},"Every page and API request now refreshes the cookie from the session. It clears when the session ends, and a returning user whose session is still alive gets a valid token on their first request back, before the page renders.",[107,591,593],{"id":592},"_4-sync-right-after-signing-in-or-out","4. Sync right after signing in or out",[86,595,596],{},"Better Auth completes sign-in from the browser, and a client-side navigation doesn't reach your server, so the cookie wouldn't change until the next server request. Give the browser a route to call:",[124,598,601],{"className":163,"code":599,"filename":600,"language":166,"meta":130,"style":130},"export default defineEventHandler(async (event) => {\n  await syncPostgrestToken(event)\n  return { ok: true }\n})\n","server\u002Fapi\u002Fpostgrest-token.post.ts",[115,602,603,625,637,656],{"__ignoreMap":130},[134,604,605,607,609,611,613,615,617,619,621,623],{"class":136,"line":137},[134,606,216],{"class":173},[134,608,505],{"class":173},[134,610,508],{"class":226},[134,612,230],{"class":147},[134,614,513],{"class":219},[134,616,312],{"class":151},[134,618,234],{"class":233},[134,620,243],{"class":151},[134,622,522],{"class":219},[134,624,246],{"class":151},[134,626,627,629,631,633,635],{"class":136,"line":144},[134,628,571],{"class":173},[134,630,227],{"class":226},[134,632,230],{"class":277},[134,634,234],{"class":147},[134,636,298],{"class":277},[134,638,639,642,644,647,649,653],{"class":136,"line":207},[134,640,641],{"class":173},"  return",[134,643,180],{"class":151},[134,645,646],{"class":277}," ok",[134,648,237],{"class":151},[134,650,652],{"class":651},"sfNiH"," true",[134,654,655],{"class":151}," }\n",[134,657,658,660],{"class":136,"line":213},[134,659,584],{"class":151},[134,661,298],{"class":147},[124,663,665],{"className":163,"code":664,"language":166,"meta":130,"style":130},"await authClient.signIn.email({ email, password })\nawait $fetch('\u002Fapi\u002Fpostgrest-token', { method: 'POST' })\nawait refreshNuxtData() \u002F\u002F re-run queries, now as the user\n",[115,666,667,701,737],{"__ignoreMap":130},[134,668,669,672,675,677,680,682,685,687,689,692,694,697,699],{"class":136,"line":137},[134,670,671],{"class":173},"await",[134,673,674],{"class":147}," authClient",[134,676,105],{"class":151},[134,678,679],{"class":147},"signIn",[134,681,105],{"class":151},[134,683,684],{"class":226},"email",[134,686,230],{"class":147},[134,688,280],{"class":151},[134,690,691],{"class":147}," email",[134,693,404],{"class":151},[134,695,696],{"class":147}," password ",[134,698,584],{"class":151},[134,700,298],{"class":147},[134,702,703,705,708,710,712,715,717,719,721,724,726,728,731,733,735],{"class":136,"line":144},[134,704,671],{"class":173},[134,706,707],{"class":226}," $fetch",[134,709,230],{"class":147},[134,711,401],{"class":151},[134,713,714],{"class":155},"\u002Fapi\u002Fpostgrest-token",[134,716,401],{"class":151},[134,718,404],{"class":151},[134,720,180],{"class":151},[134,722,723],{"class":277}," method",[134,725,237],{"class":151},[134,727,192],{"class":151},[134,729,730],{"class":155},"POST",[134,732,401],{"class":151},[134,734,186],{"class":151},[134,736,298],{"class":147},[134,738,739,741,744,747],{"class":136,"line":207},[134,740,671],{"class":173},[134,742,743],{"class":226}," refreshNuxtData",[134,745,746],{"class":147},"() ",[134,748,749],{"class":140},"\u002F\u002F re-run queries, now as the user\n",[86,751,752,753,756],{},"Do the same after ",[115,754,755],{},"authClient.signOut()",": the route finds no session and clears the cookie.",[107,758,760],{"id":759},"_5-query-as-the-user","5. Query as the user",[86,762,763,766,767,770,771,774],{},[115,764,765],{},"usePostgrest()"," and ",[115,768,769],{},"usePostgrestUser(event)"," now run as the signed-in user, and your RLS policies can read the ",[115,772,773],{},"sub"," claim:",[124,776,778],{"className":163,"code":777,"language":166,"meta":130,"style":130},"const { data } = await usePostgrest().from('todos').select('*')\n",[115,779,780],{"__ignoreMap":130},[134,781,782,785,787,790,792,794,796,799,802,804,807,809,811,814,816,818,820,823,825,827,830,832],{"class":136,"line":137},[134,783,784],{"class":219},"const",[134,786,180],{"class":151},[134,788,789],{"class":147}," data ",[134,791,584],{"class":151},[134,793,258],{"class":151},[134,795,261],{"class":173},[134,797,798],{"class":226}," usePostgrest",[134,800,801],{"class":147},"()",[134,803,105],{"class":151},[134,805,806],{"class":226},"from",[134,808,230],{"class":147},[134,810,401],{"class":151},[134,812,813],{"class":155},"todos",[134,815,401],{"class":151},[134,817,243],{"class":147},[134,819,105],{"class":151},[134,821,822],{"class":226},"select",[134,824,230],{"class":147},[134,826,401],{"class":151},[134,828,829],{"class":155},"*",[134,831,401],{"class":151},[134,833,298],{"class":147},[107,835,837],{"id":836},"things-to-know","Things to know",[839,840,841,853],"ul",{},[842,843,844,848,849,852],"li",{},[845,846,847],"strong",{},"Revocation."," If you revoke a session in Better Auth, the token already in the browser still works until its ",[115,850,851],{},"exp",". The middleware removes it on that user's next request.",[842,854,855,858,859,861],{},[845,856,857],{},"Cost."," ",[115,860,274],{}," runs on every request. If you render pages for signed-in users anyway, Better Auth is already doing this lookup, but check that it hits your session cache.",[863,864,865],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":130,"searchDepth":144,"depth":144,"links":867},[868,869,870,871,872,873],{"id":109,"depth":144,"text":110},{"id":159,"depth":144,"text":160},{"id":491,"depth":144,"text":492},{"id":592,"depth":144,"text":593},{"id":759,"depth":144,"text":760},{"id":836,"depth":144,"text":837},"Keep a PostgREST token in step with your Better Auth session.","md",null,{},{"icon":69},{"title":66,"description":874},"QmPvMM3uudgabz_Ia_2YQMW67am1ihYASSBm8mnc2x0",[882,884],{"title":61,"path":62,"stem":63,"description":883,"icon":64,"children":-1},"Sign users in with nuxt-auth-utils and run every PostgREST query as them.",{"title":71,"path":72,"stem":73,"description":885,"icon":74,"children":-1},"Keep a PostgREST token in step with your Auth.js session.",1792365098928]