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