[{"data":1,"prerenderedAt":846},["ShallowReactive",2],{"navigation_docs":3,"-playbooks-auth-clerk":80,"-playbooks-auth-clerk-surround":843},[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":76,"body":82,"description":836,"extension":837,"links":838,"meta":839,"navigation":840,"path":77,"seo":841,"stem":78,"__hash__":842},"docs\u002F3.playbooks\u002F6.auth-clerk.md",{"type":83,"value":84,"toc":828},"minimark",[85,96,110,115,121,155,159,170,435,443,447,499,502,513,517,520,585,591,731,735,794,801,805,824],[86,87,88,95],"p",{},[89,90,94],"a",{"href":91,"rel":92},"https:\u002F\u002Fclerk.com\u002Fdocs\u002Fnuxt\u002Fgetting-started\u002Fquickstart",[93],"nofollow","Clerk"," signs users in from the browser and puts the session on every server request through its Nuxt module. Your server reads who the user is, mints a PostgREST token for them, and stores it. A middleware does that on every request, and a small watcher syncs the browser when the user signs in or out.",[97,98,99,100,104,105,109],"note",{},"This guide was checked against ",[101,102,103],"code",{},"@clerk\u002Fnuxt","'s published types, not run end to end. Set up Clerk itself with ",[89,106,108],{"href":91,"rel":107},[93],"its docs",".",[111,112,114],"h2",{"id":113},"_1-before-you-start","1. Before you start",[86,116,117,118,120],{},"You need ",[101,119,103],{}," set up, which registers its server middleware for you, and the JWT secret for the module:",[122,123,129],"pre",{"className":124,"code":125,"filename":126,"language":127,"meta":128,"style":128},"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","",[101,130,131,140],{"__ignoreMap":128},[132,133,136],"span",{"class":134,"line":135},"line",1,[132,137,139],{"class":138},"sHwdD","# Must equal PostgREST's jwt-secret (PGRST_JWT_SECRET)\n",[132,141,143,147,151],{"class":134,"line":142},2,[132,144,146],{"class":145},"sTEyZ","NUXT_POSTGREST_JWT_SECRET",[132,148,150],{"class":149},"sMK4o","=",[132,152,154],{"class":153},"sfazB","replace-with-a-secret-of-at-least-32-characters\n",[111,156,158],{"id":157},"_2-turn-the-session-into-a-token","2. Turn the session into a token",[86,160,161,162,165,166,169],{},"Clerk's middleware makes ",[101,163,164],{},"event.context.auth()"," available on the server. It returns the signed-in user's id, or ",[101,167,168],{},"null"," when nobody is signed in:",[122,171,176],{"className":172,"code":173,"filename":174,"language":175,"meta":128,"style":128},"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 Clerk session. *\u002F\nexport async function syncPostgrestToken(event: H3Event) {\n  const { userId } = event.context.auth()\n\n  if (!userId) {\n    clearPostgrestToken(event)\n    return\n  }\n\n  setPostgrestToken(\n    event,\n    await mintJwt({ role: 'authenticated', sub: userId }, { expiresIn: '1h' }),\n  )\n}\n","server\u002Futils\u002Fpostgrest-token.ts","ts",[101,177,178,208,214,220,256,289,294,315,328,334,340,345,354,363,423,429],{"__ignoreMap":128},[132,179,180,184,187,190,193,196,199,202,205],{"class":134,"line":135},[132,181,183],{"class":182},"s7zQu","import",[132,185,186],{"class":182}," type",[132,188,189],{"class":149}," {",[132,191,192],{"class":145}," H3Event",[132,194,195],{"class":149}," }",[132,197,198],{"class":182}," from",[132,200,201],{"class":149}," '",[132,203,204],{"class":153},"h3",[132,206,207],{"class":149},"'\n",[132,209,210],{"class":134,"line":142},[132,211,213],{"emptyLinePlaceholder":212},true,"\n",[132,215,217],{"class":134,"line":216},3,[132,218,219],{"class":138},"\u002F** Make the PostgREST token cookie match the current Clerk session. *\u002F\n",[132,221,223,226,230,233,237,240,244,247,250,253],{"class":134,"line":222},4,[132,224,225],{"class":182},"export",[132,227,229],{"class":228},"spNyl"," async",[132,231,232],{"class":228}," function",[132,234,236],{"class":235},"s2Zo4"," syncPostgrestToken",[132,238,239],{"class":149},"(",[132,241,243],{"class":242},"sHdIc","event",[132,245,246],{"class":149},":",[132,248,192],{"class":249},"sBMFI",[132,251,252],{"class":149},")",[132,254,255],{"class":149}," {\n",[132,257,259,262,264,267,269,272,275,277,280,282,285],{"class":134,"line":258},5,[132,260,261],{"class":228},"  const",[132,263,189],{"class":149},[132,265,266],{"class":145}," userId",[132,268,195],{"class":149},[132,270,271],{"class":149}," =",[132,273,274],{"class":145}," event",[132,276,109],{"class":149},[132,278,279],{"class":145},"context",[132,281,109],{"class":149},[132,283,284],{"class":235},"auth",[132,286,288],{"class":287},"swJcz","()\n",[132,290,292],{"class":134,"line":291},6,[132,293,213],{"emptyLinePlaceholder":212},[132,295,297,300,303,306,309,312],{"class":134,"line":296},7,[132,298,299],{"class":182},"  if",[132,301,302],{"class":287}," (",[132,304,305],{"class":149},"!",[132,307,308],{"class":145},"userId",[132,310,311],{"class":287},") ",[132,313,314],{"class":149},"{\n",[132,316,318,321,323,325],{"class":134,"line":317},8,[132,319,320],{"class":235},"    clearPostgrestToken",[132,322,239],{"class":287},[132,324,243],{"class":145},[132,326,327],{"class":287},")\n",[132,329,331],{"class":134,"line":330},9,[132,332,333],{"class":182},"    return\n",[132,335,337],{"class":134,"line":336},10,[132,338,339],{"class":149},"  }\n",[132,341,343],{"class":134,"line":342},11,[132,344,213],{"emptyLinePlaceholder":212},[132,346,348,351],{"class":134,"line":347},12,[132,349,350],{"class":235},"  setPostgrestToken",[132,352,353],{"class":287},"(\n",[132,355,357,360],{"class":134,"line":356},13,[132,358,359],{"class":145},"    event",[132,361,362],{"class":149},",\n",[132,364,366,369,372,374,377,380,382,384,387,390,393,396,398,400,403,405,408,410,412,415,417,419,421],{"class":134,"line":365},14,[132,367,368],{"class":182},"    await",[132,370,371],{"class":235}," mintJwt",[132,373,239],{"class":287},[132,375,376],{"class":149},"{",[132,378,379],{"class":287}," role",[132,381,246],{"class":149},[132,383,201],{"class":149},[132,385,386],{"class":153},"authenticated",[132,388,389],{"class":149},"'",[132,391,392],{"class":149},",",[132,394,395],{"class":287}," sub",[132,397,246],{"class":149},[132,399,266],{"class":145},[132,401,402],{"class":149}," },",[132,404,189],{"class":149},[132,406,407],{"class":287}," expiresIn",[132,409,246],{"class":149},[132,411,201],{"class":149},[132,413,414],{"class":153},"1h",[132,416,389],{"class":149},[132,418,195],{"class":149},[132,420,252],{"class":287},[132,422,362],{"class":149},[132,424,426],{"class":134,"line":425},15,[132,427,428],{"class":287},"  )\n",[132,430,432],{"class":134,"line":431},16,[132,433,434],{"class":149},"}\n",[86,436,437,438,442],{},"Clerk's session tokens are short-lived (around a minute) and Clerk refreshes them in the browser, so there's no long expiry to match. Choose how long a PostgREST token should live instead, and see ",[89,439,441],{"href":440},"\u002Fguide\u002Fauthentication#expiry","Expiry"," for the trade-off.",[111,444,446],{"id":445},"_3-run-it-on-every-request","3. Run it on every request",[122,448,451],{"className":172,"code":449,"filename":450,"language":175,"meta":128,"style":128},"export default defineEventHandler(async (event) => {\n  await syncPostgrestToken(event)\n})\n","server\u002Fmiddleware\u002Fpostgrest-token.ts",[101,452,453,479,492],{"__ignoreMap":128},[132,454,455,457,460,463,465,468,470,472,474,477],{"class":134,"line":135},[132,456,225],{"class":182},[132,458,459],{"class":182}," default",[132,461,462],{"class":235}," defineEventHandler",[132,464,239],{"class":145},[132,466,467],{"class":228},"async",[132,469,302],{"class":149},[132,471,243],{"class":242},[132,473,252],{"class":149},[132,475,476],{"class":228}," =>",[132,478,255],{"class":149},[132,480,481,484,486,488,490],{"class":134,"line":142},[132,482,483],{"class":182},"  await",[132,485,236],{"class":235},[132,487,239],{"class":287},[132,489,243],{"class":145},[132,491,327],{"class":287},[132,493,494,497],{"class":134,"line":216},[132,495,496],{"class":149},"}",[132,498,327],{"class":145},[86,500,501],{},"The cookie is minted for signed-in users, renewed on each request, and cleared once Clerk reports no user.",[503,504,505,508,509,512],"warning",{},[101,506,507],{},"event.context.auth"," is set by Clerk's own middleware, so it has to run before this one. If you see ",[101,510,511],{},"event.context.auth is not a function",", check the order the two middlewares run in.",[111,514,516],{"id":515},"_4-sync-the-browser-on-sign-in-and-sign-out","4. Sync the browser on sign-in and sign-out",[86,518,519],{},"Clerk signs users in from the browser, and a client-side navigation doesn't reach your server. Give the browser a route to call:",[122,521,524],{"className":172,"code":522,"filename":523,"language":175,"meta":128,"style":128},"export default defineEventHandler(async (event) => {\n  await syncPostgrestToken(event)\n  return { ok: true }\n})\n","server\u002Fapi\u002Fpostgrest-token.post.ts",[101,525,526,548,560,579],{"__ignoreMap":128},[132,527,528,530,532,534,536,538,540,542,544,546],{"class":134,"line":135},[132,529,225],{"class":182},[132,531,459],{"class":182},[132,533,462],{"class":235},[132,535,239],{"class":145},[132,537,467],{"class":228},[132,539,302],{"class":149},[132,541,243],{"class":242},[132,543,252],{"class":149},[132,545,476],{"class":228},[132,547,255],{"class":149},[132,549,550,552,554,556,558],{"class":134,"line":142},[132,551,483],{"class":182},[132,553,236],{"class":235},[132,555,239],{"class":287},[132,557,243],{"class":145},[132,559,327],{"class":287},[132,561,562,565,567,570,572,576],{"class":134,"line":216},[132,563,564],{"class":182},"  return",[132,566,189],{"class":149},[132,568,569],{"class":287}," ok",[132,571,246],{"class":149},[132,573,575],{"class":574},"sfNiH"," true",[132,577,578],{"class":149}," }\n",[132,580,581,583],{"class":134,"line":222},[132,582,496],{"class":149},[132,584,327],{"class":145},[86,586,587,588,246],{},"Then call it whenever the signed-in state changes, for example in ",[101,589,590],{},"app.vue",[122,592,597],{"className":593,"code":594,"filename":595,"language":596,"meta":128,"style":128},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nconst { isSignedIn } = useAuth()\n\nwatch(isSignedIn, async () => {\n  await $fetch('\u002Fapi\u002Fpostgrest-token', { method: 'POST' })\n  await refreshNuxtData() \u002F\u002F re-run queries as the new user\n})\n\u003C\u002Fscript>\n","app\u002Fapp.vue","vue",[101,598,599,625,644,648,667,703,716,722],{"__ignoreMap":128},[132,600,601,604,607,610,613,615,618,620,622],{"class":134,"line":135},[132,602,603],{"class":149},"\u003C",[132,605,606],{"class":287},"script",[132,608,609],{"class":228}," setup",[132,611,612],{"class":228}," lang",[132,614,150],{"class":149},[132,616,617],{"class":149},"\"",[132,619,175],{"class":153},[132,621,617],{"class":149},[132,623,624],{"class":149},">\n",[132,626,627,630,632,635,637,639,642],{"class":134,"line":142},[132,628,629],{"class":228},"const",[132,631,189],{"class":149},[132,633,634],{"class":145}," isSignedIn ",[132,636,496],{"class":149},[132,638,271],{"class":149},[132,640,641],{"class":235}," useAuth",[132,643,288],{"class":145},[132,645,646],{"class":134,"line":216},[132,647,213],{"emptyLinePlaceholder":212},[132,649,650,653,656,658,660,663,665],{"class":134,"line":222},[132,651,652],{"class":235},"watch",[132,654,655],{"class":145},"(isSignedIn",[132,657,392],{"class":149},[132,659,229],{"class":228},[132,661,662],{"class":149}," ()",[132,664,476],{"class":228},[132,666,255],{"class":149},[132,668,669,671,674,676,678,681,683,685,687,690,692,694,697,699,701],{"class":134,"line":258},[132,670,483],{"class":182},[132,672,673],{"class":235}," $fetch",[132,675,239],{"class":287},[132,677,389],{"class":149},[132,679,680],{"class":153},"\u002Fapi\u002Fpostgrest-token",[132,682,389],{"class":149},[132,684,392],{"class":149},[132,686,189],{"class":149},[132,688,689],{"class":287}," method",[132,691,246],{"class":149},[132,693,201],{"class":149},[132,695,696],{"class":153},"POST",[132,698,389],{"class":149},[132,700,195],{"class":149},[132,702,327],{"class":287},[132,704,705,707,710,713],{"class":134,"line":291},[132,706,483],{"class":182},[132,708,709],{"class":235}," refreshNuxtData",[132,711,712],{"class":287},"() ",[132,714,715],{"class":138},"\u002F\u002F re-run queries as the new user\n",[132,717,718,720],{"class":134,"line":296},[132,719,496],{"class":149},[132,721,327],{"class":145},[132,723,724,727,729],{"class":134,"line":317},[132,725,726],{"class":149},"\u003C\u002F",[132,728,606],{"class":287},[132,730,624],{"class":149},[111,732,734],{"id":733},"_5-query-as-the-user","5. Query as the user",[122,736,738],{"className":172,"code":737,"language":175,"meta":128,"style":128},"const { data } = await usePostgrest().from('todos').select('*')\n",[101,739,740],{"__ignoreMap":128},[132,741,742,744,746,749,751,753,756,759,762,764,767,769,771,774,776,778,780,783,785,787,790,792],{"class":134,"line":135},[132,743,629],{"class":228},[132,745,189],{"class":149},[132,747,748],{"class":145}," data ",[132,750,496],{"class":149},[132,752,271],{"class":149},[132,754,755],{"class":182}," await",[132,757,758],{"class":235}," usePostgrest",[132,760,761],{"class":145},"()",[132,763,109],{"class":149},[132,765,766],{"class":235},"from",[132,768,239],{"class":145},[132,770,389],{"class":149},[132,772,773],{"class":153},"todos",[132,775,389],{"class":149},[132,777,252],{"class":145},[132,779,109],{"class":149},[132,781,782],{"class":235},"select",[132,784,239],{"class":145},[132,786,389],{"class":149},[132,788,789],{"class":153},"*",[132,791,389],{"class":149},[132,793,327],{"class":145},[86,795,796,797,800],{},"Your RLS policies can read the ",[101,798,799],{},"sub"," claim, which is the Clerk user id.",[111,802,804],{"id":803},"things-to-know","Things to know",[806,807,808],"ul",{},[809,810,811,815,816,819,820,823],"li",{},[812,813,814],"strong",{},"Revocation."," If a user is signed out or banned, a token already in the browser keeps working until its ",[101,817,818],{},"exp",". The middleware removes it on that user's next request. Use a shorter ",[101,821,822],{},"expiresIn"," if that window matters.",[825,826,827],"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":128,"searchDepth":142,"depth":142,"links":829},[830,831,832,833,834,835],{"id":113,"depth":142,"text":114},{"id":157,"depth":142,"text":158},{"id":445,"depth":142,"text":446},{"id":515,"depth":142,"text":516},{"id":733,"depth":142,"text":734},{"id":803,"depth":142,"text":804},"Run PostgREST queries as the user who is signed in with Clerk.","md",null,{},{"icon":79},{"title":76,"description":836},"jqF2yQtDoIsBbK8-GQACobCuG3gCBhG8B-1WtiFsCyk",[844,838],{"title":71,"path":72,"stem":73,"description":845,"icon":74,"children":-1},"Keep a PostgREST token in step with your Auth.js session.",1792365098928]