[{"data":1,"prerenderedAt":2473},["ShallowReactive",2],{"navigation_docs":3,"-core-concepts-route-protection":216,"-core-concepts-route-protection-surround":2468},[4,42,68,115,136,157],{"title":5,"path":6,"stem":7,"children":8,"icon":11},"Getting Started","\u002Fgetting-started","1.getting-started\u002F0.index",[9,12,17,22,27,32,37],{"title":10,"path":6,"stem":7,"icon":11},"Introduction","i-lucide-sparkles",{"title":13,"path":14,"stem":15,"icon":16},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F1.installation","i-lucide-download",{"title":18,"path":19,"stem":20,"icon":21},"Configuration","\u002Fgetting-started\u002Fconfiguration","1.getting-started\u002F2.configuration","i-lucide-settings",{"title":23,"path":24,"stem":25,"icon":26},"Client Setup","\u002Fgetting-started\u002Fclient-setup","1.getting-started\u002F3.client-setup","i-lucide-monitor",{"title":28,"path":29,"stem":30,"icon":31},"Type Augmentation","\u002Fgetting-started\u002Ftype-augmentation","1.getting-started\u002F4.type-augmentation","i-lucide-type",{"title":33,"path":34,"stem":35,"icon":36},"Schema Generation (NuxtHub)","\u002Fgetting-started\u002Fschema-generation","1.getting-started\u002F5.schema-generation","i-lucide-database",{"title":38,"path":39,"stem":40,"icon":41},"How It Works","\u002Fgetting-started\u002Fhow-it-works","1.getting-started\u002F6.how-it-works","i-lucide-workflow",{"title":43,"path":44,"stem":45,"children":46,"page":67},"Core Concepts","\u002Fcore-concepts","2.core-concepts",[47,51,55,59,63],{"title":48,"path":49,"stem":50},"serverAuth()","\u002Fcore-concepts\u002Fserver-auth","2.core-concepts\u002F1.server-auth",{"title":52,"path":53,"stem":54},"Sessions","\u002Fcore-concepts\u002Fsessions","2.core-concepts\u002F2.sessions",{"title":56,"path":57,"stem":58},"Route Protection","\u002Fcore-concepts\u002Froute-protection","2.core-concepts\u002F3.route-protection",{"title":60,"path":61,"stem":62},"Auto‑Imports and Aliases","\u002Fcore-concepts\u002Fauto-imports-aliases","2.core-concepts\u002F4.auto-imports-aliases",{"title":64,"path":65,"stem":66},"Security & Caveats","\u002Fcore-concepts\u002Fsecurity-caveats","2.core-concepts\u002F5.security-caveats",false,{"title":69,"path":70,"stem":71,"children":72,"page":67},"Guides","\u002Fguides","3.guides",[73,77,82,86,90,94,99,103,107,111],{"title":74,"path":75,"stem":76},"Role‑Based Access","\u002Fguides\u002Frole-based-access","3.guides\u002F1.role-based-access",{"title":78,"path":79,"stem":80,"icon":81},"Setup diagnostics","\u002Fguides\u002Fdiagnostics","3.guides\u002F10.diagnostics","i-lucide-circle-alert",{"title":83,"path":84,"stem":85},"OAuth Providers","\u002Fguides\u002Foauth-providers","3.guides\u002F2.oauth-providers",{"title":87,"path":88,"stem":89},"Custom Database","\u002Fguides\u002Fcustom-database","3.guides\u002F3.custom-database",{"title":91,"path":92,"stem":93},"Database-less Mode","\u002Fguides\u002Fdatabase-less-mode","3.guides\u002F4.database-less-mode",{"title":95,"path":96,"stem":97,"icon":98},"External Auth Backend","\u002Fguides\u002Fexternal-auth-backend","3.guides\u002F5.external-auth-backend","i-lucide-server",{"title":100,"path":101,"stem":102},"Migrating from nuxt-auth-utils","\u002Fguides\u002Fmigrate-from-nuxt-auth-utils","3.guides\u002F6.migrate-from-nuxt-auth-utils",{"title":104,"path":105,"stem":106},"Two-Factor Authentication (TOTP + Backup Codes)","\u002Fguides\u002Ftwo-factor-auth","3.guides\u002F7.two-factor-auth",{"title":108,"path":109,"stem":110},"Testing","\u002Fguides\u002Ftesting","3.guides\u002F8.testing",{"title":112,"path":113,"stem":114},"Production Deployment","\u002Fguides\u002Fproduction-deployment","3.guides\u002F9.production-deployment",{"title":116,"path":117,"stem":118,"children":119,"page":67},"Integrations","\u002Fintegrations","4.integrations",[120,124,128,132],{"title":121,"path":122,"stem":123},"NuxtHub","\u002Fintegrations\u002Fnuxthub","4.integrations\u002F1.nuxthub",{"title":125,"path":126,"stem":127},"DevTools","\u002Fintegrations\u002Fdevtools","4.integrations\u002F2.devtools",{"title":129,"path":130,"stem":131},"Convex","\u002Fintegrations\u002Fconvex","4.integrations\u002F3.convex",{"title":133,"path":134,"stem":135},"i18n","\u002Fintegrations\u002Fi18n","4.integrations\u002F4.i18n",{"title":137,"path":138,"stem":139,"children":140,"page":67},"API Reference","\u002Fapi","5.api",[141,145,149,153],{"title":142,"path":143,"stem":144},"Composables","\u002Fapi\u002Fcomposables","5.api\u002F1.composables",{"title":146,"path":147,"stem":148},"Server Utilities","\u002Fapi\u002Fserver-utils","5.api\u002F2.server-utils",{"title":150,"path":151,"stem":152},"Components","\u002Fapi\u002Fcomponents","5.api\u002F3.components",{"title":154,"path":155,"stem":156},"Types","\u002Fapi\u002Ftypes","5.api\u002F4.types",{"title":158,"path":159,"stem":160,"children":161,"icon":81},"Errors","\u002Ferrors","6.errors\u002F0.index",[162,164,168,172,176,180,184,188,192,196,200,204,208,212],{"title":163,"path":159,"stem":160},"Overview",{"title":165,"path":166,"stem":167},"Better Auth CLI is not installed","\u002Ferrors\u002Fnuxt-auth-cli-missing","6.errors\u002Fnuxt-auth-cli-missing",{"title":169,"path":170,"stem":171},"Better Auth CLI version does not match better-auth","\u002Ferrors\u002Fnuxt-auth-cli-version-mismatch","6.errors\u002Fnuxt-auth-cli-version-mismatch",{"title":173,"path":174,"stem":175},"Auth configuration failed to load","\u002Ferrors\u002Fnuxt-auth-config-load-failed","6.errors\u002Fnuxt-auth-config-load-failed",{"title":177,"path":178,"stem":179},"Schema generator returned no code","\u002Ferrors\u002Fnuxt-auth-empty-schema","6.errors\u002Fnuxt-auth-empty-schema",{"title":181,"path":182,"stem":183},"Invalid server auth config export","\u002Ferrors\u002Fnuxt-auth-invalid-config-export","6.errors\u002Fnuxt-auth-invalid-config-export",{"title":185,"path":186,"stem":187},"Auth plugin path must be absolute","\u002Ferrors\u002Fnuxt-auth-invalid-plugin-source","6.errors\u002Fnuxt-auth-invalid-plugin-source",{"title":189,"path":190,"stem":191},"Missing auth configuration","\u002Ferrors\u002Fnuxt-auth-missing-config","6.errors\u002Fnuxt-auth-missing-config",{"title":193,"path":194,"stem":195},"NuxtHub database alias is missing","\u002Ferrors\u002Fnuxt-auth-missing-hub-db","6.errors\u002Fnuxt-auth-missing-hub-db",{"title":197,"path":198,"stem":199},"No database provider is enabled","\u002Ferrors\u002Fnuxt-auth-no-database-provider","6.errors\u002Fnuxt-auth-no-database-provider",{"title":201,"path":202,"stem":203},"Auth schema generation failed","\u002Ferrors\u002Fnuxt-auth-schema-generation-failed","6.errors\u002Fnuxt-auth-schema-generation-failed",{"title":205,"path":206,"stem":207},"Custom session storage is missing","\u002Ferrors\u002Fnuxt-auth-schema-storage-required","6.errors\u002Fnuxt-auth-schema-storage-required",{"title":209,"path":210,"stem":211},"Secondary storage in client-only mode","\u002Ferrors\u002Fnuxt-auth-storage-client-only","6.errors\u002Fnuxt-auth-storage-client-only",{"title":213,"path":214,"stem":215},"Unsupported database dialect","\u002Ferrors\u002Fnuxt-auth-unsupported-dialect","6.errors\u002Fnuxt-auth-unsupported-dialect",{"id":217,"title":56,"body":218,"description":2462,"extension":2463,"links":2464,"meta":2465,"navigation":248,"path":57,"seo":2466,"stem":58,"__hash__":2467},"docs\u002F2.core-concepts\u002F3.route-protection.md",{"type":219,"value":220,"toc":2444},"minimark",[221,298,302,307,388,395,400,407,454,469,562,566,580,814,819,957,969,981,1004,1007,1035,1039,1048,1103,1107,1110,1206,1210,1216,1309,1313,1318,1403,1436,1439,1443,1446,1505,1516,1527,1530,2176,2188,2192,2196,2199,2265,2269,2376,2380,2386,2389,2420,2430,2433,2440],[222,223,224],"code-collapse",{},[225,226,232],"pre",{"className":227,"code":228,"filename":229,"language":230,"meta":231,"style":231},"language-txt shiki shiki-themes one-light synthwave-84 synthwave-84","Protect routes and API endpoints with @nuxtjs\u002Fbetter-auth.\n\n- Route Rules in `nuxt.config.ts`: `routeRules: { '\u002Fapp\u002F**': { auth: { only: 'user', redirectTo: '\u002Flogin' } } }`\n- Guest-only pages: `{ auth: { only: 'guest', redirectTo: '\u002Fapp' } }`\n- Role-based: `{ auth: { user: { role: 'admin' } } }` — arrays use OR logic\n- Page Meta: `definePageMeta({ auth: 'user' })` for per-page control\n- Server API: `await requireUserSession(event)` — throws 401 if not authenticated\n- Role + custom rules: `requireUserSession(event, { user: { role: 'admin' }, rule: ({ user }) => user.verified })`\n- Route rules auto-sync to client router for instant redirects\n- Always protect API endpoints with `requireUserSession` — route rules are UX only\n","Prompt","txt","",[233,234,235,243,250,256,262,268,274,280,286,292],"code",{"__ignoreMap":231},[236,237,240],"span",{"class":238,"line":239},"line",1,[236,241,242],{},"Protect routes and API endpoints with @nuxtjs\u002Fbetter-auth.\n",[236,244,246],{"class":238,"line":245},2,[236,247,249],{"emptyLinePlaceholder":248},true,"\n",[236,251,253],{"class":238,"line":252},3,[236,254,255],{},"- Route Rules in `nuxt.config.ts`: `routeRules: { '\u002Fapp\u002F**': { auth: { only: 'user', redirectTo: '\u002Flogin' } } }`\n",[236,257,259],{"class":238,"line":258},4,[236,260,261],{},"- Guest-only pages: `{ auth: { only: 'guest', redirectTo: '\u002Fapp' } }`\n",[236,263,265],{"class":238,"line":264},5,[236,266,267],{},"- Role-based: `{ auth: { user: { role: 'admin' } } }` — arrays use OR logic\n",[236,269,271],{"class":238,"line":270},6,[236,272,273],{},"- Page Meta: `definePageMeta({ auth: 'user' })` for per-page control\n",[236,275,277],{"class":238,"line":276},7,[236,278,279],{},"- Server API: `await requireUserSession(event)` — throws 401 if not authenticated\n",[236,281,283],{"class":238,"line":282},8,[236,284,285],{},"- Role + custom rules: `requireUserSession(event, { user: { role: 'admin' }, rule: ({ user }) => user.verified })`\n",[236,287,289],{"class":238,"line":288},9,[236,290,291],{},"- Route rules auto-sync to client router for instant redirects\n",[236,293,295],{"class":238,"line":294},10,[236,296,297],{},"- Always protect API endpoints with `requireUserSession` — route rules are UX only\n",[299,300,301],"p",{},"Use this page when you need to protect Nuxt pages, layouts, and API routes without guessing which layer is responsible for what.",[303,304,306],"h2",{"id":305},"quick-reference","Quick Reference",[308,309,310,327],"table",{},[311,312,313],"thead",{},[314,315,316,321,324],"tr",{},[317,318,320],"th",{"align":319},"left","Method",[317,322,323],{"align":319},"Scope",[317,325,326],{"align":319},"Use Case",[328,329,330,349,362,375],"tbody",{},[314,331,332,339,342],{},[333,334,335],"td",{"align":319},[336,337,338],"strong",{},"Route Rules",[333,340,341],{"align":319},"Global",[333,343,344,345,348],{"align":319},"Protecting whole sections (e.g., ",[233,346,347],{},"\u002Fadmin\u002F**",").",[314,350,351,356,359],{},[333,352,353],{"align":319},[336,354,355],{},"Page Meta",[333,357,358],{"align":319},"Per-Page",[333,360,361],{"align":319},"Specific logic for a single page.",[314,363,364,369,372],{},[333,365,366],{"align":319},[336,367,368],{},"Middleware",[333,370,371],{"align":319},"Client",[333,373,374],{"align":319},"Complex client-side navigation logic.",[314,376,377,382,385],{},[333,378,379],{"align":319},[336,380,381],{},"Server Utils",[333,383,384],{"align":319},"API",[333,386,387],{"align":319},"Protecting API endpoints.",[299,389,390,391,394],{},"Route rules and page meta handle navigation and UX. Server-side checks with ",[233,392,393],{},"requireUserSession(event)"," remain the actual security boundary for protected data and mutations.",[396,397,399],"h3",{"id":398},"matching-logic","Matching Logic",[299,401,402,403,406],{},"Role arrays use ",[336,404,405],{},"OR"," logic: the user needs any one of the listed values.",[225,408,412],{"className":409,"code":410,"language":411,"meta":231,"style":231},"language-ts shiki shiki-themes one-light synthwave-84 synthwave-84","\u002F\u002F User needs role 'admin' OR 'moderator' (not both)\nauth: { user: { role: ['admin', 'moderator'] } }\n","ts",[233,413,414,420],{"__ignoreMap":231},[236,415,416],{"class":238,"line":239},[236,417,419],{"class":418},"st7cf","\u002F\u002F User needs role 'admin' OR 'moderator' (not both)\n",[236,421,422,426,430,433,435,438,441,445,448,451],{"class":238,"line":245},[236,423,425],{"class":424},"sivOE","auth",[236,427,429],{"class":428},"s17Py",": { ",[236,431,432],{"class":424},"user",[236,434,429],{"class":428},[236,436,437],{"class":424},"role",[236,439,440],{"class":428},": [",[236,442,444],{"class":443},"sPAZv","'admin'",[236,446,447],{"class":428},", ",[236,449,450],{"class":443},"'moderator'",[236,452,453],{"class":428},"] } }\n",[299,455,456,457,460,461,464,465,468],{},"For ",[336,458,459],{},"AND"," logic (user needs multiple conditions), use ",[233,462,463],{},"requireUserSession"," with a ",[233,466,467],{},"rule"," callback in your server handlers:",[225,470,473],{"className":409,"code":471,"filename":472,"language":411,"meta":231,"style":231},"await requireUserSession(event, {\n  user: { role: 'admin' },\n  rule: ({ user }) => user.verified === true,\n})\n","server\u002Fapi\u002Fadmin\u002Freport.ts",[233,474,475,495,518,557],{"__ignoreMap":231},[236,476,477,481,485,488,492],{"class":238,"line":239},[236,478,480],{"class":479},"sqe1H","await",[236,482,484],{"class":483},"sfT9l"," requireUserSession",[236,486,487],{"class":428},"(",[236,489,491],{"class":490},"svFNh","event",[236,493,494],{"class":428},", {\n",[236,496,497,501,505,508,510,512,515],{"class":238,"line":245},[236,498,500],{"class":499},"sYvLG","  user",[236,502,504],{"class":503},"sVnqq",":",[236,506,507],{"class":428}," { ",[236,509,437],{"class":499},[236,511,504],{"class":503},[236,513,514],{"class":443}," 'admin'",[236,516,517],{"class":428}," },\n",[236,519,520,523,525,528,531,534,537,540,543,546,550,554],{"class":238,"line":252},[236,521,522],{"class":483},"  rule",[236,524,504],{"class":503},[236,526,527],{"class":428}," ({ ",[236,529,432],{"class":530},"sgisi",[236,532,533],{"class":428}," }) ",[236,535,536],{"class":479},"=>",[236,538,539],{"class":490}," user",[236,541,542],{"class":428},".",[236,544,545],{"class":499},"verified",[236,547,549],{"class":548},"sn-Jc"," ===",[236,551,553],{"class":552},"s3ZNE"," true",[236,555,556],{"class":428},",\n",[236,558,559],{"class":238,"line":258},[236,560,561],{"class":428},"})\n",[303,563,565],{"id":564},"_1-route-rules","1. Route Rules",[299,567,568,569,572,573,576,577,542],{},"The most efficient way to protect your app is using route rules in ",[233,570,571],{},"nuxt.config.ts",". You can define them under ",[233,574,575],{},"routeRules"," or ",[233,578,579],{},"nitro.routeRules",[225,581,583],{"className":409,"code":582,"filename":571,"language":411,"meta":231,"style":231},"export default defineNuxtConfig({\n  routeRules: {\n    \u002F\u002F Authenticated users only\n    '\u002Fapp\u002F**': { auth: { only: 'user', redirectTo: '\u002Flogin' } },\n    \n    \u002F\u002F Guests only (e.g. login page)\n    '\u002Flogin': { auth: { only: 'guest', redirectTo: '\u002Fapp' } },\n    \n    \u002F\u002F Admin role only\n    '\u002Fadmin\u002F**': { auth: { user: { role: 'admin' } } },\n    \n    \u002F\u002F Admin OR Moderator\n    '\u002Fstaff\u002F**': { \n      auth: { \n        user: { role: ['admin', 'moderator'] } \n      } \n    }\n  }\n})\n",[233,584,585,600,610,615,651,656,661,694,698,703,733,738,744,755,765,791,797,803,809],{"__ignoreMap":231},[236,586,587,590,594,597],{"class":238,"line":239},[236,588,589],{"class":479},"export",[236,591,593],{"class":592},"sKg8T"," default",[236,595,596],{"class":483}," defineNuxtConfig",[236,598,599],{"class":428},"({\n",[236,601,602,605,607],{"class":238,"line":245},[236,603,604],{"class":499},"  routeRules",[236,606,504],{"class":503},[236,608,609],{"class":428}," {\n",[236,611,612],{"class":238,"line":252},[236,613,614],{"class":418},"    \u002F\u002F Authenticated users only\n",[236,616,617,620,622,624,626,628,630,633,635,638,640,643,645,648],{"class":238,"line":258},[236,618,619],{"class":443},"    '\u002Fapp\u002F**'",[236,621,504],{"class":503},[236,623,507],{"class":428},[236,625,425],{"class":499},[236,627,504],{"class":503},[236,629,507],{"class":428},[236,631,632],{"class":499},"only",[236,634,504],{"class":503},[236,636,637],{"class":443}," 'user'",[236,639,447],{"class":428},[236,641,642],{"class":499},"redirectTo",[236,644,504],{"class":503},[236,646,647],{"class":443}," '\u002Flogin'",[236,649,650],{"class":428}," } },\n",[236,652,653],{"class":238,"line":264},[236,654,655],{"class":428},"    \n",[236,657,658],{"class":238,"line":270},[236,659,660],{"class":418},"    \u002F\u002F Guests only (e.g. login page)\n",[236,662,663,666,668,670,672,674,676,678,680,683,685,687,689,692],{"class":238,"line":276},[236,664,665],{"class":443},"    '\u002Flogin'",[236,667,504],{"class":503},[236,669,507],{"class":428},[236,671,425],{"class":499},[236,673,504],{"class":503},[236,675,507],{"class":428},[236,677,632],{"class":499},[236,679,504],{"class":503},[236,681,682],{"class":443}," 'guest'",[236,684,447],{"class":428},[236,686,642],{"class":499},[236,688,504],{"class":503},[236,690,691],{"class":443}," '\u002Fapp'",[236,693,650],{"class":428},[236,695,696],{"class":238,"line":282},[236,697,655],{"class":428},[236,699,700],{"class":238,"line":288},[236,701,702],{"class":418},"    \u002F\u002F Admin role only\n",[236,704,705,708,710,712,714,716,718,720,722,724,726,728,730],{"class":238,"line":294},[236,706,707],{"class":443},"    '\u002Fadmin\u002F**'",[236,709,504],{"class":503},[236,711,507],{"class":428},[236,713,425],{"class":499},[236,715,504],{"class":503},[236,717,507],{"class":428},[236,719,432],{"class":499},[236,721,504],{"class":503},[236,723,507],{"class":428},[236,725,437],{"class":499},[236,727,504],{"class":503},[236,729,514],{"class":443},[236,731,732],{"class":428}," } } },\n",[236,734,736],{"class":238,"line":735},11,[236,737,655],{"class":428},[236,739,741],{"class":238,"line":740},12,[236,742,743],{"class":418},"    \u002F\u002F Admin OR Moderator\n",[236,745,747,750,752],{"class":238,"line":746},13,[236,748,749],{"class":443},"    '\u002Fstaff\u002F**'",[236,751,504],{"class":503},[236,753,754],{"class":428}," { \n",[236,756,758,761,763],{"class":238,"line":757},14,[236,759,760],{"class":499},"      auth",[236,762,504],{"class":503},[236,764,754],{"class":428},[236,766,768,771,773,775,777,779,782,784,786,788],{"class":238,"line":767},15,[236,769,770],{"class":499},"        user",[236,772,504],{"class":503},[236,774,507],{"class":428},[236,776,437],{"class":499},[236,778,504],{"class":503},[236,780,781],{"class":428}," [",[236,783,444],{"class":443},[236,785,447],{"class":428},[236,787,450],{"class":443},[236,789,790],{"class":428},"] } \n",[236,792,794],{"class":238,"line":793},16,[236,795,796],{"class":428},"      } \n",[236,798,800],{"class":238,"line":799},17,[236,801,802],{"class":428},"    }\n",[236,804,806],{"class":238,"line":805},18,[236,807,808],{"class":428},"  }\n",[236,810,812],{"class":238,"line":811},19,[236,813,561],{"class":428},[299,815,816,817,504],{},"The same auth keys work under ",[233,818,579],{},[225,820,822],{"className":409,"code":821,"filename":571,"language":411,"meta":231,"style":231},"export default defineNuxtConfig({\n  nitro: {\n    routeRules: {\n      '\u002Fapp\u002F**': { auth: { only: 'user', redirectTo: '\u002Flogin' } },\n      '\u002Flogin': { auth: { only: 'guest', redirectTo: '\u002Fapp' } },\n      '\u002Fadmin\u002F**': { auth: { user: { role: 'admin' } } },\n    },\n  },\n})\n",[233,823,824,834,843,852,883,914,943,948,953],{"__ignoreMap":231},[236,825,826,828,830,832],{"class":238,"line":239},[236,827,589],{"class":479},[236,829,593],{"class":592},[236,831,596],{"class":483},[236,833,599],{"class":428},[236,835,836,839,841],{"class":238,"line":245},[236,837,838],{"class":499},"  nitro",[236,840,504],{"class":503},[236,842,609],{"class":428},[236,844,845,848,850],{"class":238,"line":252},[236,846,847],{"class":499},"    routeRules",[236,849,504],{"class":503},[236,851,609],{"class":428},[236,853,854,857,859,861,863,865,867,869,871,873,875,877,879,881],{"class":238,"line":258},[236,855,856],{"class":443},"      '\u002Fapp\u002F**'",[236,858,504],{"class":503},[236,860,507],{"class":428},[236,862,425],{"class":499},[236,864,504],{"class":503},[236,866,507],{"class":428},[236,868,632],{"class":499},[236,870,504],{"class":503},[236,872,637],{"class":443},[236,874,447],{"class":428},[236,876,642],{"class":499},[236,878,504],{"class":503},[236,880,647],{"class":443},[236,882,650],{"class":428},[236,884,885,888,890,892,894,896,898,900,902,904,906,908,910,912],{"class":238,"line":264},[236,886,887],{"class":443},"      '\u002Flogin'",[236,889,504],{"class":503},[236,891,507],{"class":428},[236,893,425],{"class":499},[236,895,504],{"class":503},[236,897,507],{"class":428},[236,899,632],{"class":499},[236,901,504],{"class":503},[236,903,682],{"class":443},[236,905,447],{"class":428},[236,907,642],{"class":499},[236,909,504],{"class":503},[236,911,691],{"class":443},[236,913,650],{"class":428},[236,915,916,919,921,923,925,927,929,931,933,935,937,939,941],{"class":238,"line":270},[236,917,918],{"class":443},"      '\u002Fadmin\u002F**'",[236,920,504],{"class":503},[236,922,507],{"class":428},[236,924,425],{"class":499},[236,926,504],{"class":503},[236,928,507],{"class":428},[236,930,432],{"class":499},[236,932,504],{"class":503},[236,934,507],{"class":428},[236,936,437],{"class":499},[236,938,504],{"class":503},[236,940,514],{"class":443},[236,942,732],{"class":428},[236,944,945],{"class":238,"line":276},[236,946,947],{"class":428},"    },\n",[236,949,950],{"class":238,"line":282},[236,951,952],{"class":428},"  },\n",[236,954,955],{"class":238,"line":288},[236,956,561],{"class":428},[958,959,960,961,963,964,966,967,542],"note",{},"If both ",[233,962,575],{}," and ",[233,965,579],{}," are present, the module reads ",[233,968,579],{},[299,970,971,972,974,975,447,978,348],{},"If ",[233,973,642],{}," is omitted, shorthand defaults apply (",[233,976,977],{},"'user' -> '\u002Flogin'",[233,979,980],{},"'guest' -> '\u002F'",[982,983,984,1001],"tip",{},[299,985,971,986,989,990,992,993,996,997,1000],{},[233,987,988],{},"auth: { user: { ... } }"," complains about missing fields (e.g. ",[233,991,437],{},"), ensure your Better Auth plugin fields are inferred or that you have a working ",[233,994,995],{},"#nuxt-better-auth"," type augmentation in a root ",[233,998,999],{},"*.d.ts"," file.",[1002,1003],"read-more",{"title":28,"to":29},[958,1005,1006],{},"These rules are automatically synced to the client-side router, ensuring instant redirects without server roundtrips for navigation.",[958,1008,1009,1010,1013,1014,447,1017,447,1020,447,1023,1026,1027,1030,1031,1034],{},"Broad rules such as ",[233,1011,1012],{},"'\u002F**': { auth: 'user' }"," skip framework and module internals that must stay reachable, including ",[233,1015,1016],{},"\u002F_nuxt\u002F**",[233,1018,1019],{},"\u002F_ipx\u002F**",[233,1021,1022],{},"\u002Fapi\u002F_nuxt_icon\u002F**",[233,1024,1025],{},"\u002Fapi\u002F_better-auth\u002F**",", Better Auth's ",[233,1028,1029],{},"\u002Fapi\u002Fauth\u002F**",", and development-only module routes. The module still applies broad auth rules to app-owned pages and ",[233,1032,1033],{},"\u002Fapi\u002F**"," handlers.",[303,1036,1038],{"id":1037},"_2-page-meta","2. Page Meta",[299,1040,1041,1042,1045,1046,542],{},"For page-specific control, use ",[233,1043,1044],{},"definePageMeta"," within your Vue components. This overrides global ",[233,1047,575],{},[225,1049,1054],{"className":1050,"code":1051,"filename":1052,"language":1053,"meta":231,"style":231},"language-vue shiki shiki-themes one-light synthwave-84 synthwave-84","\u003Cscript setup>\ndefinePageMeta({\n  auth: 'user'\n})\n\u003C\u002Fscript>\n","pages\u002Fdashboard.vue","vue",[233,1055,1056,1073,1080,1090,1094],{"__ignoreMap":231},[236,1057,1058,1062,1066,1070],{"class":238,"line":239},[236,1059,1061],{"class":1060},"sL9le","\u003C",[236,1063,1065],{"class":1064},"stweg","script",[236,1067,1069],{"class":1068},"s0frj"," setup",[236,1071,1072],{"class":1060},">\n",[236,1074,1075,1077],{"class":238,"line":245},[236,1076,1044],{"class":483},[236,1078,599],{"class":1079},"sEEOt",[236,1081,1082,1085,1087],{"class":238,"line":252},[236,1083,1084],{"class":499},"  auth",[236,1086,504],{"class":503},[236,1088,1089],{"class":443}," 'user'\n",[236,1091,1092],{"class":238,"line":258},[236,1093,561],{"class":1079},[236,1095,1096,1099,1101],{"class":238,"line":264},[236,1097,1098],{"class":1060},"\u003C\u002F",[236,1100,1065],{"class":1064},[236,1102,1072],{"class":1060},[396,1104,1106],{"id":1105},"advanced-options","Advanced Options",[299,1108,1109],{},"You can pass an object for granular control:",[225,1111,1113],{"className":409,"code":1112,"language":411,"meta":231,"style":231},"definePageMeta({\n  auth: {\n    \u002F\u002F Only allow authenticated users\n    only: 'user',\n    \n    \u002F\u002F Redirect blocked users to a specific page\n    redirectTo: '\u002Fsubscribe',\n    \n    \u002F\u002F Match specific user properties\n    user: {\n      verified: true\n    }\n  }\n})\n",[233,1114,1115,1121,1129,1134,1145,1149,1154,1166,1170,1175,1184,1194,1198,1202],{"__ignoreMap":231},[236,1116,1117,1119],{"class":238,"line":239},[236,1118,1044],{"class":483},[236,1120,599],{"class":428},[236,1122,1123,1125,1127],{"class":238,"line":245},[236,1124,1084],{"class":499},[236,1126,504],{"class":503},[236,1128,609],{"class":428},[236,1130,1131],{"class":238,"line":252},[236,1132,1133],{"class":418},"    \u002F\u002F Only allow authenticated users\n",[236,1135,1136,1139,1141,1143],{"class":238,"line":258},[236,1137,1138],{"class":499},"    only",[236,1140,504],{"class":503},[236,1142,637],{"class":443},[236,1144,556],{"class":428},[236,1146,1147],{"class":238,"line":264},[236,1148,655],{"class":428},[236,1150,1151],{"class":238,"line":270},[236,1152,1153],{"class":418},"    \u002F\u002F Redirect blocked users to a specific page\n",[236,1155,1156,1159,1161,1164],{"class":238,"line":276},[236,1157,1158],{"class":499},"    redirectTo",[236,1160,504],{"class":503},[236,1162,1163],{"class":443}," '\u002Fsubscribe'",[236,1165,556],{"class":428},[236,1167,1168],{"class":238,"line":282},[236,1169,655],{"class":428},[236,1171,1172],{"class":238,"line":288},[236,1173,1174],{"class":418},"    \u002F\u002F Match specific user properties\n",[236,1176,1177,1180,1182],{"class":238,"line":294},[236,1178,1179],{"class":499},"    user",[236,1181,504],{"class":503},[236,1183,609],{"class":428},[236,1185,1186,1189,1191],{"class":238,"line":735},[236,1187,1188],{"class":499},"      verified",[236,1190,504],{"class":503},[236,1192,1193],{"class":552}," true\n",[236,1195,1196],{"class":238,"line":740},[236,1197,802],{"class":428},[236,1199,1200],{"class":238,"line":746},[236,1201,808],{"class":428},[236,1203,1204],{"class":238,"line":757},[236,1205,561],{"class":428},[303,1207,1209],{"id":1208},"_3-server-api-protection","3. Server API Protection",[299,1211,1212,1213,1215],{},"Protecting your API endpoints is critical. Use ",[233,1214,463],{}," to enforce authentication on server routes.",[225,1217,1220],{"className":409,"code":1218,"filename":1219,"language":411,"meta":231,"style":231},"export default defineEventHandler(async (event) => {\n  \u002F\u002F Throws 401 if not logged in\n  const { user } = await requireUserSession(event)\n  \n  return { secret: 'data' }\n})\n","server\u002Fapi\u002Fsecret.get.ts",[233,1221,1222,1248,1253,1282,1287,1305],{"__ignoreMap":231},[236,1223,1224,1226,1228,1231,1233,1236,1239,1241,1244,1246],{"class":238,"line":239},[236,1225,589],{"class":479},[236,1227,593],{"class":592},[236,1229,1230],{"class":483}," defineEventHandler",[236,1232,487],{"class":428},[236,1234,1235],{"class":479},"async",[236,1237,1238],{"class":428}," (",[236,1240,491],{"class":530},[236,1242,1243],{"class":428},") ",[236,1245,536],{"class":479},[236,1247,609],{"class":428},[236,1249,1250],{"class":238,"line":245},[236,1251,1252],{"class":418},"  \u002F\u002F Throws 401 if not logged in\n",[236,1254,1255,1258,1260,1263,1266,1270,1273,1275,1277,1279],{"class":238,"line":252},[236,1256,1257],{"class":479},"  const",[236,1259,507],{"class":428},[236,1261,432],{"class":1262},"s6Rhl",[236,1264,1265],{"class":428}," } ",[236,1267,1269],{"class":1268},"sQBpM","=",[236,1271,1272],{"class":479}," await",[236,1274,484],{"class":483},[236,1276,487],{"class":428},[236,1278,491],{"class":499},[236,1280,1281],{"class":428},")\n",[236,1283,1284],{"class":238,"line":258},[236,1285,1286],{"class":428},"  \n",[236,1288,1289,1292,1294,1297,1299,1302],{"class":238,"line":264},[236,1290,1291],{"class":479},"  return",[236,1293,507],{"class":428},[236,1295,1296],{"class":499},"secret",[236,1298,504],{"class":503},[236,1300,1301],{"class":443}," 'data'",[236,1303,1304],{"class":428}," }\n",[236,1306,1307],{"class":238,"line":270},[236,1308,561],{"class":428},[396,1310,1312],{"id":1311},"role-based-access","Role-Based Access",[299,1314,1315,1316,504],{},"You can also pass requirements to ",[233,1317,463],{},[225,1319,1321],{"className":409,"code":1320,"language":411,"meta":231,"style":231},"await requireUserSession(event, {\n  \u002F\u002F User must match ALL conditions\n  user: {\n    role: 'admin',\n    verified: true,\n    \u002F\u002F OR logic for array values\n    plan: ['pro', 'enterprise']\n  }\n})\n",[233,1322,1323,1335,1340,1348,1359,1370,1375,1395,1399],{"__ignoreMap":231},[236,1324,1325,1327,1329,1331,1333],{"class":238,"line":239},[236,1326,480],{"class":479},[236,1328,484],{"class":483},[236,1330,487],{"class":428},[236,1332,491],{"class":490},[236,1334,494],{"class":428},[236,1336,1337],{"class":238,"line":245},[236,1338,1339],{"class":418},"  \u002F\u002F User must match ALL conditions\n",[236,1341,1342,1344,1346],{"class":238,"line":252},[236,1343,500],{"class":499},[236,1345,504],{"class":503},[236,1347,609],{"class":428},[236,1349,1350,1353,1355,1357],{"class":238,"line":258},[236,1351,1352],{"class":499},"    role",[236,1354,504],{"class":503},[236,1356,514],{"class":443},[236,1358,556],{"class":428},[236,1360,1361,1364,1366,1368],{"class":238,"line":264},[236,1362,1363],{"class":499},"    verified",[236,1365,504],{"class":503},[236,1367,553],{"class":552},[236,1369,556],{"class":428},[236,1371,1372],{"class":238,"line":270},[236,1373,1374],{"class":418},"    \u002F\u002F OR logic for array values\n",[236,1376,1377,1380,1382,1384,1387,1389,1392],{"class":238,"line":276},[236,1378,1379],{"class":499},"    plan",[236,1381,504],{"class":503},[236,1383,781],{"class":428},[236,1385,1386],{"class":443},"'pro'",[236,1388,447],{"class":428},[236,1390,1391],{"class":443},"'enterprise'",[236,1393,1394],{"class":428},"]\n",[236,1396,1397],{"class":238,"line":282},[236,1398,808],{"class":428},[236,1400,1401],{"class":238,"line":288},[236,1402,561],{"class":428},[225,1404,1406],{"className":409,"code":1405,"language":411,"meta":231,"style":231},"\u002F\u002F Custom fields (like 'plan') must be defined in your Better Auth schema\nauth: { user: { plan: ['pro', 'enterprise'] } }\n",[233,1407,1408,1413],{"__ignoreMap":231},[236,1409,1410],{"class":238,"line":239},[236,1411,1412],{"class":418},"\u002F\u002F Custom fields (like 'plan') must be defined in your Better Auth schema\n",[236,1414,1415,1417,1419,1421,1423,1426,1428,1430,1432,1434],{"class":238,"line":245},[236,1416,425],{"class":424},[236,1418,429],{"class":428},[236,1420,432],{"class":424},[236,1422,429],{"class":428},[236,1424,1425],{"class":424},"plan",[236,1427,440],{"class":428},[236,1429,1386],{"class":443},[236,1431,447],{"class":428},[236,1433,1391],{"class":443},[236,1435,453],{"class":428},[1002,1437],{"title":1438,"to":75},"Role-Based Access guide",[303,1440,1442],{"id":1441},"safe-redirects-after-login","Safe Redirects After Login",[299,1444,1445],{},"When redirecting unauthenticated users to your login page, this module automatically appends a return-to query param by default:",[225,1447,1449],{"className":409,"code":1448,"filename":571,"language":411,"meta":231,"style":231},"export default defineNuxtConfig({\n  auth: {\n    preserveRedirect: true, \u002F\u002F default\n    redirectQueryKey: 'redirect', \u002F\u002F default\n  },\n})\n",[233,1450,1451,1461,1469,1483,1497,1501],{"__ignoreMap":231},[236,1452,1453,1455,1457,1459],{"class":238,"line":239},[236,1454,589],{"class":479},[236,1456,593],{"class":592},[236,1458,596],{"class":483},[236,1460,599],{"class":428},[236,1462,1463,1465,1467],{"class":238,"line":245},[236,1464,1084],{"class":499},[236,1466,504],{"class":503},[236,1468,609],{"class":428},[236,1470,1471,1474,1476,1478,1480],{"class":238,"line":252},[236,1472,1473],{"class":499},"    preserveRedirect",[236,1475,504],{"class":503},[236,1477,553],{"class":552},[236,1479,447],{"class":428},[236,1481,1482],{"class":418},"\u002F\u002F default\n",[236,1484,1485,1488,1490,1493,1495],{"class":238,"line":258},[236,1486,1487],{"class":499},"    redirectQueryKey",[236,1489,504],{"class":503},[236,1491,1492],{"class":443}," 'redirect'",[236,1494,447],{"class":428},[236,1496,1482],{"class":418},[236,1498,1499],{"class":238,"line":264},[236,1500,952],{"class":428},[236,1502,1503],{"class":238,"line":270},[236,1504,561],{"class":428},[299,1506,1507,1508,1511,1512,1515],{},"Your login page can then read ",[233,1509,1510],{},"route.query.redirect"," (or your custom ",[233,1513,1514],{},"auth.redirectQueryKey",") to navigate back after a successful login.",[299,1517,1518,1519,1522,1523,1526],{},"You can also set ",[233,1520,1521],{},"auth.redirects.authenticated"," to define where users land after successful login\u002Fsign-up when ",[233,1524,1525],{},"onSuccess"," is not provided in the auth method call.",[299,1528,1529],{},"When preserving the original URL for post-login redirects, validate it to prevent open redirect attacks:",[225,1531,1534],{"className":409,"code":1532,"filename":1533,"language":411,"meta":231,"style":231},"const route = useRoute()\nconst signInEmail = useSignIn('email')\n\nfunction isSafeLocalRedirect(value: unknown): value is string {\n  if (\n    typeof value !== 'string'\n    || !value.startsWith('\u002F')\n    || value.startsWith('\u002F\u002F')\n    || value.includes('\\\\')\n    || \u002F[\\u0000-\\u001F\\u007F]\u002F.test(value)\n  ) {\n    return false\n  }\n\n  try {\n    const origin = 'https:\u002F\u002Fapp.invalid'\n    const parsed = new URL(value, origin)\n    if (parsed.origin !== origin || parsed.pathname.startsWith('\u002F\u002F')) {\n      return false\n    }\n\n    \u002F\u002F Normalize encoded path separators only for validation.\n    const normalizedPath = parsed.pathname.replace(\u002F%(?:2f|5c)\u002Fgi, '\u002F')\n    const normalized = new URL(normalizedPath, origin)\n    return normalized.origin === origin\n      && !normalized.pathname.startsWith('\u002F\u002F')\n  }\n  catch {\n    return false\n  }\n}\n\nfunction getSafeRedirect(): string {\n  const redirect = route.query.redirect\n  return isSafeLocalRedirect(redirect)\n    ? redirect\n    : '\u002F'\n}\n\nasync function login(email: string, password: string) {\n  await signInEmail.execute(\n    { email, password },\n    { onSuccess: () => navigateTo(getSafeRedirect()) },\n  )\n}\n","pages\u002Flogin.vue",[233,1535,1536,1553,1572,1576,1611,1619,1632,1654,1671,1695,1720,1725,1733,1737,1741,1748,1761,1787,1826,1833,1838,1843,1849,1895,1920,1936,1961,1966,1974,1981,1986,1992,1997,2014,2036,2050,2059,2068,2073,2078,2110,2126,2140,2165,2171],{"__ignoreMap":231},[236,1537,1538,1541,1544,1547,1550],{"class":238,"line":239},[236,1539,1540],{"class":479},"const",[236,1542,1543],{"class":1262}," route",[236,1545,1546],{"class":1268}," =",[236,1548,1549],{"class":483}," useRoute",[236,1551,1552],{"class":428},"()\n",[236,1554,1555,1557,1560,1562,1565,1567,1570],{"class":238,"line":245},[236,1556,1540],{"class":479},[236,1558,1559],{"class":1262}," signInEmail",[236,1561,1546],{"class":1268},[236,1563,1564],{"class":483}," useSignIn",[236,1566,487],{"class":428},[236,1568,1569],{"class":443},"'email'",[236,1571,1281],{"class":428},[236,1573,1574],{"class":238,"line":252},[236,1575,249],{"emptyLinePlaceholder":248},[236,1577,1578,1581,1584,1586,1589,1591,1595,1598,1600,1603,1606,1609],{"class":238,"line":258},[236,1579,1580],{"class":479},"function",[236,1582,1583],{"class":483}," isSafeLocalRedirect",[236,1585,487],{"class":428},[236,1587,1588],{"class":530},"value",[236,1590,504],{"class":548},[236,1592,1594],{"class":1593},"sr8De"," unknown",[236,1596,1597],{"class":428},")",[236,1599,504],{"class":548},[236,1601,1602],{"class":530}," value",[236,1604,1605],{"class":548}," is",[236,1607,1608],{"class":1593}," string",[236,1610,609],{"class":428},[236,1612,1613,1616],{"class":238,"line":264},[236,1614,1615],{"class":479},"  if",[236,1617,1618],{"class":428}," (\n",[236,1620,1621,1624,1626,1629],{"class":238,"line":270},[236,1622,1623],{"class":548},"    typeof",[236,1625,1602],{"class":490},[236,1627,1628],{"class":548}," !==",[236,1630,1631],{"class":443}," 'string'\n",[236,1633,1634,1637,1640,1642,1644,1647,1649,1652],{"class":238,"line":276},[236,1635,1636],{"class":548},"    ||",[236,1638,1639],{"class":548}," !",[236,1641,1588],{"class":490},[236,1643,542],{"class":428},[236,1645,1646],{"class":483},"startsWith",[236,1648,487],{"class":428},[236,1650,1651],{"class":443},"'\u002F'",[236,1653,1281],{"class":428},[236,1655,1656,1658,1660,1662,1664,1666,1669],{"class":238,"line":282},[236,1657,1636],{"class":548},[236,1659,1602],{"class":490},[236,1661,542],{"class":428},[236,1663,1646],{"class":483},[236,1665,487],{"class":428},[236,1667,1668],{"class":443},"'\u002F\u002F'",[236,1670,1281],{"class":428},[236,1672,1673,1675,1677,1679,1682,1684,1687,1691,1693],{"class":238,"line":288},[236,1674,1636],{"class":548},[236,1676,1602],{"class":490},[236,1678,542],{"class":428},[236,1680,1681],{"class":483},"includes",[236,1683,487],{"class":428},[236,1685,1686],{"class":443},"'",[236,1688,1690],{"class":1689},"s09iw","\\\\",[236,1692,1686],{"class":443},[236,1694,1281],{"class":428},[236,1696,1697,1699,1703,1706,1709,1711,1714,1716,1718],{"class":238,"line":294},[236,1698,1636],{"class":548},[236,1700,1702],{"class":1701},"sXP93"," \u002F",[236,1704,1705],{"class":552},"[\\u0000-\\u001F\\u007F]",[236,1707,1708],{"class":1701},"\u002F",[236,1710,542],{"class":428},[236,1712,1713],{"class":483},"test",[236,1715,487],{"class":428},[236,1717,1588],{"class":490},[236,1719,1281],{"class":428},[236,1721,1722],{"class":238,"line":735},[236,1723,1724],{"class":428},"  ) {\n",[236,1726,1727,1730],{"class":238,"line":740},[236,1728,1729],{"class":479},"    return",[236,1731,1732],{"class":552}," false\n",[236,1734,1735],{"class":238,"line":746},[236,1736,808],{"class":428},[236,1738,1739],{"class":238,"line":757},[236,1740,249],{"emptyLinePlaceholder":248},[236,1742,1743,1746],{"class":238,"line":767},[236,1744,1745],{"class":479},"  try",[236,1747,609],{"class":428},[236,1749,1750,1753,1756,1758],{"class":238,"line":793},[236,1751,1752],{"class":479},"    const",[236,1754,1755],{"class":1262}," origin",[236,1757,1546],{"class":1268},[236,1759,1760],{"class":443}," 'https:\u002F\u002Fapp.invalid'\n",[236,1762,1763,1765,1768,1770,1773,1776,1778,1780,1782,1785],{"class":238,"line":799},[236,1764,1752],{"class":479},[236,1766,1767],{"class":1262}," parsed",[236,1769,1546],{"class":1268},[236,1771,1772],{"class":479}," new",[236,1774,1775],{"class":483}," URL",[236,1777,487],{"class":428},[236,1779,1588],{"class":490},[236,1781,447],{"class":428},[236,1783,1784],{"class":490},"origin",[236,1786,1281],{"class":428},[236,1788,1789,1792,1794,1797,1799,1801,1803,1805,1808,1810,1812,1815,1817,1819,1821,1823],{"class":238,"line":805},[236,1790,1791],{"class":479},"    if",[236,1793,1238],{"class":428},[236,1795,1796],{"class":490},"parsed",[236,1798,542],{"class":428},[236,1800,1784],{"class":499},[236,1802,1628],{"class":548},[236,1804,1755],{"class":490},[236,1806,1807],{"class":548}," ||",[236,1809,1767],{"class":490},[236,1811,542],{"class":428},[236,1813,1814],{"class":499},"pathname",[236,1816,542],{"class":428},[236,1818,1646],{"class":483},[236,1820,487],{"class":428},[236,1822,1668],{"class":443},[236,1824,1825],{"class":428},")) {\n",[236,1827,1828,1831],{"class":238,"line":811},[236,1829,1830],{"class":479},"      return",[236,1832,1732],{"class":552},[236,1834,1836],{"class":238,"line":1835},20,[236,1837,802],{"class":428},[236,1839,1841],{"class":238,"line":1840},21,[236,1842,249],{"emptyLinePlaceholder":248},[236,1844,1846],{"class":238,"line":1845},22,[236,1847,1848],{"class":418},"    \u002F\u002F Normalize encoded path separators only for validation.\n",[236,1850,1852,1854,1857,1859,1861,1863,1865,1867,1870,1872,1874,1878,1881,1884,1886,1889,1891,1893],{"class":238,"line":1851},23,[236,1853,1752],{"class":479},[236,1855,1856],{"class":1262}," normalizedPath",[236,1858,1546],{"class":1268},[236,1860,1767],{"class":490},[236,1862,542],{"class":428},[236,1864,1814],{"class":499},[236,1866,542],{"class":428},[236,1868,1869],{"class":483},"replace",[236,1871,487],{"class":428},[236,1873,1708],{"class":1701},[236,1875,1877],{"class":1876},"svxf7","%(?:2f",[236,1879,1880],{"class":548},"|",[236,1882,1883],{"class":1876},"5c)",[236,1885,1708],{"class":1701},[236,1887,1888],{"class":479},"gi",[236,1890,447],{"class":428},[236,1892,1651],{"class":443},[236,1894,1281],{"class":428},[236,1896,1898,1900,1903,1905,1907,1909,1911,1914,1916,1918],{"class":238,"line":1897},24,[236,1899,1752],{"class":479},[236,1901,1902],{"class":1262}," normalized",[236,1904,1546],{"class":1268},[236,1906,1772],{"class":479},[236,1908,1775],{"class":483},[236,1910,487],{"class":428},[236,1912,1913],{"class":490},"normalizedPath",[236,1915,447],{"class":428},[236,1917,1784],{"class":490},[236,1919,1281],{"class":428},[236,1921,1923,1925,1927,1929,1931,1933],{"class":238,"line":1922},25,[236,1924,1729],{"class":479},[236,1926,1902],{"class":490},[236,1928,542],{"class":428},[236,1930,1784],{"class":499},[236,1932,549],{"class":548},[236,1934,1935],{"class":490}," origin\n",[236,1937,1939,1942,1944,1947,1949,1951,1953,1955,1957,1959],{"class":238,"line":1938},26,[236,1940,1941],{"class":548},"      &&",[236,1943,1639],{"class":548},[236,1945,1946],{"class":490},"normalized",[236,1948,542],{"class":428},[236,1950,1814],{"class":499},[236,1952,542],{"class":428},[236,1954,1646],{"class":483},[236,1956,487],{"class":428},[236,1958,1668],{"class":443},[236,1960,1281],{"class":428},[236,1962,1964],{"class":238,"line":1963},27,[236,1965,808],{"class":428},[236,1967,1969,1972],{"class":238,"line":1968},28,[236,1970,1971],{"class":479},"  catch",[236,1973,609],{"class":428},[236,1975,1977,1979],{"class":238,"line":1976},29,[236,1978,1729],{"class":479},[236,1980,1732],{"class":552},[236,1982,1984],{"class":238,"line":1983},30,[236,1985,808],{"class":428},[236,1987,1989],{"class":238,"line":1988},31,[236,1990,1991],{"class":428},"}\n",[236,1993,1995],{"class":238,"line":1994},32,[236,1996,249],{"emptyLinePlaceholder":248},[236,1998,2000,2002,2005,2008,2010,2012],{"class":238,"line":1999},33,[236,2001,1580],{"class":479},[236,2003,2004],{"class":483}," getSafeRedirect",[236,2006,2007],{"class":428},"()",[236,2009,504],{"class":548},[236,2011,1608],{"class":1593},[236,2013,609],{"class":428},[236,2015,2017,2019,2022,2024,2026,2028,2031,2033],{"class":238,"line":2016},34,[236,2018,1257],{"class":479},[236,2020,2021],{"class":1262}," redirect",[236,2023,1546],{"class":1268},[236,2025,1543],{"class":490},[236,2027,542],{"class":428},[236,2029,2030],{"class":499},"query",[236,2032,542],{"class":428},[236,2034,2035],{"class":499},"redirect\n",[236,2037,2039,2041,2043,2045,2048],{"class":238,"line":2038},35,[236,2040,1291],{"class":479},[236,2042,1583],{"class":483},[236,2044,487],{"class":428},[236,2046,2047],{"class":490},"redirect",[236,2049,1281],{"class":428},[236,2051,2053,2056],{"class":238,"line":2052},36,[236,2054,2055],{"class":548},"    ?",[236,2057,2058],{"class":490}," redirect\n",[236,2060,2062,2065],{"class":238,"line":2061},37,[236,2063,2064],{"class":548},"    :",[236,2066,2067],{"class":443}," '\u002F'\n",[236,2069,2071],{"class":238,"line":2070},38,[236,2072,1991],{"class":428},[236,2074,2076],{"class":238,"line":2075},39,[236,2077,249],{"emptyLinePlaceholder":248},[236,2079,2081,2083,2086,2089,2091,2094,2096,2098,2100,2103,2105,2107],{"class":238,"line":2080},40,[236,2082,1235],{"class":479},[236,2084,2085],{"class":479}," function",[236,2087,2088],{"class":483}," login",[236,2090,487],{"class":428},[236,2092,2093],{"class":530},"email",[236,2095,504],{"class":548},[236,2097,1608],{"class":1593},[236,2099,447],{"class":428},[236,2101,2102],{"class":530},"password",[236,2104,504],{"class":548},[236,2106,1608],{"class":1593},[236,2108,2109],{"class":428},") {\n",[236,2111,2113,2116,2118,2120,2123],{"class":238,"line":2112},41,[236,2114,2115],{"class":479},"  await",[236,2117,1559],{"class":490},[236,2119,542],{"class":428},[236,2121,2122],{"class":483},"execute",[236,2124,2125],{"class":428},"(\n",[236,2127,2129,2132,2134,2136,2138],{"class":238,"line":2128},42,[236,2130,2131],{"class":428},"    { ",[236,2133,2093],{"class":490},[236,2135,447],{"class":428},[236,2137,2102],{"class":490},[236,2139,517],{"class":428},[236,2141,2143,2145,2147,2149,2152,2154,2157,2159,2162],{"class":238,"line":2142},43,[236,2144,2131],{"class":428},[236,2146,1525],{"class":483},[236,2148,504],{"class":503},[236,2150,2151],{"class":428}," () ",[236,2153,536],{"class":479},[236,2155,2156],{"class":483}," navigateTo",[236,2158,487],{"class":428},[236,2160,2161],{"class":483},"getSafeRedirect",[236,2163,2164],{"class":428},"()) },\n",[236,2166,2168],{"class":238,"line":2167},44,[236,2169,2170],{"class":428},"  )\n",[236,2172,2174],{"class":238,"line":2173},45,[236,2175,1991],{"class":428},[2177,2178,2179,2180,2183,2184,2187],"warning",{},"Always validate redirect URLs. Accepting arbitrary URLs allows attackers to redirect users to malicious sites after login. Pass the validated value unchanged to ",[233,2181,2182],{},"navigateTo","; do not decode it again after validation. Encoded separators can be valid route data, such as ",[233,2185,2186],{},"\u002Ffiles\u002Fa%2Fb",", and do not change the parsed URL's origin.",[303,2189,2191],{"id":2190},"advanced-api-patterns","Advanced API Patterns",[396,2193,2195],{"id":2194},"custom-rule-callback","Custom Rule Callback",[299,2197,2198],{},"For complex authorization logic:",[225,2200,2202],{"className":409,"code":2201,"filename":472,"language":411,"meta":231,"style":231},"const session = await requireUserSession(event, {\n  user: { emailVerified: true },\n  rule: ({ user }) => user.subscriptionActive\n})\n",[233,2203,2204,2223,2240,2261],{"__ignoreMap":231},[236,2205,2206,2208,2211,2213,2215,2217,2219,2221],{"class":238,"line":239},[236,2207,1540],{"class":479},[236,2209,2210],{"class":1262}," session",[236,2212,1546],{"class":1268},[236,2214,1272],{"class":479},[236,2216,484],{"class":483},[236,2218,487],{"class":428},[236,2220,491],{"class":490},[236,2222,494],{"class":428},[236,2224,2225,2227,2229,2231,2234,2236,2238],{"class":238,"line":245},[236,2226,500],{"class":499},[236,2228,504],{"class":503},[236,2230,507],{"class":428},[236,2232,2233],{"class":499},"emailVerified",[236,2235,504],{"class":503},[236,2237,553],{"class":552},[236,2239,517],{"class":428},[236,2241,2242,2244,2246,2248,2250,2252,2254,2256,2258],{"class":238,"line":252},[236,2243,522],{"class":483},[236,2245,504],{"class":503},[236,2247,527],{"class":428},[236,2249,432],{"class":530},[236,2251,533],{"class":428},[236,2253,536],{"class":479},[236,2255,539],{"class":490},[236,2257,542],{"class":428},[236,2259,2260],{"class":499},"subscriptionActive\n",[236,2262,2263],{"class":238,"line":258},[236,2264,561],{"class":428},[396,2266,2268],{"id":2267},"websocket-handlers","WebSocket Handlers",[225,2270,2273],{"className":409,"code":2271,"filename":2272,"language":411,"meta":231,"style":231},"import { defineWebSocketHandler } from 'h3'\n\nexport default defineWebSocketHandler({\n  open: async (peer) => {\n    await requireUserSession(peer.ctx.event, { user: { role: 'member' } })\n  },\n})\n","server\u002Fapi\u002Fws.ts",[233,2274,2275,2293,2297,2308,2329,2368,2372],{"__ignoreMap":231},[236,2276,2277,2280,2282,2285,2287,2290],{"class":238,"line":239},[236,2278,2279],{"class":479},"import",[236,2281,507],{"class":428},[236,2283,2284],{"class":499},"defineWebSocketHandler",[236,2286,1265],{"class":428},[236,2288,2289],{"class":479},"from",[236,2291,2292],{"class":443}," 'h3'\n",[236,2294,2295],{"class":238,"line":245},[236,2296,249],{"emptyLinePlaceholder":248},[236,2298,2299,2301,2303,2306],{"class":238,"line":252},[236,2300,589],{"class":479},[236,2302,593],{"class":592},[236,2304,2305],{"class":483}," defineWebSocketHandler",[236,2307,599],{"class":428},[236,2309,2310,2313,2315,2318,2320,2323,2325,2327],{"class":238,"line":258},[236,2311,2312],{"class":483},"  open",[236,2314,504],{"class":503},[236,2316,2317],{"class":479}," async",[236,2319,1238],{"class":428},[236,2321,2322],{"class":530},"peer",[236,2324,1243],{"class":428},[236,2326,536],{"class":479},[236,2328,609],{"class":428},[236,2330,2331,2334,2336,2338,2340,2342,2345,2347,2349,2352,2354,2356,2358,2360,2362,2365],{"class":238,"line":264},[236,2332,2333],{"class":479},"    await",[236,2335,484],{"class":483},[236,2337,487],{"class":428},[236,2339,2322],{"class":490},[236,2341,542],{"class":428},[236,2343,2344],{"class":499},"ctx",[236,2346,542],{"class":428},[236,2348,491],{"class":499},[236,2350,2351],{"class":428},", { ",[236,2353,432],{"class":499},[236,2355,504],{"class":503},[236,2357,507],{"class":428},[236,2359,437],{"class":499},[236,2361,504],{"class":503},[236,2363,2364],{"class":443}," 'member'",[236,2366,2367],{"class":428}," } })\n",[236,2369,2370],{"class":238,"line":270},[236,2371,952],{"class":428},[236,2373,2374],{"class":238,"line":276},[236,2375,561],{"class":428},[303,2377,2379],{"id":2378},"csrf-protection","CSRF Protection",[299,2381,2382,2383,542],{},"Better Auth checks request origins for state-changing auth requests. This protection applies whether you use an auth client method or raw ",[233,2384,2385],{},"fetch",[299,2387,2388],{},"Prefer the module composables and Better Auth client methods because they provide the expected request shape, typed responses, and error handling:",[225,2390,2392],{"className":409,"code":2391,"language":411,"meta":231,"style":231},"await useSignIn('email').execute({ email, password })\n",[233,2393,2394],{"__ignoreMap":231},[236,2395,2396,2398,2400,2402,2404,2406,2408,2411,2413,2415,2417],{"class":238,"line":239},[236,2397,480],{"class":479},[236,2399,1564],{"class":483},[236,2401,487],{"class":428},[236,2403,1569],{"class":443},[236,2405,348],{"class":428},[236,2407,2122],{"class":483},[236,2409,2410],{"class":428},"({ ",[236,2412,2093],{"class":490},[236,2414,447],{"class":428},[236,2416,2102],{"class":490},[236,2418,2419],{"class":428}," })\n",[299,2421,2422,2423,2425,2426,2429],{},"If you call an auth endpoint with ",[233,2424,2385],{},", follow Better Auth's endpoint contract and include credentials for cross-origin cookie requests. Do not disable origin checks to make a cross-origin request work. Add the frontend origin to ",[233,2427,2428],{},"trustedOrigins"," on the auth server instead.",[1002,2431],{"title":2432,"to":65},"Security Caveats",[2434,2435,2436,2437,2439],"important",{},"Route Rules and Page Meta are primarily for UX (redirects). Always protect your API endpoints with ",[233,2438,463],{}," to ensure real security.",[2441,2442,2443],"style",{},"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 .st7cf, html code.shiki .st7cf{--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-default:#848BBD;--shiki-default-font-style:italic;--shiki-dark:#848BBD;--shiki-dark-font-style:italic}html pre.shiki code .sivOE, html code.shiki .sivOE{--shiki-light:#383A42;--shiki-default:#FE4450;--shiki-dark:#FE4450}html pre.shiki code .s17Py, html code.shiki .s17Py{--shiki-light:#383A42;--shiki-default:#BBBBBB;--shiki-dark:#BBBBBB}html pre.shiki code .sPAZv, html code.shiki .sPAZv{--shiki-light:#50A14F;--shiki-default:#FF8B39;--shiki-dark:#FF8B39}html pre.shiki code .sqe1H, html code.shiki .sqe1H{--shiki-light:#A626A4;--shiki-default:#FEDE5D;--shiki-dark:#FEDE5D}html pre.shiki code .sfT9l, html code.shiki .sfT9l{--shiki-light:#4078F2;--shiki-default:#36F9F6;--shiki-dark:#36F9F6}html pre.shiki code .svFNh, html code.shiki .svFNh{--shiki-light:#383A42;--shiki-default:#FF7EDB;--shiki-dark:#FF7EDB}html pre.shiki code .sYvLG, html code.shiki .sYvLG{--shiki-light:#E45649;--shiki-default:#FF7EDB;--shiki-dark:#FF7EDB}html pre.shiki code .sVnqq, html code.shiki .sVnqq{--shiki-light:#0184BC;--shiki-default:#B6B1B1;--shiki-dark:#B6B1B1}html pre.shiki code .sgisi, html code.shiki .sgisi{--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-default:#FF7EDB;--shiki-default-font-style:italic;--shiki-dark:#FF7EDB;--shiki-dark-font-style:italic}html pre.shiki code .sn-Jc, html code.shiki .sn-Jc{--shiki-light:#0184BC;--shiki-default:#FEDE5D;--shiki-dark:#FEDE5D}html pre.shiki code .s3ZNE, html code.shiki .s3ZNE{--shiki-light:#986801;--shiki-default:#F97E72;--shiki-dark:#F97E72}html pre.shiki code .sKg8T, html code.shiki .sKg8T{--shiki-light:#E45649;--shiki-default:#FEDE5D;--shiki-dark:#FEDE5D}html pre.shiki code .sL9le, html code.shiki .sL9le{--shiki-light:#383A42;--shiki-default:#36F9F6;--shiki-dark:#36F9F6}html pre.shiki code .stweg, html code.shiki .stweg{--shiki-light:#E45649;--shiki-default:#72F1B8;--shiki-dark:#72F1B8}html pre.shiki code .s0frj, html code.shiki .s0frj{--shiki-light:#986801;--shiki-light-font-style:inherit;--shiki-default:#FEDE5D;--shiki-default-font-style:italic;--shiki-dark:#FEDE5D;--shiki-dark-font-style:italic}html pre.shiki code .sEEOt, html code.shiki .sEEOt{--shiki-light:#383A42;--shiki-default:#FFFFFFEE;--shiki-dark:#FFFFFFEE}html pre.shiki code .s6Rhl, html code.shiki .s6Rhl{--shiki-light:#986801;--shiki-default:#FF7EDB;--shiki-dark:#FF7EDB}html pre.shiki code .sQBpM, html code.shiki .sQBpM{--shiki-light:#0184BC;--shiki-default:#FFFFFFEE;--shiki-dark:#FFFFFFEE}html pre.shiki code .sr8De, html code.shiki .sr8De{--shiki-light:#0184BC;--shiki-default:#FE4450;--shiki-dark:#FE4450}html pre.shiki code .s09iw, html code.shiki .s09iw{--shiki-light:#0184BC;--shiki-default:#36F9F6;--shiki-dark:#36F9F6}html pre.shiki code .sXP93, html code.shiki .sXP93{--shiki-light:#0184BC;--shiki-default:#FF8B39;--shiki-dark:#FF8B39}html pre.shiki code .svxf7, html code.shiki .svxf7{--shiki-light:#0184BC;--shiki-default:#F97E72;--shiki-dark:#F97E72}",{"title":231,"searchDepth":245,"depth":245,"links":2445},[2446,2449,2450,2453,2456,2457,2461],{"id":305,"depth":245,"text":306,"children":2447},[2448],{"id":398,"depth":252,"text":399},{"id":564,"depth":245,"text":565},{"id":1037,"depth":245,"text":1038,"children":2451},[2452],{"id":1105,"depth":252,"text":1106},{"id":1208,"depth":245,"text":1209,"children":2454},[2455],{"id":1311,"depth":252,"text":1312},{"id":1441,"depth":245,"text":1442},{"id":2190,"depth":245,"text":2191,"children":2458},[2459,2460],{"id":2194,"depth":252,"text":2195},{"id":2267,"depth":252,"text":2268},{"id":2378,"depth":245,"text":2379},"Learn how to protect your pages and API routes using a layered approach.","md",null,{},{"title":56,"description":2462},"GcMrw_NVzfyFF_Msr4HYk7ISJqv3UqDbAsSOmOppisY",[2469,2471],{"title":52,"path":53,"stem":54,"description":2470,"children":-1},"Access reactive session state with SSR support using `useUserSession()`.",{"title":60,"path":61,"stem":62,"description":2472,"children":-1},"What the module registers for you.",1792535505093]