[{"data":1,"prerenderedAt":4114},["ShallowReactive",2],{"navigation_docs":3,"-modules-identity-auth-server":587,"-modules-identity-auth-server-surround":4109},[4,50,155,441,504],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":49},"Get Started","i-lucide-rocket","\u002Fget-started","1.get-started",[10,15,20,25,30,35,40,45],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fget-started\u002Fintroduction","1.get-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fget-started\u002Finstallation","1.get-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Project structure","\u002Fget-started\u002Fproject-structure","1.get-started\u002F3.project-structure","i-lucide-folder-tree",{"title":26,"path":27,"stem":28,"icon":29},"Configuration","\u002Fget-started\u002Fconfiguration","1.get-started\u002F4.configuration","i-lucide-settings",{"title":31,"path":32,"stem":33,"icon":34},"Authentication","\u002Fget-started\u002Fauthentication","1.get-started\u002F5.authentication","i-lucide-lock",{"title":36,"path":37,"stem":38,"icon":39},"Adding modules","\u002Fget-started\u002Fadding-modules","1.get-started\u002F6.adding-modules","i-lucide-puzzle",{"title":41,"path":42,"stem":43,"icon":44},"Database","\u002Fget-started\u002Fdatabase","1.get-started\u002F7.database","i-lucide-database",{"title":46,"path":47,"stem":48,"icon":6},"Deployment","\u002Fget-started\u002Fdeployment","1.get-started\u002F8.deployment",false,{"title":51,"icon":52,"path":53,"stem":54,"children":55,"page":49},"Guide","i-lucide-book-open","\u002Fguide","2.guide",[56,90,115,125],{"title":57,"icon":49,"path":58,"stem":59,"children":60,"page":49},"Concepts","\u002Fguide\u002Fconcepts","2.guide\u002F1.concepts",[61,66,71,76,80,85],{"title":62,"path":63,"stem":64,"icon":65},"Architecture","\u002Fguide\u002Fconcepts\u002Farchitecture","2.guide\u002F1.concepts\u002F1.architecture","i-lucide-blocks",{"title":67,"path":68,"stem":69,"icon":70},"Access control","\u002Fguide\u002Fconcepts\u002Fauth-and-scopes","2.guide\u002F1.concepts\u002F2.auth-and-scopes","i-lucide-shield-check",{"title":72,"path":73,"stem":74,"icon":75},"Extension points","\u002Fguide\u002Fconcepts\u002Fextension-points","2.guide\u002F1.concepts\u002F3.extension-points","i-lucide-plug",{"title":77,"path":78,"stem":79,"icon":44},"Database extensions","\u002Fguide\u002Fconcepts\u002Fdatabase-extensions","2.guide\u002F1.concepts\u002F4.database-extensions",{"title":81,"path":82,"stem":83,"icon":84},"Identity","\u002Fguide\u002Fconcepts\u002Fidentity","2.guide\u002F1.concepts\u002F5.identity","i-lucide-fingerprint",{"title":86,"path":87,"stem":88,"icon":89},"Glossary","\u002Fguide\u002Fconcepts\u002Fglossary","2.guide\u002F1.concepts\u002F6.glossary","i-lucide-book-a",{"title":91,"icon":49,"path":92,"stem":93,"children":94,"page":49},"UX patterns","\u002Fguide\u002Fux-patterns","2.guide\u002F2.ux-patterns",[95,100,105,110],{"title":96,"path":97,"stem":98,"icon":99},"Layouts and navigation","\u002Fguide\u002Fux-patterns\u002Flayouts-and-navigation","2.guide\u002F2.ux-patterns\u002F1.layouts-and-navigation","i-lucide-layout-template",{"title":101,"path":102,"stem":103,"icon":104},"Pages","\u002Fguide\u002Fux-patterns\u002Fpages","2.guide\u002F2.ux-patterns\u002F2.pages","i-lucide-table",{"title":106,"path":107,"stem":108,"icon":109},"Forms and actions","\u002Fguide\u002Fux-patterns\u002Fforms-and-actions","2.guide\u002F2.ux-patterns\u002F3.forms-and-actions","i-lucide-square-pen",{"title":111,"path":112,"stem":113,"icon":114},"Text and formatting","\u002Fguide\u002Fux-patterns\u002Ftext-and-formatting","2.guide\u002F2.ux-patterns\u002F4.text-and-formatting","i-lucide-type",{"title":116,"icon":49,"path":117,"stem":118,"children":119,"page":49},"Best practices","\u002Fguide\u002Fbest-practices","2.guide\u002F3.best-practices",[120],{"title":121,"path":122,"stem":123,"icon":124},"HTTP routes","\u002Fguide\u002Fbest-practices\u002Fhttp-routes","2.guide\u002F3.best-practices\u002F1.http-routes","i-lucide-route",{"title":126,"icon":49,"path":127,"stem":128,"children":129,"page":49},"Going further","\u002Fguide\u002Fgoing-further","2.guide\u002F4.going-further",[130,135,140,145,150],{"title":131,"path":132,"stem":133,"icon":134},"Use your own OAuth server","\u002Fguide\u002Fgoing-further\u002Fown-oauth-server","2.guide\u002F4.going-further\u002F1.own-oauth-server","i-lucide-key-round",{"title":136,"path":137,"stem":138,"icon":139},"Create an OAuth application","\u002Fguide\u002Fgoing-further\u002Foauth-application","2.guide\u002F4.going-further\u002F2.oauth-application","i-lucide-app-window",{"title":141,"path":142,"stem":143,"icon":144},"Signing keys","\u002Fguide\u002Fgoing-further\u002Fsigning-keys","2.guide\u002F4.going-further\u002F3.signing-keys","i-lucide-key-square",{"title":146,"path":147,"stem":148,"icon":149},"Stripe setup","\u002Fguide\u002Fgoing-further\u002Fstripe-setup","2.guide\u002F4.going-further\u002F4.stripe-setup","i-lucide-credit-card",{"title":151,"path":152,"stem":153,"icon":154},"Migrate to the permission model","\u002Fguide\u002Fgoing-further\u002Fmigrate-access-control","2.guide\u002F4.going-further\u002F5.migrate-access-control","i-lucide-arrow-right-left",{"title":156,"icon":157,"path":158,"stem":159,"children":160},"Modules","i-lucide-boxes","\u002Fmodules","3.modules",[161,163,244,273,320,375],{"title":156,"path":158,"stem":162},"3.modules\u002Findex",{"title":81,"icon":49,"path":164,"stem":165,"children":166,"page":49},"\u002Fmodules\u002Fidentity","3.modules\u002F1.identity",[167,190,194,198,220,224,240],{"title":168,"path":169,"stem":170,"children":171},"Auth","\u002Fmodules\u002Fidentity\u002Fauth","3.modules\u002F1.identity\u002F1.auth\u002Findex",[172,174,178,182,186],{"title":173,"path":169,"stem":170},"Overview",{"title":175,"path":176,"stem":177},"Server API","\u002Fmodules\u002Fidentity\u002Fauth\u002Fserver","3.modules\u002F1.identity\u002F1.auth\u002F2.server",{"title":179,"path":180,"stem":181},"Reference","\u002Fmodules\u002Fidentity\u002Fauth\u002Freference","3.modules\u002F1.identity\u002F1.auth\u002F3.reference",{"title":183,"path":184,"stem":185},"Client mode","\u002Fmodules\u002Fidentity\u002Fauth\u002Fclient-mode","3.modules\u002F1.identity\u002F1.auth\u002F4.client-mode",{"title":187,"path":188,"stem":189},"OAuth providers","\u002Fmodules\u002Fidentity\u002Fauth\u002Foauth-providers","3.modules\u002F1.identity\u002F1.auth\u002F5.oauth-providers",{"title":191,"path":192,"stem":193},"Users","\u002Fmodules\u002Fidentity\u002Fusers","3.modules\u002F1.identity\u002F2.users",{"title":195,"path":196,"stem":197},"Account","\u002Fmodules\u002Fidentity\u002Faccount","3.modules\u002F1.identity\u002F3.account",{"title":199,"path":200,"stem":201,"children":202},"Authorization","\u002Fmodules\u002Fidentity\u002Fauthorization","3.modules\u002F1.identity\u002F4.authorization\u002Findex",[203,204,208,212,216],{"title":173,"path":200,"stem":201},{"title":205,"path":206,"stem":207},"Roles and requirements","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Froles-and-requirements","3.modules\u002F1.identity\u002F4.authorization\u002F2.roles-and-requirements",{"title":209,"path":210,"stem":211},"Policies","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Fpolicies","3.modules\u002F1.identity\u002F4.authorization\u002F3.policies",{"title":213,"path":214,"stem":215},"Client","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Fclient","3.modules\u002F1.identity\u002F4.authorization\u002F4.client",{"title":217,"path":218,"stem":219},"Admin","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Fadmin","3.modules\u002F1.identity\u002F4.authorization\u002F5.admin",{"title":221,"path":222,"stem":223},"Preferences","\u002Fmodules\u002Fidentity\u002Fpreferences","3.modules\u002F1.identity\u002F5.preferences",{"title":225,"path":226,"stem":227,"children":228},"OAuth Server","\u002Fmodules\u002Fidentity\u002Foauth-server","3.modules\u002F1.identity\u002F6.oauth-server\u002Findex",[229,230,233,237],{"title":173,"path":226,"stem":227},{"title":175,"path":231,"stem":232},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Fserver","3.modules\u002F1.identity\u002F6.oauth-server\u002F2.server",{"title":234,"path":235,"stem":236},"Hooks","\u002Fmodules\u002Fidentity\u002Foauth-server\u002Fhooks","3.modules\u002F1.identity\u002F6.oauth-server\u002F3.hooks",{"title":179,"path":238,"stem":239},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients","3.modules\u002F1.identity\u002F6.oauth-server\u002F4.routes-and-clients",{"title":241,"path":242,"stem":243},"OAuth Resource","\u002Fmodules\u002Fidentity\u002Foauth-resource","3.modules\u002F1.identity\u002F8.oauth-resource",{"title":245,"icon":49,"path":246,"stem":247,"children":248,"page":49},"Commerce","\u002Fmodules\u002Fcommerce","3.modules\u002F2.commerce",[249,253,257],{"title":250,"path":251,"stem":252},"Stripe","\u002Fmodules\u002Fcommerce\u002Fstripe","3.modules\u002F2.commerce\u002F1.stripe",{"title":254,"path":255,"stem":256},"Payments","\u002Fmodules\u002Fcommerce\u002Fpayments","3.modules\u002F2.commerce\u002F2.payments",{"title":258,"path":259,"stem":260,"children":261},"Subscriptions","\u002Fmodules\u002Fcommerce\u002Fsubscriptions","3.modules\u002F2.commerce\u002F3.subscriptions\u002Findex",[262,263,266,270],{"title":173,"path":259,"stem":260},{"title":175,"path":264,"stem":265},"\u002Fmodules\u002Fcommerce\u002Fsubscriptions\u002Fserver","3.modules\u002F2.commerce\u002F3.subscriptions\u002F2.server",{"title":267,"path":268,"stem":269},"App","\u002Fmodules\u002Fcommerce\u002Fsubscriptions\u002Fapp","3.modules\u002F2.commerce\u002F3.subscriptions\u002F3.app",{"title":179,"path":271,"stem":272},"\u002Fmodules\u002Fcommerce\u002Fsubscriptions\u002Freference","3.modules\u002F2.commerce\u002F3.subscriptions\u002F4.reference",{"title":274,"icon":49,"path":275,"stem":276,"children":277,"page":49},"Features","\u002Fmodules\u002Ffeatures","3.modules\u002F3.features",[278,293,312,316],{"title":279,"path":280,"stem":281,"children":282},"Events","\u002Fmodules\u002Ffeatures\u002Fevents","3.modules\u002F3.features\u002F1.events\u002Findex",[283,284,287,290],{"title":173,"path":280,"stem":281},{"title":175,"path":285,"stem":286},"\u002Fmodules\u002Ffeatures\u002Fevents\u002Fserver","3.modules\u002F3.features\u002F1.events\u002F2.server",{"title":267,"path":288,"stem":289},"\u002Fmodules\u002Ffeatures\u002Fevents\u002Fapp","3.modules\u002F3.features\u002F1.events\u002F3.app",{"title":179,"path":291,"stem":292},"\u002Fmodules\u002Ffeatures\u002Fevents\u002Freference","3.modules\u002F3.features\u002F1.events\u002F4.reference",{"title":294,"path":295,"stem":296,"children":297},"Forms","\u002Fmodules\u002Ffeatures\u002Fforms","3.modules\u002F3.features\u002F2.forms\u002Findex",[298,299,303,306,309],{"title":173,"path":295,"stem":296},{"title":300,"path":301,"stem":302},"Definition","\u002Fmodules\u002Ffeatures\u002Fforms\u002Fdefinition","3.modules\u002F3.features\u002F2.forms\u002F2.definition",{"title":175,"path":304,"stem":305},"\u002Fmodules\u002Ffeatures\u002Fforms\u002Fserver","3.modules\u002F3.features\u002F2.forms\u002F3.server",{"title":267,"path":307,"stem":308},"\u002Fmodules\u002Ffeatures\u002Fforms\u002Fapp","3.modules\u002F3.features\u002F2.forms\u002F4.app",{"title":179,"path":310,"stem":311},"\u002Fmodules\u002Ffeatures\u002Fforms\u002Freference","3.modules\u002F3.features\u002F2.forms\u002F5.reference",{"title":313,"path":314,"stem":315},"Cookie Consent","\u002Fmodules\u002Ffeatures\u002Fcookie-consent","3.modules\u002F3.features\u002F3.cookie-consent",{"title":317,"path":318,"stem":319},"MCP","\u002Fmodules\u002Ffeatures\u002Fmcp","3.modules\u002F3.features\u002F4.mcp",{"title":321,"icon":49,"path":322,"stem":323,"children":324,"page":49},"Interface","\u002Fmodules\u002Finterface","3.modules\u002F4.interface",[325,350,353,357,361],{"title":326,"path":327,"stem":328,"children":329},"UI","\u002Fmodules\u002Finterface\u002Fui","3.modules\u002F4.interface\u002F1.ui\u002Findex",[330,331,335,339,343,347],{"title":173,"path":327,"stem":328},{"title":332,"path":333,"stem":334},"Page components","\u002Fmodules\u002Finterface\u002Fui\u002Fpage-components","3.modules\u002F4.interface\u002F1.ui\u002F2.page-components",{"title":336,"path":337,"stem":338},"App shell","\u002Fmodules\u002Finterface\u002Fui\u002Fapp-shell","3.modules\u002F4.interface\u002F1.ui\u002F3.app-shell",{"title":340,"path":341,"stem":342},"Form inputs","\u002Fmodules\u002Finterface\u002Fui\u002Fform-inputs","3.modules\u002F4.interface\u002F1.ui\u002F4.form-inputs",{"title":344,"path":345,"stem":346},"Composables and helpers","\u002Fmodules\u002Finterface\u002Fui\u002Fcomposables","3.modules\u002F4.interface\u002F1.ui\u002F5.composables",{"title":179,"path":348,"stem":349},"\u002Fmodules\u002Finterface\u002Fui\u002Freference","3.modules\u002F4.interface\u002F1.ui\u002F6.reference",{"title":217,"path":351,"stem":352},"\u002Fmodules\u002Finterface\u002Fadmin","3.modules\u002F4.interface\u002F2.admin",{"title":354,"path":355,"stem":356},"Menu","\u002Fmodules\u002Finterface\u002Fmenu","3.modules\u002F4.interface\u002F3.menu",{"title":358,"path":359,"stem":360},"Widget","\u002Fmodules\u002Finterface\u002Fwidget","3.modules\u002F4.interface\u002F4.widget",{"title":362,"path":363,"stem":364,"children":365},"Mobile","\u002Fmodules\u002Finterface\u002Fmobile","3.modules\u002F4.interface\u002F5.mobile\u002Findex",[366,367,371],{"title":173,"path":363,"stem":364},{"title":368,"path":369,"stem":370},"CLI and devices","\u002Fmodules\u002Finterface\u002Fmobile\u002Fcli-and-devices","3.modules\u002F4.interface\u002F5.mobile\u002F2.cli-and-devices",{"title":372,"path":373,"stem":374},"Splash screen","\u002Fmodules\u002Finterface\u002Fmobile\u002Fsplash-screen","3.modules\u002F4.interface\u002F5.mobile\u002F3.splash-screen",{"title":376,"icon":49,"path":377,"stem":378,"children":379,"page":49},"Platform","\u002Fmodules\u002Fplatform","3.modules\u002F5.platform",[380,402,406,423],{"title":381,"path":382,"stem":383,"children":384},"Module Kit","\u002Fmodules\u002Fplatform\u002Fmodule-kit","3.modules\u002F5.platform\u002F1.module-kit\u002Findex",[385,386,390,394,398],{"title":173,"path":382,"stem":383},{"title":387,"path":388,"stem":389},"Server toolkit","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fserver-toolkit","3.modules\u002F5.platform\u002F1.module-kit\u002F2.server-toolkit",{"title":391,"path":392,"stem":393},"Registries and menus","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fregistries-and-menus","3.modules\u002F5.platform\u002F1.module-kit\u002F3.registries-and-menus",{"title":395,"path":396,"stem":397},"Translations","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fi18n","3.modules\u002F5.platform\u002F1.module-kit\u002F4.i18n",{"title":399,"path":400,"stem":401},"Database helpers","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fdatabase","3.modules\u002F5.platform\u002F1.module-kit\u002F5.database",{"title":403,"path":404,"stem":405},"Notify","\u002Fmodules\u002Fplatform\u002Fnotify","3.modules\u002F5.platform\u002F2.notify",{"title":407,"path":408,"stem":409,"children":410},"Email Templates","\u002Fmodules\u002Fplatform\u002Femail-templates","3.modules\u002F5.platform\u002F3.email-templates\u002Findex",[411,412,416,419],{"title":173,"path":408,"stem":409},{"title":413,"path":414,"stem":415},"Email components","\u002Fmodules\u002Fplatform\u002Femail-templates\u002Fcomponents","3.modules\u002F5.platform\u002F3.email-templates\u002F2.components",{"title":175,"path":417,"stem":418},"\u002Fmodules\u002Fplatform\u002Femail-templates\u002Fserver","3.modules\u002F5.platform\u002F3.email-templates\u002F3.server",{"title":420,"path":421,"stem":422},"Module emails","\u002Fmodules\u002Fplatform\u002Femail-templates\u002Fmodule-emails","3.modules\u002F5.platform\u002F3.email-templates\u002F4.module-emails",{"title":424,"path":425,"stem":426,"children":427},"Base Layer","\u002Fmodules\u002Fplatform\u002Fbase-layer","3.modules\u002F5.platform\u002F4.base-layer\u002Findex",[428,429,433,437],{"title":173,"path":425,"stem":426},{"title":430,"path":431,"stem":432},"Content collections","\u002Fmodules\u002Fplatform\u002Fbase-layer\u002Fcontent-collections","3.modules\u002F5.platform\u002F4.base-layer\u002F2.content-collections",{"title":434,"path":435,"stem":436},"Layouts and routing","\u002Fmodules\u002Fplatform\u002Fbase-layer\u002Flayouts-and-routing","3.modules\u002F5.platform\u002F4.base-layer\u002F3.layouts-and-routing",{"title":438,"path":439,"stem":440},"Composables and utils","\u002Fmodules\u002Fplatform\u002Fbase-layer\u002Fcomposables-and-utils","3.modules\u002F5.platform\u002F4.base-layer\u002F4.composables-and-utils",{"title":442,"icon":443,"path":444,"stem":445,"children":446,"page":49},"Examples","i-lucide-code-xml","\u002Fexamples","4.examples",[447,466,476],{"title":448,"icon":49,"path":449,"stem":450,"children":451,"page":49},"Starters","\u002Fexamples\u002Fstarters","4.examples\u002F1.starters",[452,456,461],{"title":453,"path":454,"stem":455,"icon":6},"Workspace starter","\u002Fexamples\u002Fstarters\u002Fworkspace","4.examples\u002F1.starters\u002F1.workspace",{"title":457,"path":458,"stem":459,"icon":460},"Mobile starter","\u002Fexamples\u002Fstarters\u002Fmobile","4.examples\u002F1.starters\u002F2.mobile","i-lucide-smartphone",{"title":462,"path":463,"stem":464,"icon":465},"Standalone mobile starter","\u002Fexamples\u002Fstarters\u002Fmobile-standalone","4.examples\u002F1.starters\u002F3.mobile-standalone","i-lucide-tablet-smartphone",{"title":467,"icon":49,"path":468,"stem":469,"children":470,"page":49},"Apps","\u002Fexamples\u002Fapps","4.examples\u002F2.apps",[471],{"title":472,"path":473,"stem":474,"icon":475},"Frigear website app","\u002Fexamples\u002Fapps\u002Ffrigear-nu-website","4.examples\u002F2.apps\u002F1.frigear-nu-website","i-lucide-layout-dashboard",{"title":477,"icon":49,"path":478,"stem":479,"children":480,"page":49},"Tasks","\u002Fexamples\u002Ftasks","4.examples\u002F3.tasks",[481,485,489,494,499],{"title":482,"path":483,"stem":484,"icon":70},"Protect a route","\u002Fexamples\u002Ftasks\u002Fprotect-a-route","4.examples\u002F3.tasks\u002F1.protect-a-route",{"title":486,"path":487,"stem":488,"icon":475},"Add an admin page","\u002Fexamples\u002Ftasks\u002Fadd-an-admin-page","4.examples\u002F3.tasks\u002F2.add-an-admin-page",{"title":490,"path":491,"stem":492,"icon":493},"Sell event tickets","\u002Fexamples\u002Ftasks\u002Fsell-event-tickets","4.examples\u002F3.tasks\u002F3.sell-event-tickets","i-lucide-ticket",{"title":495,"path":496,"stem":497,"icon":498},"Build a form","\u002Fexamples\u002Ftasks\u002Fbuild-a-form","4.examples\u002F3.tasks\u002F4.build-a-form","i-lucide-clipboard-list",{"title":500,"path":501,"stem":502,"icon":503},"Send an email","\u002Fexamples\u002Ftasks\u002Fsend-an-email","4.examples\u002F3.tasks\u002F5.send-an-email","i-lucide-mail",{"title":505,"icon":506,"path":507,"stem":508,"children":509,"page":49},"Contribute","i-lucide-git-pull-request","\u002Fcontribute","5.contribute",[510,513,518,544,549,552,556,571],{"title":173,"path":511,"stem":512,"icon":506},"\u002Fcontribute\u002Foverview","5.contribute\u002F1.overview",{"title":514,"path":515,"stem":516,"icon":517},"Local development","\u002Fcontribute\u002Flocal-development","5.contribute\u002F2.local-development","i-lucide-terminal",{"title":519,"icon":49,"path":520,"stem":521,"children":522,"page":49},"Writing a module","\u002Fcontribute\u002Fwriting-a-module","5.contribute\u002F3.writing-a-module",[523,527,532,536,541],{"title":524,"path":525,"stem":526,"icon":39},"Scaffold a module","\u002Fcontribute\u002Fwriting-a-module\u002Fscaffold","5.contribute\u002F3.writing-a-module\u002F1.scaffold",{"title":528,"path":529,"stem":530,"icon":531},"Setup checklist","\u002Fcontribute\u002Fwriting-a-module\u002Fsetup-checklist","5.contribute\u002F3.writing-a-module\u002F2.setup-checklist","i-lucide-list-checks",{"title":533,"path":534,"stem":535,"icon":44},"Database tables","\u002Fcontribute\u002Fwriting-a-module\u002Fdatabase-tables","5.contribute\u002F3.writing-a-module\u002F3.database-tables",{"title":537,"path":538,"stem":539,"icon":540},"Testing","\u002Fcontribute\u002Fwriting-a-module\u002Ftesting","5.contribute\u002F3.writing-a-module\u002F4.testing","i-lucide-flask-conical",{"title":67,"path":542,"stem":543,"icon":70},"\u002Fcontribute\u002Fwriting-a-module\u002Faccess-control","5.contribute\u002F3.writing-a-module\u002F5.access-control",{"title":545,"path":546,"stem":547,"icon":548},"Publishing","\u002Fcontribute\u002Fpublishing","5.contribute\u002F4.publishing","i-lucide-package-check",{"title":448,"path":550,"stem":551,"icon":6},"\u002Fcontribute\u002Fstarters","5.contribute\u002F5.starters",{"title":553,"path":554,"stem":555,"icon":52},"Documentation","\u002Fcontribute\u002Fdocumentation","5.contribute\u002F6.documentation",{"title":557,"icon":49,"path":558,"stem":559,"children":560,"page":49},"Tooling","\u002Fcontribute\u002Ftooling","5.contribute\u002F7.tooling",[561,566],{"title":562,"path":563,"stem":564,"icon":565},"CLI","\u002Fcontribute\u002Ftooling\u002Fcli","5.contribute\u002F7.tooling\u002F1.cli","i-lucide-square-terminal",{"title":567,"path":568,"stem":569,"icon":570},"ESLint config","\u002Fcontribute\u002Ftooling\u002Feslint-config","5.contribute\u002F7.tooling\u002F2.eslint-config","i-lucide-spell-check",{"title":572,"icon":49,"path":573,"stem":574,"children":575},"Decisions","\u002Fcontribute\u002Fdecisions","5.contribute\u002F8.decisions\u002Findex",[576,578,583],{"title":572,"path":573,"stem":574,"icon":577},"i-lucide-scale",{"title":579,"path":580,"stem":581,"icon":582},"Session lifetimes per OAuth client","\u002Fcontribute\u002Fdecisions\u002Foauth-token-lifetimes","5.contribute\u002F8.decisions\u002F1.oauth-token-lifetimes","i-lucide-timer",{"title":584,"path":585,"stem":586,"icon":70},"One permission model","\u002Fcontribute\u002Fdecisions\u002Fpermission-model","5.contribute\u002F8.decisions\u002F2.permission-model",{"id":588,"title":589,"body":590,"description":4102,"extension":4103,"links":4104,"meta":4105,"navigation":4106,"path":176,"seo":4107,"stem":177,"__hash__":4108},"docs\u002F3.modules\u002F1.identity\u002F1.auth\u002F2.server.md","Auth server API",{"type":591,"value":592,"toc":4086},"minimark",[593,601,617,621,633,859,877,1077,1085,1120,1151,1171,1271,1293,1318,1331,1334,1359,1363,1447,1451,1485,1535,1555,1559,1575,1706,1724,1739,1756,1760,1780,1901,1964,1969,1995,1999,2012,2039,2074,2087,2096,2138,2144,2147,2209,2215,2232,2238,2242,2259,2350,2360,2371,2374,2393,2410,2414,2428,2446,2457,2584,2587,2599,2608,2904,2925,2931,3504,3637,3689,3697,3700,3767,3773,3977,3980,4023,4061,4065,4082],[594,595,596,597,600],"p",{},"This page shows how a server route learns who is calling and what they may do. It is for app developers who write\nroutes and for module authors. Server mode only. Set up the module first: ",[598,599,173],"a",{"href":169},".",[594,602,603,604,607,608,611,612,614,615,600],{},"An app in ",[598,605,606],{"href":184},"client mode"," has no server of its own. The API it calls protects its\nroutes the way this page shows. That API accepts the app's ",[598,609,610],{"href":87},"access token"," through\n",[598,613,225],{"href":226}," or ",[598,616,241],{"href":242},[618,619,482],"h2",{"id":620},"protect-a-route",[594,622,623,624,628,629,632],{},"Call ",[625,626,627],"code",{},"authorize"," with the permission the route needs. It is auto-imported in server code, accepts cookie sessions,\n",[598,630,631],{"href":87},"API tokens"," and access tokens alike, and returns the principal:",[634,635,640],"pre",{"className":636,"code":637,"language":638,"meta":639,"style":639},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F server\u002Fapi\u002Fevents\u002F[id].put.ts\nexport default defineEventHandler(async (event) => {\n  await authorize(event, 'events:update') \u002F\u002F 401 or 403 before anything is loaded\n  const { id } = await getValidatedRouterParams(event, intIdParams().parse)\n  const ev = await findEvent(id) \u002F\u002F 404 only now\n  const { user } = await authorize(event, 'events:update', ev) \u002F\u002F the policies of the permission see the record\n  return updateEvent(ev, user)\n})\n","ts","",[625,641,642,651,690,723,767,792,831,851],{"__ignoreMap":639},[643,644,647],"span",{"class":645,"line":646},"line",1,[643,648,650],{"class":649},"sHwdD","\u002F\u002F server\u002Fapi\u002Fevents\u002F[id].put.ts\n",[643,652,654,658,661,665,669,673,677,681,684,687],{"class":645,"line":653},2,[643,655,657],{"class":656},"s7zQu","export",[643,659,660],{"class":656}," default",[643,662,664],{"class":663},"s2Zo4"," defineEventHandler",[643,666,668],{"class":667},"sTEyZ","(",[643,670,672],{"class":671},"spNyl","async",[643,674,676],{"class":675},"sMK4o"," (",[643,678,680],{"class":679},"sHdIc","event",[643,682,683],{"class":675},")",[643,685,686],{"class":671}," =>",[643,688,689],{"class":675}," {\n",[643,691,693,696,699,702,704,707,710,714,717,720],{"class":645,"line":692},3,[643,694,695],{"class":656},"  await",[643,697,698],{"class":663}," authorize",[643,700,668],{"class":701},"swJcz",[643,703,680],{"class":667},[643,705,706],{"class":675},",",[643,708,709],{"class":675}," '",[643,711,713],{"class":712},"sfazB","events:update",[643,715,716],{"class":675},"'",[643,718,719],{"class":701},") ",[643,721,722],{"class":649},"\u002F\u002F 401 or 403 before anything is loaded\n",[643,724,726,729,732,735,738,741,744,747,749,751,753,756,759,761,764],{"class":645,"line":725},4,[643,727,728],{"class":671},"  const",[643,730,731],{"class":675}," {",[643,733,734],{"class":667}," id",[643,736,737],{"class":675}," }",[643,739,740],{"class":675}," =",[643,742,743],{"class":656}," await",[643,745,746],{"class":663}," getValidatedRouterParams",[643,748,668],{"class":701},[643,750,680],{"class":667},[643,752,706],{"class":675},[643,754,755],{"class":663}," intIdParams",[643,757,758],{"class":701},"()",[643,760,600],{"class":675},[643,762,763],{"class":667},"parse",[643,765,766],{"class":701},")\n",[643,768,770,772,775,777,779,782,784,787,789],{"class":645,"line":769},5,[643,771,728],{"class":671},[643,773,774],{"class":667}," ev",[643,776,740],{"class":675},[643,778,743],{"class":656},[643,780,781],{"class":663}," findEvent",[643,783,668],{"class":701},[643,785,786],{"class":667},"id",[643,788,719],{"class":701},[643,790,791],{"class":649},"\u002F\u002F 404 only now\n",[643,793,795,797,799,802,804,806,808,810,812,814,816,818,820,822,824,826,828],{"class":645,"line":794},6,[643,796,728],{"class":671},[643,798,731],{"class":675},[643,800,801],{"class":667}," user",[643,803,737],{"class":675},[643,805,740],{"class":675},[643,807,743],{"class":656},[643,809,698],{"class":663},[643,811,668],{"class":701},[643,813,680],{"class":667},[643,815,706],{"class":675},[643,817,709],{"class":675},[643,819,713],{"class":712},[643,821,716],{"class":675},[643,823,706],{"class":675},[643,825,774],{"class":667},[643,827,719],{"class":701},[643,829,830],{"class":649},"\u002F\u002F the policies of the permission see the record\n",[643,832,834,837,840,842,845,847,849],{"class":645,"line":833},7,[643,835,836],{"class":656},"  return",[643,838,839],{"class":663}," updateEvent",[643,841,668],{"class":701},[643,843,844],{"class":667},"ev",[643,846,706],{"class":675},[643,848,801],{"class":667},[643,850,766],{"class":701},[643,852,854,857],{"class":645,"line":853},8,[643,855,856],{"class":675},"}",[643,858,766],{"class":667},[594,860,861,862,865,866,614,869,872,873,876],{},"This is the two-step route: the first call has no record, so a ",[625,863,864],{},"404"," can never answer before a ",[625,867,868],{},"401",[625,870,871],{},"403","; the\nsecond runs the ",[598,874,875],{"href":210},"policies"," that refine the permission for this record.",[878,879,880,893],"table",{},[881,882,883],"thead",{},[884,885,886,890],"tr",{},[887,888,889],"th",{},"Call",[887,891,892],{},"Behaviour",[894,895,896,920,934,967,980,994,1012,1027,1047,1064],"tbody",{},[884,897,898,904],{},[899,900,901],"td",{},[625,902,903],{},"authorize(event, permission, record?)",[899,905,906,907,910,911,910,914,614,917,600],{},"Returns the principal, or throws ",[625,908,909],{},"401 unauthorized",", ",[625,912,913],{},"403 session_required",[625,915,916],{},"403 insufficient_scope",[625,918,919],{},"403 forbidden",[884,921,922,931],{},[899,923,924,927,928],{},[625,925,926],{},"allows(event, permission, record?)"," \u002F ",[625,929,930],{},"denies(...)",[899,932,933],{},"The same decision as a boolean. Never throws for a denial.",[884,935,936,941],{},[899,937,938],{},[625,939,940],{},"inspect(event, permission, record?)",[899,942,943,946,947,950,951,910,954,910,957,910,960,910,963,966],{},[625,944,945],{},"{ allowed, status, code, via }",", where ",[625,948,949],{},"via"," says which step decided (",[625,952,953],{},"registry",[625,955,956],{},"credential",[625,958,959],{},"before",[625,961,962],{},"role",[625,964,965],{},"policy",").",[884,968,969,974],{},[899,970,971],{},[625,972,973],{},"requireAuth(event, { sessionOnly? })",[899,975,976,977,979],{},"Authentication only: the principal from any credential, ",[625,978,868],{}," without one. Checks no permission.",[884,981,982,987],{},[899,983,984],{},[625,985,986],{},"getAuth(event)",[899,988,989,990,993],{},"The principal or ",[625,991,992],{},"null",". Never throws.",[884,995,996,1001],{},[899,997,998],{},[625,999,1000],{},"canGrantScope(event, scope, options?)",[899,1002,1003,1004,1007,1008,600],{},"Whether the caller may give ",[625,1005,1006],{},"scope"," to a token or an OAuth client. See ",[598,1009,1011],{"href":1010},"#user-api-tokens","Tokens",[884,1013,1014,1019],{},[899,1015,1016],{},[625,1017,1018],{},"userCan(event, userId, permission)",[899,1020,1021,1022,1026],{},"Whether ",[1023,1024,1025],"strong",{},"another"," user holds a permission: their roles and the before hook, no policies. For assignment and target checks only, never for the caller.",[884,1028,1029,1034],{},[899,1030,1031],{},[625,1032,1033],{},"getAccess(event)",[899,1035,1036,1037,1040,1041,1044,1045,600],{},"The ",[625,1038,1039],{},"AccessDto"," behind ",[625,1042,1043],{},"GET \u002Fapi\u002Fauth\u002Fme\u002Faccess",", or ",[625,1046,992],{},[884,1048,1049,1054],{},[899,1050,1051],{},[625,1052,1053],{},"getEffectiveRoles(event, user)",[899,1055,1056,1057,1060,1061,600],{},"The roles the user holds now: assigned, default and ",[625,1058,1059],{},"auto"," roles, minus failed requirements, closed over ",[625,1062,1063],{},"inherit",[884,1065,1066,1071],{},[899,1067,1068],{},[625,1069,1070],{},"definePolicy(permission, fn, { withoutRecord? })",[899,1072,1073,1074,600],{},"Defines a ",[598,1075,1076],{"href":210},"server policy",[1078,1079,1036,1081,1084],"h3",{"id":1080},"the-permission-type",[625,1082,1083],{},"Permission"," type",[594,1086,1087,1088,1091,1092,910,1095,910,1098,1101,1102,1105,1106,1108,1109,1112,1113,1115,1116,1119],{},"Every ",[625,1089,1090],{},"permission"," parameter above (and ",[625,1093,1094],{},"defineSharedPolicy",[625,1096,1097],{},"useAuthorization().can",[625,1099,1100],{},"\u003CCan>",", route rules,\n",[625,1103,1104],{},"definePageMeta({ authorize })",", MCP definitions and menu, widget and tab items) is typed ",[625,1107,1083],{},". Auth generates\n",[625,1110,1111],{},".nuxt\u002Ftypes\u002Fauth-access.d.ts"," from the registered permissions and roles, so in an app ",[625,1114,1083],{}," is the union of\nevery registered name and a typo fails typecheck in the app, the server and ",[625,1117,1118],{},"nuxt.config.ts",":",[634,1121,1123],{"className":636,"code":1122,"language":638,"meta":639,"style":639},"await authorize(event, 'events:mange')\n\u002F\u002F error: Argument of type '\"events:mange\"' is not assignable to parameter of type 'Permission'. Did you mean '\"events:manage\"'?\n",[625,1124,1125,1146],{"__ignoreMap":639},[643,1126,1127,1130,1132,1135,1137,1139,1142,1144],{"class":645,"line":646},[643,1128,1129],{"class":656},"await",[643,1131,698],{"class":663},[643,1133,1134],{"class":667},"(event",[643,1136,706],{"class":675},[643,1138,709],{"class":675},[643,1140,1141],{"class":712},"events:mange",[643,1143,716],{"class":675},[643,1145,766],{"class":667},[643,1147,1148],{"class":645,"line":653},[643,1149,1150],{"class":649},"\u002F\u002F error: Argument of type '\"events:mange\"' is not assignable to parameter of type 'Permission'. Did you mean '\"events:manage\"'?\n",[594,1152,1153,1154,1157,1158,1160,1161,1164,1165,1168,1169,600],{},"Where nothing is generated (a module package's own build, or before the first ",[625,1155,1156],{},"nuxi prepare","), ",[625,1159,1083],{}," is\n",[625,1162,1163],{},"string",". A name built at runtime needs a cast (",[625,1166,1167],{},"name as Permission","): the build still rejects unknown permissions in\nroute rules and page meta, and a check of an unknown name is ",[625,1170,919],{},[634,1172,1174],{"className":636,"code":1173,"language":638,"meta":639,"style":639},"interface AccessDto {\n  kind: 'session' | 'user-token' | 'oauth'\n  scopes: string[] \u002F\u002F the credential's scopes; a session has none\n  permissions: string[] \u002F\u002F registered permissions a check without a record allows\n  roles?: string[] \u002F\u002F for a session, or a token with the `profile` scope\n}\n",[625,1175,1176,1187,1221,1237,1251,1266],{"__ignoreMap":639},[643,1177,1178,1181,1185],{"class":645,"line":646},[643,1179,1180],{"class":671},"interface",[643,1182,1184],{"class":1183},"sBMFI"," AccessDto",[643,1186,689],{"class":675},[643,1188,1189,1192,1194,1196,1199,1201,1204,1206,1209,1211,1213,1215,1218],{"class":645,"line":653},[643,1190,1191],{"class":701},"  kind",[643,1193,1119],{"class":675},[643,1195,709],{"class":675},[643,1197,1198],{"class":712},"session",[643,1200,716],{"class":675},[643,1202,1203],{"class":675}," |",[643,1205,709],{"class":675},[643,1207,1208],{"class":712},"user-token",[643,1210,716],{"class":675},[643,1212,1203],{"class":675},[643,1214,709],{"class":675},[643,1216,1217],{"class":712},"oauth",[643,1219,1220],{"class":675},"'\n",[643,1222,1223,1226,1228,1231,1234],{"class":645,"line":692},[643,1224,1225],{"class":701},"  scopes",[643,1227,1119],{"class":675},[643,1229,1230],{"class":1183}," string",[643,1232,1233],{"class":667},"[] ",[643,1235,1236],{"class":649},"\u002F\u002F the credential's scopes; a session has none\n",[643,1238,1239,1242,1244,1246,1248],{"class":645,"line":725},[643,1240,1241],{"class":701},"  permissions",[643,1243,1119],{"class":675},[643,1245,1230],{"class":1183},[643,1247,1233],{"class":667},[643,1249,1250],{"class":649},"\u002F\u002F registered permissions a check without a record allows\n",[643,1252,1253,1256,1259,1261,1263],{"class":645,"line":769},[643,1254,1255],{"class":701},"  roles",[643,1257,1258],{"class":675},"?:",[643,1260,1230],{"class":1183},[643,1262,1233],{"class":667},[643,1264,1265],{"class":649},"\u002F\u002F for a session, or a token with the `profile` scope\n",[643,1267,1268],{"class":645,"line":794},[643,1269,1270],{"class":675},"}\n",[1272,1273,1274,1275,1278,1279,1281,1282,910,1285,1288,1289,1292],"note",{},"Whether a permission is ",[625,1276,1277],{},"sessionOnly"," comes from its definition, never from the call. Money and credential actions get\ntheir own ",[625,1280,1277],{}," permissions (",[625,1283,1284],{},"payments:refund",[625,1286,1287],{},"users:manage","), so a handler cannot forget the option. Use\n",[625,1290,1291],{},"requireAuth(event, { sessionOnly: true })"," only for a route that checks no permission of its own.",[594,1294,1295,1296,1300,1301,1305,1306,1309,1310,1313,1314,1317],{},"The three kinds of credential, and what the principal holds for each, are explained in\n",[598,1297,1299],{"href":1298},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#the-principal","The principal",", and the order in which a check is decided in\n",[598,1302,1304],{"href":1303},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#how-a-check-is-decided","How a check is decided",". ",[598,1307,191],{"href":1308},"\u002Fmodules\u002Fidentity\u002Fusers#server","\nbuilds ",[625,1311,1312],{},"requireUser(event)"," on top of ",[625,1315,1316],{},"requireAuth","; it returns just the user.",[1319,1320,1321,1322,1324,1325,1327,1328,1330],"caution",{},"Use ",[625,1323,627],{},", not ",[625,1326,1316],{},", for anything that is not \"any signed-in user\". A route that only calls\n",[625,1329,1316],{}," lets every signed-in user, and every token, through.",[594,1332,1333],{},"A role change applies on the next request: a session and a bearer request both load the user and their roles again.",[594,1335,1336,1339,1340,1343,1344,1347,1348,1350,1351,1354,1355,1358],{},[1023,1337,1338],{},"A bad token is never ignored."," A request may carry a token that a resolver claims: the API token prefix (",[625,1341,1342],{},"fnu_…"," by\ndefault) or a JWT. When that token is invalid, expired or revoked, the request is ",[1023,1345,1346],{},"unauthenticated",", even with a valid\nsession cookie. The answer is ",[625,1349,868],{}," with ",[625,1352,1353],{},"WWW-Authenticate: Bearer error=\"invalid_token\"",". Tokens no resolver claims,\nsuch as the per-form ",[625,1356,1357],{},"ffk_"," tokens, are ignored, and the other credentials apply.",[1078,1360,1362],{"id":1361},"other-helpers","Other helpers",[634,1364,1366],{"className":636,"code":1365,"language":638,"meta":639,"style":639},"mapUserToSession(storedUser) \u002F\u002F strips the password hash and applies `session.mapper`; use it to refresh the session\nstartAuthSession(event, storedUser) \u002F\u002F signs the user in: a new session stamped with their `sessionVersion` and `signedInAt`\nendAuthSessions(userId, event?) \u002F\u002F ends every session of the user, except the one of `event` (a \"sign out everywhere\")\ngetLoginPath(event) \u002F\u002F the sign-in page path (`pages.login`, else `\u002Flogin`)\ndeleteUserAuthData(userId) \u002F\u002F removes tokens, kept OAuth tokens, passkeys, reset codes and sign-in links (call when deleting a user)\ndeleteUnusedAuthEmailCodes(userId) \u002F\u002F removes unused reset codes and sign-in links (call when the user's email changes)\n",[625,1367,1368,1379,1394,1415,1426,1437],{"__ignoreMap":639},[643,1369,1370,1373,1376],{"class":645,"line":646},[643,1371,1372],{"class":663},"mapUserToSession",[643,1374,1375],{"class":667},"(storedUser) ",[643,1377,1378],{"class":649},"\u002F\u002F strips the password hash and applies `session.mapper`; use it to refresh the session\n",[643,1380,1381,1384,1386,1388,1391],{"class":645,"line":653},[643,1382,1383],{"class":663},"startAuthSession",[643,1385,1134],{"class":667},[643,1387,706],{"class":675},[643,1389,1390],{"class":667}," storedUser) ",[643,1392,1393],{"class":649},"\u002F\u002F signs the user in: a new session stamped with their `sessionVersion` and `signedInAt`\n",[643,1395,1396,1399,1402,1404,1407,1410,1412],{"class":645,"line":692},[643,1397,1398],{"class":663},"endAuthSessions",[643,1400,1401],{"class":667},"(userId",[643,1403,706],{"class":675},[643,1405,1406],{"class":667}," event",[643,1408,1409],{"class":675},"?",[643,1411,719],{"class":667},[643,1413,1414],{"class":649},"\u002F\u002F ends every session of the user, except the one of `event` (a \"sign out everywhere\")\n",[643,1416,1417,1420,1423],{"class":645,"line":725},[643,1418,1419],{"class":663},"getLoginPath",[643,1421,1422],{"class":667},"(event) ",[643,1424,1425],{"class":649},"\u002F\u002F the sign-in page path (`pages.login`, else `\u002Flogin`)\n",[643,1427,1428,1431,1434],{"class":645,"line":769},[643,1429,1430],{"class":663},"deleteUserAuthData",[643,1432,1433],{"class":667},"(userId) ",[643,1435,1436],{"class":649},"\u002F\u002F removes tokens, kept OAuth tokens, passkeys, reset codes and sign-in links (call when deleting a user)\n",[643,1438,1439,1442,1444],{"class":645,"line":794},[643,1440,1441],{"class":663},"deleteUnusedAuthEmailCodes",[643,1443,1433],{"class":667},[643,1445,1446],{"class":649},"\u002F\u002F removes unused reset codes and sign-in links (call when the user's email changes)\n",[618,1448,1450],{"id":1449},"define-permissions-roles-and-scopes","Define permissions, roles and scopes",[594,1452,1453,1454,676,1457,1460,1461,1464,1465,1350,1468,1471,1472,1476,1477,1480,1481,1484],{},"A module declares what it offers with ",[625,1455,1456],{},"defineAccess",[625,1458,1459],{},"@frigear-nu\u002Fauth\u002Faccess",") in a ",[625,1462,1463],{},"src\u002Fruntime\u002Faccess.ts",", and\nregisters it in ",[625,1466,1467],{},"setup()",[625,1469,1470],{},"registerAccess"," (see ",[598,1473,1475],{"href":1474},"#extend-auth-at-build-time","Extend Auth at build time"," and\n",[598,1478,1479],{"href":542},"Access control for module authors","). Everything merges with the host app's\nconfig at ",[625,1482,1483],{},"modules:done",", and the build stops when something does not add up.",[1486,1487,1488,1508,1518],"ul",{},[1489,1490,1491,1492,1494,1495,1498,1499,1503,1504,1507],"li",{},"A ",[1023,1493,1090],{}," is a ",[625,1496,1497],{},"resource:action"," string. ",[598,1500,1502],{"href":1501},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#permissions-and-sensitive-ones","Permissions and sensitive ones","\nexplains wildcards and ",[625,1505,1506],{},":pii"," permissions, which a wildcard never covers.",[1489,1509,1491,1510,1512,1513,1517],{},[1023,1511,1006],{}," limits what a token may do. A scope is either a permission, a wildcard, or a named scope that expands to\npermissions. ",[598,1514,1516],{"href":1515},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#scopes","Scopes"," has the rules.",[1489,1519,1520,1521,1476,1524,1527,1528,1531,1532,1534],{},"A user is only offered the scopes they may give away. The token page, ",[625,1522,1523],{},"GET \u002Fapi\u002Fauth\u002Fscopes",[625,1525,1526],{},"POST \u002Fapi\u002Fauth\u002Fme\u002Ftokens"," use ",[625,1529,1530],{},"canGrantScope",": every permission a scope expands to has to pass the before hook and\nthe user's roles (no policies), and ",[625,1533,1277],{}," and sensitive permissions are never part of a wildcard.",[594,1536,1537,1538,910,1540,910,1542,1544,1545,910,1547,1544,1549,910,1551,1476,1553,600],{},"The permissions each built-in module registers are on its own page, in a Permissions table:\n",[598,1539,279],{"href":280},[598,1541,294],{"href":295},[598,1543,254],{"href":255},",\n",[598,1546,258],{"href":259},[598,1548,191],{"href":192},[598,1550,225],{"href":226},[598,1552,199],{"href":200},[598,1554,217],{"href":351},[618,1556,1558],{"id":1557},"answer-with-the-before-hook","Answer with the before hook",[594,1560,1561,1562,1565,1566,1569,1570,1574],{},"A module never decides who is ",[598,1563,1564],{"href":87},"staff",". Roles do, and the one override your host app has is\nthe ",[625,1567,1568],{},"authorization:before"," Nitro hook. ",[598,1571,1573],{"href":1572},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#the-before-hook","The before hook"," shows how\nyour app answers it:",[634,1576,1578],{"className":636,"code":1577,"language":638,"meta":639,"style":639},"\u002F\u002F server\u002Fplugins\u002Faccess.ts\nexport default defineNitroPlugin((nitro) => {\n  nitro.hooks.hook('authorization:before', (ctx) => {\n    if (ctx.permission === 'events:update' && ctx.user.suspended) ctx.deny()\n  })\n})\n",[625,1579,1580,1585,1607,1643,1693,1700],{"__ignoreMap":639},[643,1581,1582],{"class":645,"line":646},[643,1583,1584],{"class":649},"\u002F\u002F server\u002Fplugins\u002Faccess.ts\n",[643,1586,1587,1589,1591,1594,1596,1598,1601,1603,1605],{"class":645,"line":653},[643,1588,657],{"class":656},[643,1590,660],{"class":656},[643,1592,1593],{"class":663}," defineNitroPlugin",[643,1595,668],{"class":667},[643,1597,668],{"class":675},[643,1599,1600],{"class":679},"nitro",[643,1602,683],{"class":675},[643,1604,686],{"class":671},[643,1606,689],{"class":675},[643,1608,1609,1612,1614,1617,1619,1622,1624,1626,1628,1630,1632,1634,1637,1639,1641],{"class":645,"line":692},[643,1610,1611],{"class":667},"  nitro",[643,1613,600],{"class":675},[643,1615,1616],{"class":667},"hooks",[643,1618,600],{"class":675},[643,1620,1621],{"class":663},"hook",[643,1623,668],{"class":701},[643,1625,716],{"class":675},[643,1627,1568],{"class":712},[643,1629,716],{"class":675},[643,1631,706],{"class":675},[643,1633,676],{"class":675},[643,1635,1636],{"class":679},"ctx",[643,1638,683],{"class":675},[643,1640,686],{"class":671},[643,1642,689],{"class":675},[643,1644,1645,1648,1650,1652,1654,1656,1659,1661,1663,1665,1668,1671,1673,1676,1678,1681,1683,1685,1687,1690],{"class":645,"line":725},[643,1646,1647],{"class":656},"    if",[643,1649,676],{"class":701},[643,1651,1636],{"class":667},[643,1653,600],{"class":675},[643,1655,1090],{"class":667},[643,1657,1658],{"class":675}," ===",[643,1660,709],{"class":675},[643,1662,713],{"class":712},[643,1664,716],{"class":675},[643,1666,1667],{"class":675}," &&",[643,1669,1670],{"class":667}," ctx",[643,1672,600],{"class":675},[643,1674,1675],{"class":667},"user",[643,1677,600],{"class":675},[643,1679,1680],{"class":667},"suspended",[643,1682,719],{"class":701},[643,1684,1636],{"class":667},[643,1686,600],{"class":675},[643,1688,1689],{"class":663},"deny",[643,1691,1692],{"class":701},"()\n",[643,1694,1695,1698],{"class":645,"line":769},[643,1696,1697],{"class":675},"  }",[643,1699,766],{"class":701},[643,1701,1702,1704],{"class":645,"line":794},[643,1703,856],{"class":675},[643,1705,766],{"class":667},[594,1707,1708,1709,1712,1713,1716,1717,1720,1721,1723],{},"Listeners get ",[625,1710,1711],{},"{ event, principal, user, permission, record, allow(), deny() }",". Results are collected across\nlisteners: any ",[625,1714,1715],{},"deny()",", or a listener that throws, denies; otherwise any ",[625,1718,1719],{},"allow()"," allows. A bearer request still has\nto pass the credential's own limits first, so the hook can never widen a token or use a ",[625,1722,1277],{}," permission.",[1725,1726,1727,1728,614,1731,1734,1735,1738],"warning",{},"A listener must identify the caller from ",[625,1729,1730],{},"ctx.user",[625,1732,1733],{},"ctx.principal",", never from the session on ",[625,1736,1737],{},"ctx.event",". The\nsession is empty for bearer requests.",[594,1740,1741,1742,910,1745,1748,1749,1752,1753,600],{},"The second hook is ",[625,1743,1744],{},"authorization:roles",[625,1746,1747],{},"{ event, user, roles }",", from ",[598,1750,199],{"href":1751},"\u002Fmodules\u002Fidentity\u002Fauthorization\u002Froles-and-requirements#where-a-users-roles-come-from",".\nIt lets the host add or remove roles for a user, such as mapping an allowlist of emails to ",[625,1754,1755],{},"admin",[618,1757,1759],{"id":1758},"route-rules","Route rules",[594,1761,1762,910,1764,1767,1768,1771,1772,1775,1776,1779],{},[625,1763,1090],{},[625,1765,1766],{},"authenticated"," and ",[625,1769,1770],{},"guest"," are keys of Nuxt's ",[625,1773,1774],{},"routeRules",". Auth's server middleware enforces them for\n",[625,1777,1778],{},"\u002Fapi\u002F**"," with or without Authorization, so they fail closed:",[634,1781,1783],{"className":636,"code":1782,"language":638,"meta":639,"style":639},"export default defineNuxtConfig({\n  routeRules: {\n    '\u002Fapi\u002Fadmin\u002F**': { permission: 'admin:access' }, \u002F\u002F 401 for visitors, 403 without the permission\n    '\u002Fapi\u002Freports\u002F**': { authenticated: true }, \u002F\u002F any credential\n    '\u002Fapi\u002Fpublic\u002F**': { guest: true },\n  },\n})\n",[625,1784,1785,1799,1808,1840,1867,1890,1895],{"__ignoreMap":639},[643,1786,1787,1789,1791,1794,1796],{"class":645,"line":646},[643,1788,657],{"class":656},[643,1790,660],{"class":656},[643,1792,1793],{"class":663}," defineNuxtConfig",[643,1795,668],{"class":667},[643,1797,1798],{"class":675},"{\n",[643,1800,1801,1804,1806],{"class":645,"line":653},[643,1802,1803],{"class":701},"  routeRules",[643,1805,1119],{"class":675},[643,1807,689],{"class":675},[643,1809,1810,1813,1816,1818,1820,1822,1825,1827,1829,1832,1834,1837],{"class":645,"line":692},[643,1811,1812],{"class":675},"    '",[643,1814,1815],{"class":701},"\u002Fapi\u002Fadmin\u002F**",[643,1817,716],{"class":675},[643,1819,1119],{"class":675},[643,1821,731],{"class":675},[643,1823,1824],{"class":701}," permission",[643,1826,1119],{"class":675},[643,1828,709],{"class":675},[643,1830,1831],{"class":712},"admin:access",[643,1833,716],{"class":675},[643,1835,1836],{"class":675}," },",[643,1838,1839],{"class":649}," \u002F\u002F 401 for visitors, 403 without the permission\n",[643,1841,1842,1844,1847,1849,1851,1853,1856,1858,1862,1864],{"class":645,"line":725},[643,1843,1812],{"class":675},[643,1845,1846],{"class":701},"\u002Fapi\u002Freports\u002F**",[643,1848,716],{"class":675},[643,1850,1119],{"class":675},[643,1852,731],{"class":675},[643,1854,1855],{"class":701}," authenticated",[643,1857,1119],{"class":675},[643,1859,1861],{"class":1860},"sfNiH"," true",[643,1863,1836],{"class":675},[643,1865,1866],{"class":649}," \u002F\u002F any credential\n",[643,1868,1869,1871,1874,1876,1878,1880,1883,1885,1887],{"class":645,"line":769},[643,1870,1812],{"class":675},[643,1872,1873],{"class":701},"\u002Fapi\u002Fpublic\u002F**",[643,1875,716],{"class":675},[643,1877,1119],{"class":675},[643,1879,731],{"class":675},[643,1881,1882],{"class":701}," guest",[643,1884,1119],{"class":675},[643,1886,1861],{"class":1860},[643,1888,1889],{"class":675}," },\n",[643,1891,1892],{"class":645,"line":794},[643,1893,1894],{"class":675},"  },\n",[643,1896,1897,1899],{"class":645,"line":833},[643,1898,856],{"class":675},[643,1900,766],{"class":667},[1486,1902,1903,1915,1925,1939,1954],{},[1489,1904,1905,1907,1908,1911,1912,1914],{},[625,1906,1090],{}," runs ",[625,1909,1910],{},"authorize(event, permission)",", so tokens, ",[625,1913,1277],{}," and the before hook apply as in a handler.\nAn unknown permission fails the build.",[1489,1916,1917,1920,1921,1924],{},[625,1918,1919],{},"authenticated: true"," needs any valid credential; ",[625,1922,1923],{},"authenticated: false"," turns the rules of less specific patterns\noff for that path.",[1489,1926,1927,1928,1931,1932,1935,1936,600],{},"The match uses the percent-decoded path the router dispatches on, and an API path with an encoded ",[625,1929,1930],{},"\u002F",", a ",[625,1933,1934],{},"\\"," or a\ndot segment is refused with ",[625,1937,1938],{},"400",[1489,1940,1941,1942,1944,1945,910,1948,1951,1952,600],{},"Only pages and ",[625,1943,1778],{}," are enforced. Guard other routes (",[625,1946,1947],{},"\u002Fmcp",[625,1949,1950],{},"\u002Foauth\u002F**",") in their handler with ",[625,1953,627],{},[1489,1955,1956,1957,1959,1960,1963],{},"Rules are coarse. A handler still calls ",[625,1958,627],{}," for its own permission and its records. See also\n",[598,1961,1759],{"href":1962},"\u002Fmodules\u002Fidentity\u002Fauthorization\u002Fclient#route-rules"," for pages.",[618,1965,1967],{"id":1966},"get-apiauthmeaccess",[625,1968,1043],{},[594,1970,1971,1972,1974,1975,1978,1979,1982,1983,1986,1987,1990,1991,1994],{},"Answers what the caller may do, for any credential: the ",[625,1973,1039],{}," above, with ",[625,1976,1977],{},"Cache-Control: private, no-store",". It\nis what ",[625,1980,1981],{},"useAuthorization()"," reads in the browser, and what a mobile app or another service calls with its bearer\ntoken. ",[625,1984,1985],{},"permissions"," is every registered permission where a check without a record passes, so a policy that needs a\nrecord is not reflected here. ",[625,1988,1989],{},"roles"," appears for a session, and for a token that carries the ",[625,1992,1993],{},"profile"," scope.",[618,1996,1998],{"id":1997},"user-api-tokens","User API tokens",[594,2000,2001,2002,2004,2005,2008,2009,2011],{},"Users create API tokens (",[625,2003,1342],{},") on the account page (",[625,2006,2007],{},"\u002Faccount\u002Fapi-tokens",", see\n",[598,2010,195],{"href":196},") or through the API. A script or an MCP client sends one like this:",[634,2013,2017],{"className":2014,"code":2015,"language":2016,"meta":639,"style":639},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -H \"Authorization: Bearer fnu_…\" https:\u002F\u002Fexample.com\u002Fapi\u002Ftickets\n","bash",[625,2018,2019],{"__ignoreMap":639},[643,2020,2021,2024,2027,2030,2033,2036],{"class":645,"line":646},[643,2022,2023],{"class":1183},"curl",[643,2025,2026],{"class":712}," -H",[643,2028,2029],{"class":675}," \"",[643,2031,2032],{"class":712},"Authorization: Bearer fnu_…",[643,2034,2035],{"class":675},"\"",[643,2037,2038],{"class":712}," https:\u002F\u002Fexample.com\u002Fapi\u002Ftickets\n",[1486,2040,2041,2044,2047,2057,2068],{},[1489,2042,2043],{},"Only a hash (SHA-256) of a token is stored, plus a prefix and the last four characters.",[1489,2045,2046],{},"A lost token is replaced, not looked up.",[1489,2048,2049,2050,2052,2053,2056],{},"Send a token in the ",[625,2051,199],{}," header ",[1023,2054,2055],{},"only",". Tokens in the query string are ignored.",[1489,2058,2059,2060,2063,2064,2067],{},"Tokens can expire (",[625,2061,2062],{},"expiresInDays",", or the ",[625,2065,2066],{},"tokenTtlDays"," option) and can be revoked.",[1489,2069,2070,2073],{},[625,2071,2072],{},"lastUsedAt"," is written at most every five minutes. A failure to write it never fails a request.",[594,2075,2076,2077,2080,2081,1767,2084,600],{},"Tokens start with ",[625,2078,2079],{},"fnu_"," unless your app picks its own prefix. A prefix holds letters, digits, ",[625,2082,2083],{},"_",[625,2085,2086],{},"-",[594,2088,2089,2092,2093,2095],{},[1023,2090,2091],{},"What a token may do is frozen when it is created."," ",[625,2094,1526],{}," (session only):",[1486,2097,2098,2108,2122],{},[1489,2099,2100,2101,614,2104,2107],{},"accepts one to 50 scopes, each a permission, a ",[625,2102,2103],{},"resource:*",[625,2105,2106],{},"*"," wildcard, or a named scope;",[1489,2109,2110,2111,2114,2115,2118,2119,2121],{},"answers ",[625,2112,2113],{},"400 unknown_scope"," for a name nobody registered, and ",[625,2116,2117],{},"403 insufficient_permission"," for a scope the user may\nnot give away (",[625,2120,1530],{},");",[1489,2123,2124,2125,2127,2128,2131,2132,2134,2135,2137],{},"stores the ",[625,2126,1985],{}," the scopes expand to, ",[1023,2129,2130],{},"intersected with what the user holds at that moment",". The\nwildcard ",[625,2133,2106],{}," therefore stands for \"everything I may delegate now\", and never covers sensitive or ",[625,2136,1277],{},"\npermissions: choose a sensitive one by name.",[594,2139,2140,2141,2143],{},"On each request the token is checked against that frozen set (step 4 of\n",[598,2142,1304],{"href":1303},"), and then, like a session, against the\nuser's roles as they are now. So a token never does more than its user could when it was created, and loses what the\nuser loses.",[1319,2145,2146],{},"Changing the prefix later invalidates every token already issued. Auth no longer claims them, so they are ignored like\nany unknown bearer token.",[634,2148,2150],{"className":636,"code":2149,"language":638,"meta":639,"style":639},"export default defineNuxtConfig({\n  authentication: {\n    apiTokens: { prefix: 'acme_' }, \u002F\u002F or `false` to turn API tokens off\n  },\n})\n",[625,2151,2152,2164,2173,2199,2203],{"__ignoreMap":639},[643,2153,2154,2156,2158,2160,2162],{"class":645,"line":646},[643,2155,657],{"class":656},[643,2157,660],{"class":656},[643,2159,1793],{"class":663},[643,2161,668],{"class":667},[643,2163,1798],{"class":675},[643,2165,2166,2169,2171],{"class":645,"line":653},[643,2167,2168],{"class":701},"  authentication",[643,2170,1119],{"class":675},[643,2172,689],{"class":675},[643,2174,2175,2178,2180,2182,2185,2187,2189,2192,2194,2196],{"class":645,"line":692},[643,2176,2177],{"class":701},"    apiTokens",[643,2179,1119],{"class":675},[643,2181,731],{"class":675},[643,2183,2184],{"class":701}," prefix",[643,2186,1119],{"class":675},[643,2188,709],{"class":675},[643,2190,2191],{"class":712},"acme_",[643,2193,716],{"class":675},[643,2195,1836],{"class":675},[643,2197,2198],{"class":649}," \u002F\u002F or `false` to turn API tokens off\n",[643,2200,2201],{"class":645,"line":725},[643,2202,1894],{"class":675},[643,2204,2205,2207],{"class":645,"line":769},[643,2206,856],{"class":675},[643,2208,766],{"class":667},[594,2210,2211,2212,1119],{},"With ",[625,2213,2214],{},"apiTokens: false",[1486,2216,2217,2224,2229],{},[1489,2218,2219,2220,2223],{},"the ",[625,2221,2222],{},"\u002Fapi\u002Fauth\u002Fme\u002Ftokens"," routes and the token resolver are not registered,",[1489,2225,2226,2228],{},[598,2227,195],{"href":196}," drops its API tokens page,",[1489,2230,2231],{},"access tokens are not affected.",[594,2233,1036,2234,2237],{},[625,2235,2236],{},"auth_user_tokens"," table stays, so turning tokens back on needs no migration.",[618,2239,2241],{"id":2240},"oauth-access-tokens","OAuth access tokens",[594,2243,2211,2244,2246,2247,2250,2251,2254,2255,2258],{},[598,2245,225],{"href":226},", an ",[598,2248,2249],{"href":87},"OAuth application"," the user signed in\nto calls your API as that user. ",[625,2252,2253],{},"principal.user"," is the user, and ",[625,2256,2257],{},"principal.client.id"," is the application.",[634,2260,2262],{"className":636,"code":2261,"language":638,"meta":639,"style":639},"const principal = await authorize(event, 'tickets:read')\nif (principal.kind === 'oauth') console.log(`called by application ${principal.client.id}`)\n",[625,2263,2264,2292],{"__ignoreMap":639},[643,2265,2266,2269,2272,2275,2277,2279,2281,2283,2285,2288,2290],{"class":645,"line":646},[643,2267,2268],{"class":671},"const",[643,2270,2271],{"class":667}," principal ",[643,2273,2274],{"class":675},"=",[643,2276,743],{"class":656},[643,2278,698],{"class":663},[643,2280,1134],{"class":667},[643,2282,706],{"class":675},[643,2284,709],{"class":675},[643,2286,2287],{"class":712},"tickets:read",[643,2289,716],{"class":675},[643,2291,766],{"class":667},[643,2293,2294,2297,2300,2302,2305,2308,2310,2312,2314,2317,2319,2322,2324,2327,2330,2333,2336,2338,2341,2343,2345,2348],{"class":645,"line":653},[643,2295,2296],{"class":656},"if",[643,2298,2299],{"class":667}," (principal",[643,2301,600],{"class":675},[643,2303,2304],{"class":667},"kind ",[643,2306,2307],{"class":675},"===",[643,2309,709],{"class":675},[643,2311,1217],{"class":712},[643,2313,716],{"class":675},[643,2315,2316],{"class":667},") console",[643,2318,600],{"class":675},[643,2320,2321],{"class":663},"log",[643,2323,668],{"class":667},[643,2325,2326],{"class":675},"`",[643,2328,2329],{"class":712},"called by application ",[643,2331,2332],{"class":675},"${",[643,2334,2335],{"class":667},"principal",[643,2337,600],{"class":675},[643,2339,2340],{"class":667},"client",[643,2342,600],{"class":675},[643,2344,786],{"class":667},[643,2346,2347],{"class":675},"}`",[643,2349,766],{"class":667},[594,2351,2352,2353,2355,2356,2359],{},"The access token carries a frozen ",[625,2354,1985],{}," claim, like an API token's row: the scopes the user granted, expanded\nand intersected with what the user held at that moment. Auth checks that claim and never asks the OAuth server about\nscopes. ",[598,2357,225],{"href":2358},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Fserver#scopes"," explains which scopes exist and who may grant them.\nEvery request also checks the token against live state:",[1486,2361,2362,2365,2368],{},[1489,2363,2364],{},"the application must be active,",[1489,2366,2367],{},"the user must exist,",[1489,2369,2370],{},"the user must still pass the application's role requirement.",[594,2372,2373],{},"So disabling an application or changing a role cuts access immediately.",[1486,2375,2376,2383,2390],{},[1489,2377,2378,2379,2382],{},"ID tokens are not credentials. Only access tokens (",[625,2380,2381],{},"typ: at+jwt",") authenticate.",[1489,2384,2385,2386,2389],{},"The audience is the application, so ",[625,2387,2388],{},"aud"," does not have to match this server.",[1489,2391,2392],{},"Verification checks the signature against the JSON Web Key Set (JWKS), the issuer and the type.",[594,2394,2395,2396,2399,2400,2402,2403,2409],{},"An MCP client that sends ",[625,2397,2398],{},"Authorization: Bearer …"," only needs an API token (",[598,2401,317],{"href":318},").\nClients that need the full OAuth flow also need protected-resource metadata\n(",[598,2404,2408],{"href":2405,"rel":2406},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9728",[2407],"nofollow","RFC 9728",") and dynamic client registration. This module provides neither.",[618,2411,2413],{"id":2412},"customize-the-auth-emails","Customize the auth emails",[594,2415,2416,2417,2420,2421,2423,2424,2427],{},"Auth sends the emailed sign-in link, the address verification and the password reset as ",[625,2418,2419],{},"EmailNotification","s through\n",[598,2422,403],{"href":404},". Configure the delivery driver under the ",[625,2425,2426],{},"notify"," key.",[594,2429,2211,2430,2432,2433,910,2436,1767,2439,2442,2443,600],{},[598,2431,407],{"href":408}," installed, the emails are rendered from the module's Vue\ntemplates: ",[625,2434,2435],{},"auth-magic-link",[625,2437,2438],{},"auth-verify-email",[625,2440,2441],{},"auth-password-reset",". Override one by adding\n",[625,2444,2445],{},"app\u002Femails\u002F\u003Cname>.vue",[594,2447,2448,2449,2452,2453,2456],{},"Without templates, brand the sign-in link and verification emails in the ",[625,2450,2451],{},"auth:email"," hook by replacing the subject and\nbody. Delete ",[625,2454,2455],{},"ctx.notification.template"," there if a template should not render over your body:",[634,2458,2460],{"className":636,"code":2459,"language":638,"meta":639,"style":639},"export default defineNitroPlugin((nitro) => {\n  nitro.hooks.hook('auth:email', (ctx) => {\n    \u002F\u002F ctx.type: 'magic-link' | 'verify-email'; ctx.data: { name, url }\n    ctx.notification.subject = 'Velkommen til Frigear'\n    ctx.notification.body = renderMyTemplate(ctx.data)\n  })\n})\n",[625,2461,2462,2482,2514,2519,2543,2572,2578],{"__ignoreMap":639},[643,2463,2464,2466,2468,2470,2472,2474,2476,2478,2480],{"class":645,"line":646},[643,2465,657],{"class":656},[643,2467,660],{"class":656},[643,2469,1593],{"class":663},[643,2471,668],{"class":667},[643,2473,668],{"class":675},[643,2475,1600],{"class":679},[643,2477,683],{"class":675},[643,2479,686],{"class":671},[643,2481,689],{"class":675},[643,2483,2484,2486,2488,2490,2492,2494,2496,2498,2500,2502,2504,2506,2508,2510,2512],{"class":645,"line":653},[643,2485,1611],{"class":667},[643,2487,600],{"class":675},[643,2489,1616],{"class":667},[643,2491,600],{"class":675},[643,2493,1621],{"class":663},[643,2495,668],{"class":701},[643,2497,716],{"class":675},[643,2499,2451],{"class":712},[643,2501,716],{"class":675},[643,2503,706],{"class":675},[643,2505,676],{"class":675},[643,2507,1636],{"class":679},[643,2509,683],{"class":675},[643,2511,686],{"class":671},[643,2513,689],{"class":675},[643,2515,2516],{"class":645,"line":692},[643,2517,2518],{"class":649},"    \u002F\u002F ctx.type: 'magic-link' | 'verify-email'; ctx.data: { name, url }\n",[643,2520,2521,2524,2526,2529,2531,2534,2536,2538,2541],{"class":645,"line":725},[643,2522,2523],{"class":667},"    ctx",[643,2525,600],{"class":675},[643,2527,2528],{"class":667},"notification",[643,2530,600],{"class":675},[643,2532,2533],{"class":667},"subject",[643,2535,740],{"class":675},[643,2537,709],{"class":675},[643,2539,2540],{"class":712},"Velkommen til Frigear",[643,2542,1220],{"class":675},[643,2544,2545,2547,2549,2551,2553,2556,2558,2561,2563,2565,2567,2570],{"class":645,"line":769},[643,2546,2523],{"class":667},[643,2548,600],{"class":675},[643,2550,2528],{"class":667},[643,2552,600],{"class":675},[643,2554,2555],{"class":667},"body",[643,2557,740],{"class":675},[643,2559,2560],{"class":663}," renderMyTemplate",[643,2562,668],{"class":701},[643,2564,1636],{"class":667},[643,2566,600],{"class":675},[643,2568,2569],{"class":667},"data",[643,2571,766],{"class":701},[643,2573,2574,2576],{"class":645,"line":794},[643,2575,1697],{"class":675},[643,2577,766],{"class":701},[643,2579,2580,2582],{"class":645,"line":833},[643,2581,856],{"class":675},[643,2583,766],{"class":667},[618,2585,1475],{"id":2586},"extend-auth-at-build-time",[594,2588,2589,2590,2592,2593,2596,2597,600],{},"This section is for module authors. A module's ",[625,2591,1467],{}," can import these from ",[625,2594,2595],{},"@frigear-nu\u002Fauth\u002Futils",". They are\ncallable from any module, and everything merges with the host config at ",[625,2598,1483],{},[594,2600,2601,2602,2604,2605,2607],{},"Declare permissions, scopes and roles with ",[625,2603,1456],{}," and register them with ",[625,2606,1470],{},": it derives the\nnames and i18n keys, and gives typed constants to use in routes and tests.",[634,2609,2611],{"className":636,"code":2610,"filename":1463,"language":638,"meta":639,"style":639},"import { defineAccess } from '@frigear-nu\u002Fauth\u002Faccess'\n\nexport const reportsAccess = defineAccess('reports', {\n  permissions: {\n    read: { label: 'Read reports', grantTo: ['user'] }, \u002F\u002F reports:read, labelKey reports.permissions.read\n    pii: { label: 'See personal data in reports' }, \u002F\u002F `:pii` is sensitive by default\n    export: { label: 'Export reports', sessionOnly: true },\n  },\n  scopes: { reports: { label: 'Your reports', permissions: ['reports:read'] } },\n  roles: { analyst: { label: 'Analyst', grant: ['reports:*'], inherit: ['user'] } },\n})\n",[625,2612,2613,2634,2640,2667,2675,2721,2746,2777,2781,2830,2897],{"__ignoreMap":639},[643,2614,2615,2618,2620,2623,2625,2628,2630,2632],{"class":645,"line":646},[643,2616,2617],{"class":656},"import",[643,2619,731],{"class":675},[643,2621,2622],{"class":667}," defineAccess",[643,2624,737],{"class":675},[643,2626,2627],{"class":656}," from",[643,2629,709],{"class":675},[643,2631,1459],{"class":712},[643,2633,1220],{"class":675},[643,2635,2636],{"class":645,"line":653},[643,2637,2639],{"emptyLinePlaceholder":2638},true,"\n",[643,2641,2642,2644,2647,2650,2652,2654,2656,2658,2661,2663,2665],{"class":645,"line":692},[643,2643,657],{"class":656},[643,2645,2646],{"class":671}," const",[643,2648,2649],{"class":667}," reportsAccess ",[643,2651,2274],{"class":675},[643,2653,2622],{"class":663},[643,2655,668],{"class":667},[643,2657,716],{"class":675},[643,2659,2660],{"class":712},"reports",[643,2662,716],{"class":675},[643,2664,706],{"class":675},[643,2666,689],{"class":675},[643,2668,2669,2671,2673],{"class":645,"line":725},[643,2670,1241],{"class":701},[643,2672,1119],{"class":675},[643,2674,689],{"class":675},[643,2676,2677,2680,2682,2684,2687,2689,2691,2694,2696,2698,2701,2703,2706,2708,2710,2712,2715,2718],{"class":645,"line":769},[643,2678,2679],{"class":701},"    read",[643,2681,1119],{"class":675},[643,2683,731],{"class":675},[643,2685,2686],{"class":701}," label",[643,2688,1119],{"class":675},[643,2690,709],{"class":675},[643,2692,2693],{"class":712},"Read reports",[643,2695,716],{"class":675},[643,2697,706],{"class":675},[643,2699,2700],{"class":701}," grantTo",[643,2702,1119],{"class":675},[643,2704,2705],{"class":667}," [",[643,2707,716],{"class":675},[643,2709,1675],{"class":712},[643,2711,716],{"class":675},[643,2713,2714],{"class":667},"] ",[643,2716,2717],{"class":675},"},",[643,2719,2720],{"class":649}," \u002F\u002F reports:read, labelKey reports.permissions.read\n",[643,2722,2723,2726,2728,2730,2732,2734,2736,2739,2741,2743],{"class":645,"line":794},[643,2724,2725],{"class":701},"    pii",[643,2727,1119],{"class":675},[643,2729,731],{"class":675},[643,2731,2686],{"class":701},[643,2733,1119],{"class":675},[643,2735,709],{"class":675},[643,2737,2738],{"class":712},"See personal data in reports",[643,2740,716],{"class":675},[643,2742,1836],{"class":675},[643,2744,2745],{"class":649}," \u002F\u002F `:pii` is sensitive by default\n",[643,2747,2748,2751,2753,2755,2757,2759,2761,2764,2766,2768,2771,2773,2775],{"class":645,"line":833},[643,2749,2750],{"class":701},"    export",[643,2752,1119],{"class":675},[643,2754,731],{"class":675},[643,2756,2686],{"class":701},[643,2758,1119],{"class":675},[643,2760,709],{"class":675},[643,2762,2763],{"class":712},"Export reports",[643,2765,716],{"class":675},[643,2767,706],{"class":675},[643,2769,2770],{"class":701}," sessionOnly",[643,2772,1119],{"class":675},[643,2774,1861],{"class":1860},[643,2776,1889],{"class":675},[643,2778,2779],{"class":645,"line":853},[643,2780,1894],{"class":675},[643,2782,2784,2786,2788,2790,2793,2795,2797,2799,2801,2803,2806,2808,2810,2813,2815,2817,2819,2822,2824,2826,2828],{"class":645,"line":2783},9,[643,2785,1225],{"class":701},[643,2787,1119],{"class":675},[643,2789,731],{"class":675},[643,2791,2792],{"class":701}," reports",[643,2794,1119],{"class":675},[643,2796,731],{"class":675},[643,2798,2686],{"class":701},[643,2800,1119],{"class":675},[643,2802,709],{"class":675},[643,2804,2805],{"class":712},"Your reports",[643,2807,716],{"class":675},[643,2809,706],{"class":675},[643,2811,2812],{"class":701}," permissions",[643,2814,1119],{"class":675},[643,2816,2705],{"class":667},[643,2818,716],{"class":675},[643,2820,2821],{"class":712},"reports:read",[643,2823,716],{"class":675},[643,2825,2714],{"class":667},[643,2827,856],{"class":675},[643,2829,1889],{"class":675},[643,2831,2833,2835,2837,2839,2842,2844,2846,2848,2850,2852,2855,2857,2859,2862,2864,2866,2868,2871,2873,2876,2878,2881,2883,2885,2887,2889,2891,2893,2895],{"class":645,"line":2832},10,[643,2834,1255],{"class":701},[643,2836,1119],{"class":675},[643,2838,731],{"class":675},[643,2840,2841],{"class":701}," analyst",[643,2843,1119],{"class":675},[643,2845,731],{"class":675},[643,2847,2686],{"class":701},[643,2849,1119],{"class":675},[643,2851,709],{"class":675},[643,2853,2854],{"class":712},"Analyst",[643,2856,716],{"class":675},[643,2858,706],{"class":675},[643,2860,2861],{"class":701}," grant",[643,2863,1119],{"class":675},[643,2865,2705],{"class":667},[643,2867,716],{"class":675},[643,2869,2870],{"class":712},"reports:*",[643,2872,716],{"class":675},[643,2874,2875],{"class":667},"]",[643,2877,706],{"class":675},[643,2879,2880],{"class":701}," inherit",[643,2882,1119],{"class":675},[643,2884,2705],{"class":667},[643,2886,716],{"class":675},[643,2888,1675],{"class":712},[643,2890,716],{"class":675},[643,2892,2714],{"class":667},[643,2894,856],{"class":675},[643,2896,1889],{"class":675},[643,2898,2900,2902],{"class":645,"line":2899},11,[643,2901,856],{"class":675},[643,2903,766],{"class":667},[634,2905,2908],{"className":636,"code":2906,"filename":2907,"language":638,"meta":639,"style":639},"registerAccess(nuxt, reportsAccess) \u002F\u002F or { roles: false }, { scopes: [...] }, { permissions: [...] }, { from }\n","src\u002Fmodule.ts",[625,2909,2910],{"__ignoreMap":639},[643,2911,2912,2914,2917,2919,2922],{"class":645,"line":646},[643,2913,1470],{"class":663},[643,2915,2916],{"class":667},"(nuxt",[643,2918,706],{"class":675},[643,2920,2921],{"class":667}," reportsAccess) ",[643,2923,2924],{"class":649},"\u002F\u002F or { roles: false }, { scopes: [...] }, { permissions: [...] }, { from }\n",[594,2926,2927,2928,2930],{},"Under the hood, and for anything ",[625,2929,1456],{}," does not cover (scopes read from options, a fixture's one-off role),\nthe low-level calls:",[634,2932,2934],{"className":636,"code":2933,"language":638,"meta":639,"style":639},"addPermission(nuxt,\n  { name: 'reports:read', label: 'Read reports', group: 'reports', grantTo: ['user'] },\n  { name: 'reports:pii', label: 'See personal data in reports' }, \u002F\u002F `:pii` is sensitive by default\n  { name: 'reports:export', label: 'Export reports', sessionOnly: true },\n  { name: 'tickets:read', implicit: true, aliases: ['tickets:view'] },\n)\n\naddScope(nuxt, { name: 'reports', label: 'Your reports', permissions: ['reports:read'] })\n\naddRole(nuxt, { name: 'analyst', label: 'Analyst', grant: ['reports:*'], inherit: ['user'], from: '@acme\u002Freports' })\n\naddRoleRequirement(nuxt, { name: 'subscription', path: resolver.resolve('.\u002Fruntime\u002Fserver\u002Frequirements\u002Fsubscription') })\n\naddPolicies(nuxt, { path: resolver.resolve('.\u002Fruntime\u002Fserver\u002Fpolicies') })\n\naddAuthResolver(nuxt, {\n  name: 'my-tokens',\n  prefix: 'mt_', \u002F\u002F or `jwt: true`; one of the two is required\n  resolver: resolver.resolve('.\u002Fruntime\u002Fserver\u002Fresolvers\u002Fmy-token'),\n  order: 100, \u002F\u002F lower runs first\n})\n\nsetAuthUserStore(nuxt, resolver.resolve('.\u002Fruntime\u002Fserver\u002Fuser-store')) \u002F\u002F first registration wins\n",[625,2935,2936,2945,3005,3036,3073,3116,3120,3124,3177,3181,3264,3268,3321,3326,3363,3368,3380,3397,3417,3444,3461,3468,3473],{"__ignoreMap":639},[643,2937,2938,2941,2943],{"class":645,"line":646},[643,2939,2940],{"class":663},"addPermission",[643,2942,2916],{"class":667},[643,2944,1544],{"class":675},[643,2946,2947,2950,2953,2955,2957,2959,2961,2963,2965,2967,2969,2971,2973,2975,2978,2980,2982,2984,2986,2988,2990,2992,2994,2996,2998,3000,3002],{"class":645,"line":653},[643,2948,2949],{"class":675},"  {",[643,2951,2952],{"class":701}," name",[643,2954,1119],{"class":675},[643,2956,709],{"class":675},[643,2958,2821],{"class":712},[643,2960,716],{"class":675},[643,2962,706],{"class":675},[643,2964,2686],{"class":701},[643,2966,1119],{"class":675},[643,2968,709],{"class":675},[643,2970,2693],{"class":712},[643,2972,716],{"class":675},[643,2974,706],{"class":675},[643,2976,2977],{"class":701}," group",[643,2979,1119],{"class":675},[643,2981,709],{"class":675},[643,2983,2660],{"class":712},[643,2985,716],{"class":675},[643,2987,706],{"class":675},[643,2989,2700],{"class":701},[643,2991,1119],{"class":675},[643,2993,2705],{"class":667},[643,2995,716],{"class":675},[643,2997,1675],{"class":712},[643,2999,716],{"class":675},[643,3001,2714],{"class":667},[643,3003,3004],{"class":675},"},\n",[643,3006,3007,3009,3011,3013,3015,3018,3020,3022,3024,3026,3028,3030,3032,3034],{"class":645,"line":692},[643,3008,2949],{"class":675},[643,3010,2952],{"class":701},[643,3012,1119],{"class":675},[643,3014,709],{"class":675},[643,3016,3017],{"class":712},"reports:pii",[643,3019,716],{"class":675},[643,3021,706],{"class":675},[643,3023,2686],{"class":701},[643,3025,1119],{"class":675},[643,3027,709],{"class":675},[643,3029,2738],{"class":712},[643,3031,716],{"class":675},[643,3033,1836],{"class":675},[643,3035,2745],{"class":649},[643,3037,3038,3040,3042,3044,3046,3049,3051,3053,3055,3057,3059,3061,3063,3065,3067,3069,3071],{"class":645,"line":725},[643,3039,2949],{"class":675},[643,3041,2952],{"class":701},[643,3043,1119],{"class":675},[643,3045,709],{"class":675},[643,3047,3048],{"class":712},"reports:export",[643,3050,716],{"class":675},[643,3052,706],{"class":675},[643,3054,2686],{"class":701},[643,3056,1119],{"class":675},[643,3058,709],{"class":675},[643,3060,2763],{"class":712},[643,3062,716],{"class":675},[643,3064,706],{"class":675},[643,3066,2770],{"class":701},[643,3068,1119],{"class":675},[643,3070,1861],{"class":1860},[643,3072,1889],{"class":675},[643,3074,3075,3077,3079,3081,3083,3085,3087,3089,3092,3094,3096,3098,3101,3103,3105,3107,3110,3112,3114],{"class":645,"line":769},[643,3076,2949],{"class":675},[643,3078,2952],{"class":701},[643,3080,1119],{"class":675},[643,3082,709],{"class":675},[643,3084,2287],{"class":712},[643,3086,716],{"class":675},[643,3088,706],{"class":675},[643,3090,3091],{"class":701}," implicit",[643,3093,1119],{"class":675},[643,3095,1861],{"class":1860},[643,3097,706],{"class":675},[643,3099,3100],{"class":701}," aliases",[643,3102,1119],{"class":675},[643,3104,2705],{"class":667},[643,3106,716],{"class":675},[643,3108,3109],{"class":712},"tickets:view",[643,3111,716],{"class":675},[643,3113,2714],{"class":667},[643,3115,3004],{"class":675},[643,3117,3118],{"class":645,"line":794},[643,3119,766],{"class":667},[643,3121,3122],{"class":645,"line":833},[643,3123,2639],{"emptyLinePlaceholder":2638},[643,3125,3126,3129,3131,3133,3135,3137,3139,3141,3143,3145,3147,3149,3151,3153,3155,3157,3159,3161,3163,3165,3167,3169,3171,3173,3175],{"class":645,"line":853},[643,3127,3128],{"class":663},"addScope",[643,3130,2916],{"class":667},[643,3132,706],{"class":675},[643,3134,731],{"class":675},[643,3136,2952],{"class":701},[643,3138,1119],{"class":675},[643,3140,709],{"class":675},[643,3142,2660],{"class":712},[643,3144,716],{"class":675},[643,3146,706],{"class":675},[643,3148,2686],{"class":701},[643,3150,1119],{"class":675},[643,3152,709],{"class":675},[643,3154,2805],{"class":712},[643,3156,716],{"class":675},[643,3158,706],{"class":675},[643,3160,2812],{"class":701},[643,3162,1119],{"class":675},[643,3164,2705],{"class":667},[643,3166,716],{"class":675},[643,3168,2821],{"class":712},[643,3170,716],{"class":675},[643,3172,2714],{"class":667},[643,3174,856],{"class":675},[643,3176,766],{"class":667},[643,3178,3179],{"class":645,"line":2783},[643,3180,2639],{"emptyLinePlaceholder":2638},[643,3182,3183,3186,3188,3190,3192,3194,3196,3198,3201,3203,3205,3207,3209,3211,3213,3215,3217,3219,3221,3223,3225,3227,3229,3231,3233,3235,3237,3239,3241,3243,3245,3247,3249,3251,3253,3255,3258,3260,3262],{"class":645,"line":2832},[643,3184,3185],{"class":663},"addRole",[643,3187,2916],{"class":667},[643,3189,706],{"class":675},[643,3191,731],{"class":675},[643,3193,2952],{"class":701},[643,3195,1119],{"class":675},[643,3197,709],{"class":675},[643,3199,3200],{"class":712},"analyst",[643,3202,716],{"class":675},[643,3204,706],{"class":675},[643,3206,2686],{"class":701},[643,3208,1119],{"class":675},[643,3210,709],{"class":675},[643,3212,2854],{"class":712},[643,3214,716],{"class":675},[643,3216,706],{"class":675},[643,3218,2861],{"class":701},[643,3220,1119],{"class":675},[643,3222,2705],{"class":667},[643,3224,716],{"class":675},[643,3226,2870],{"class":712},[643,3228,716],{"class":675},[643,3230,2875],{"class":667},[643,3232,706],{"class":675},[643,3234,2880],{"class":701},[643,3236,1119],{"class":675},[643,3238,2705],{"class":667},[643,3240,716],{"class":675},[643,3242,1675],{"class":712},[643,3244,716],{"class":675},[643,3246,2875],{"class":667},[643,3248,706],{"class":675},[643,3250,2627],{"class":701},[643,3252,1119],{"class":675},[643,3254,709],{"class":675},[643,3256,3257],{"class":712},"@acme\u002Freports",[643,3259,716],{"class":675},[643,3261,737],{"class":675},[643,3263,766],{"class":667},[643,3265,3266],{"class":645,"line":2899},[643,3267,2639],{"emptyLinePlaceholder":2638},[643,3269,3271,3274,3276,3278,3280,3282,3284,3286,3289,3291,3293,3296,3298,3301,3303,3306,3308,3310,3313,3315,3317,3319],{"class":645,"line":3270},12,[643,3272,3273],{"class":663},"addRoleRequirement",[643,3275,2916],{"class":667},[643,3277,706],{"class":675},[643,3279,731],{"class":675},[643,3281,2952],{"class":701},[643,3283,1119],{"class":675},[643,3285,709],{"class":675},[643,3287,3288],{"class":712},"subscription",[643,3290,716],{"class":675},[643,3292,706],{"class":675},[643,3294,3295],{"class":701}," path",[643,3297,1119],{"class":675},[643,3299,3300],{"class":667}," resolver",[643,3302,600],{"class":675},[643,3304,3305],{"class":663},"resolve",[643,3307,668],{"class":667},[643,3309,716],{"class":675},[643,3311,3312],{"class":712},".\u002Fruntime\u002Fserver\u002Frequirements\u002Fsubscription",[643,3314,716],{"class":675},[643,3316,719],{"class":667},[643,3318,856],{"class":675},[643,3320,766],{"class":667},[643,3322,3324],{"class":645,"line":3323},13,[643,3325,2639],{"emptyLinePlaceholder":2638},[643,3327,3329,3332,3334,3336,3338,3340,3342,3344,3346,3348,3350,3352,3355,3357,3359,3361],{"class":645,"line":3328},14,[643,3330,3331],{"class":663},"addPolicies",[643,3333,2916],{"class":667},[643,3335,706],{"class":675},[643,3337,731],{"class":675},[643,3339,3295],{"class":701},[643,3341,1119],{"class":675},[643,3343,3300],{"class":667},[643,3345,600],{"class":675},[643,3347,3305],{"class":663},[643,3349,668],{"class":667},[643,3351,716],{"class":675},[643,3353,3354],{"class":712},".\u002Fruntime\u002Fserver\u002Fpolicies",[643,3356,716],{"class":675},[643,3358,719],{"class":667},[643,3360,856],{"class":675},[643,3362,766],{"class":667},[643,3364,3366],{"class":645,"line":3365},15,[643,3367,2639],{"emptyLinePlaceholder":2638},[643,3369,3371,3374,3376,3378],{"class":645,"line":3370},16,[643,3372,3373],{"class":663},"addAuthResolver",[643,3375,2916],{"class":667},[643,3377,706],{"class":675},[643,3379,689],{"class":675},[643,3381,3383,3386,3388,3390,3393,3395],{"class":645,"line":3382},17,[643,3384,3385],{"class":701},"  name",[643,3387,1119],{"class":675},[643,3389,709],{"class":675},[643,3391,3392],{"class":712},"my-tokens",[643,3394,716],{"class":675},[643,3396,1544],{"class":675},[643,3398,3400,3403,3405,3407,3410,3412,3414],{"class":645,"line":3399},18,[643,3401,3402],{"class":701},"  prefix",[643,3404,1119],{"class":675},[643,3406,709],{"class":675},[643,3408,3409],{"class":712},"mt_",[643,3411,716],{"class":675},[643,3413,706],{"class":675},[643,3415,3416],{"class":649}," \u002F\u002F or `jwt: true`; one of the two is required\n",[643,3418,3420,3423,3425,3427,3429,3431,3433,3435,3438,3440,3442],{"class":645,"line":3419},19,[643,3421,3422],{"class":701},"  resolver",[643,3424,1119],{"class":675},[643,3426,3300],{"class":667},[643,3428,600],{"class":675},[643,3430,3305],{"class":663},[643,3432,668],{"class":667},[643,3434,716],{"class":675},[643,3436,3437],{"class":712},".\u002Fruntime\u002Fserver\u002Fresolvers\u002Fmy-token",[643,3439,716],{"class":675},[643,3441,683],{"class":667},[643,3443,1544],{"class":675},[643,3445,3447,3450,3452,3456,3458],{"class":645,"line":3446},20,[643,3448,3449],{"class":701},"  order",[643,3451,1119],{"class":675},[643,3453,3455],{"class":3454},"sbssI"," 100",[643,3457,706],{"class":675},[643,3459,3460],{"class":649}," \u002F\u002F lower runs first\n",[643,3462,3464,3466],{"class":645,"line":3463},21,[643,3465,856],{"class":675},[643,3467,766],{"class":667},[643,3469,3471],{"class":645,"line":3470},22,[643,3472,2639],{"emptyLinePlaceholder":2638},[643,3474,3476,3479,3481,3483,3485,3487,3489,3491,3493,3496,3498,3501],{"class":645,"line":3475},23,[643,3477,3478],{"class":663},"setAuthUserStore",[643,3480,2916],{"class":667},[643,3482,706],{"class":675},[643,3484,3300],{"class":667},[643,3486,600],{"class":675},[643,3488,3305],{"class":663},[643,3490,668],{"class":667},[643,3492,716],{"class":675},[643,3494,3495],{"class":712},".\u002Fruntime\u002Fserver\u002Fuser-store",[643,3497,716],{"class":675},[643,3499,3500],{"class":667},")) ",[643,3502,3503],{"class":649},"\u002F\u002F first registration wins\n",[878,3505,3506,3515],{},[881,3507,3508],{},[884,3509,3510,3512],{},[887,3511,889],{},[887,3513,3514],{},"What it registers",[894,3516,3517,3538,3555,3575,3589,3603,3613,3626],{},[884,3518,3519,3524],{},[899,3520,3521],{},[625,3522,3523],{},"addPermission(nuxt, ...defs)",[899,3525,3526,3529,3530,910,3533,1767,3535,600],{},[625,3527,3528],{},"{ name, label?, labelKey?, description?, group?, sensitive?, sessionOnly?, implicit?, aliases?, grantTo? }",". The same name from two sources must agree on ",[625,3531,3532],{},"sensitive",[625,3534,1277],{},[625,3536,3537],{},"implicit",[884,3539,3540,3545],{},[899,3541,3542],{},[625,3543,3544],{},"addScope(nuxt, ...defs)",[899,3546,3547,3548,3551,3552,3554],{},"A named scope: ",[625,3549,3550],{},"{ name, label?, description?, icon?, service?, permissions? }",". It works for API tokens and OAuth alike. Never lists sensitive or ",[625,3553,1277],{}," permissions.",[884,3556,3557,3562],{},[899,3558,3559],{},[625,3560,3561],{},"addRole(nuxt, ...roles)",[899,3563,3564,3565,1767,3568,3570,3571,3574],{},"A role the module ships. Two modules defining one role union ",[625,3566,3567],{},"grant",[625,3569,1063],{},"; conflicting ",[625,3572,3573],{},"requirements"," fail the build. The host merges into it.",[884,3576,3577,3582],{},[899,3578,3579],{},[625,3580,3581],{},"addRoleRequirement(nuxt, { name, path })",[899,3583,3584,3585,3588],{},"A requirement for a role: the file's default export is ",[625,3586,3587],{},"(ctx: { event, user, value }) => boolean | Promise\u003Cboolean>",". A throw counts as not met.",[884,3590,3591,3596],{},[899,3592,3593],{},[625,3594,3595],{},"addPolicies(nuxt, { path })",[899,3597,3598,3599,3602],{},"A file of ",[625,3600,3601],{},"definePolicy"," exports.",[884,3604,3605,3610],{},[899,3606,3607],{},[625,3608,3609],{},"setRoleProvider(nuxt, { path })",[899,3611,3612],{},"Where assigned roles are stored; Authorization sets it. First registration wins.",[884,3614,3615,3620],{},[899,3616,3617],{},[625,3618,3619],{},"setDefaultRoles(nuxt, roles)",[899,3621,3622,3623,600],{},"The roles every signed-in user holds; Authorization sets it from ",[625,3624,3625],{},"defaultRoles",[884,3627,3628,3634],{},[899,3629,3630,910,3632],{},[625,3631,3373],{},[625,3633,3478],{},[899,3635,3636],{},"Bearer-token resolvers and the user store, as below.",[1486,3638,3639,3656,3672,3680],{},[1489,3640,3641,3645,3646,614,3648,3651,3652,1324,3654,600],{},[1023,3642,3643],{},[625,3644,3537],{}," is for self-service permissions every signed-in user needs even without Authorization, such as\n",[625,3647,2287],{},[625,3649,3650],{},"profile:write",". The host's baseline role is ",[625,3653,3625],{},[625,3655,3537],{},[1489,3657,3658,3663,3664,3667,3668,3671],{},[1023,3659,3660],{},[625,3661,3662],{},"grantTo"," puts the permission into roles by name (",[625,3665,3666],{},"['admin']","), and roles that inherit them get it too. A role\nthat is not configured is skipped, with an info log; a host role defined with ",[625,3669,3670],{},"replace: true"," drops these grants.",[1489,3673,3674,3679],{},[1023,3675,3676],{},[625,3677,3678],{},"aliases"," are old names that, when held, also hold the new permission. They expand on the held side only.",[1489,3681,3682,3683,3685,3686,3688],{},"The build fails on a grant that names no registered permission, an unknown requirement, an inheritance cycle, an\n",[625,3684,1059],{}," role in ",[625,3687,3625],{},", a conflicting redefinition, and an unknown permission in route rules or page meta.",[594,3690,1036,3691,3693,3694,3696],{},[598,3692,199],{"href":206}," pages explain how roles merge and where the\nprovenance of each grant is shown. ",[598,3695,519],{"href":542}," is the checklist for\na new module.",[594,3698,3699],{},"A resolver turns a token into a principal:",[634,3701,3703],{"className":636,"code":3702,"language":638,"meta":639,"style":639},"\u002F\u002F resolver: return a principal, or null when the token is invalid\nexport default async function (event: H3Event, token: string): Promise\u003CAuthPrincipal | null> { … }\n",[625,3704,3705,3710],{"__ignoreMap":639},[643,3706,3707],{"class":645,"line":646},[643,3708,3709],{"class":649},"\u002F\u002F resolver: return a principal, or null when the token is invalid\n",[643,3711,3712,3714,3716,3719,3722,3724,3726,3728,3731,3733,3736,3738,3740,3743,3746,3749,3752,3754,3757,3760,3762,3765],{"class":645,"line":653},[643,3713,657],{"class":656},[643,3715,660],{"class":656},[643,3717,3718],{"class":671}," async",[643,3720,3721],{"class":671}," function",[643,3723,676],{"class":675},[643,3725,680],{"class":679},[643,3727,1119],{"class":675},[643,3729,3730],{"class":1183}," H3Event",[643,3732,706],{"class":675},[643,3734,3735],{"class":679}," token",[643,3737,1119],{"class":675},[643,3739,1230],{"class":1183},[643,3741,3742],{"class":675},"):",[643,3744,3745],{"class":1183}," Promise",[643,3747,3748],{"class":675},"\u003C",[643,3750,3751],{"class":1183},"AuthPrincipal",[643,3753,1203],{"class":675},[643,3755,3756],{"class":1183}," null",[643,3758,3759],{"class":675},">",[643,3761,731],{"class":675},[643,3763,3764],{"class":701}," … ",[643,3766,1270],{"class":675},[594,3768,3769,3770,3772],{},"The user store is how Auth reads and writes users. ",[598,3771,191],{"href":192}," registers one:",[634,3774,3776],{"className":636,"code":3775,"language":638,"meta":639,"style":639},"interface AuthUserStore {\n  findById(id: string | number): Promise\u003CAuthStoredUser | null>\n  findByEmail(email: string): Promise\u003CAuthStoredUser | null>\n  create(values: AuthUserCreate): Promise\u003CAuthStoredUser>\n  update(id: number, values: AuthUserUpdate): Promise\u003CAuthStoredUser | null>\n  remove(id: number): Promise\u003Cvoid>\n  \u002F** Gives the user a new id, taking everything that references it along. *\u002F\n  changeId(from: number, to: number): Promise\u003Cvoid>\n}\n",[625,3777,3778,3787,3821,3849,3874,3911,3935,3940,3973],{"__ignoreMap":639},[643,3779,3780,3782,3785],{"class":645,"line":646},[643,3781,1180],{"class":671},[643,3783,3784],{"class":1183}," AuthUserStore",[643,3786,689],{"class":675},[643,3788,3789,3792,3794,3796,3798,3800,3802,3805,3807,3809,3811,3814,3816,3818],{"class":645,"line":653},[643,3790,3791],{"class":701},"  findById",[643,3793,668],{"class":675},[643,3795,786],{"class":679},[643,3797,1119],{"class":675},[643,3799,1230],{"class":1183},[643,3801,1203],{"class":675},[643,3803,3804],{"class":1183}," number",[643,3806,3742],{"class":675},[643,3808,3745],{"class":1183},[643,3810,3748],{"class":675},[643,3812,3813],{"class":1183},"AuthStoredUser",[643,3815,1203],{"class":675},[643,3817,3756],{"class":1183},[643,3819,3820],{"class":675},">\n",[643,3822,3823,3826,3828,3831,3833,3835,3837,3839,3841,3843,3845,3847],{"class":645,"line":692},[643,3824,3825],{"class":701},"  findByEmail",[643,3827,668],{"class":675},[643,3829,3830],{"class":679},"email",[643,3832,1119],{"class":675},[643,3834,1230],{"class":1183},[643,3836,3742],{"class":675},[643,3838,3745],{"class":1183},[643,3840,3748],{"class":675},[643,3842,3813],{"class":1183},[643,3844,1203],{"class":675},[643,3846,3756],{"class":1183},[643,3848,3820],{"class":675},[643,3850,3851,3854,3856,3859,3861,3864,3866,3868,3870,3872],{"class":645,"line":725},[643,3852,3853],{"class":701},"  create",[643,3855,668],{"class":675},[643,3857,3858],{"class":679},"values",[643,3860,1119],{"class":675},[643,3862,3863],{"class":1183}," AuthUserCreate",[643,3865,3742],{"class":675},[643,3867,3745],{"class":1183},[643,3869,3748],{"class":675},[643,3871,3813],{"class":1183},[643,3873,3820],{"class":675},[643,3875,3876,3879,3881,3883,3885,3887,3889,3892,3894,3897,3899,3901,3903,3905,3907,3909],{"class":645,"line":769},[643,3877,3878],{"class":701},"  update",[643,3880,668],{"class":675},[643,3882,786],{"class":679},[643,3884,1119],{"class":675},[643,3886,3804],{"class":1183},[643,3888,706],{"class":675},[643,3890,3891],{"class":679}," values",[643,3893,1119],{"class":675},[643,3895,3896],{"class":1183}," AuthUserUpdate",[643,3898,3742],{"class":675},[643,3900,3745],{"class":1183},[643,3902,3748],{"class":675},[643,3904,3813],{"class":1183},[643,3906,1203],{"class":675},[643,3908,3756],{"class":1183},[643,3910,3820],{"class":675},[643,3912,3913,3916,3918,3920,3922,3924,3926,3928,3930,3933],{"class":645,"line":794},[643,3914,3915],{"class":701},"  remove",[643,3917,668],{"class":675},[643,3919,786],{"class":679},[643,3921,1119],{"class":675},[643,3923,3804],{"class":1183},[643,3925,3742],{"class":675},[643,3927,3745],{"class":1183},[643,3929,3748],{"class":675},[643,3931,3932],{"class":1183},"void",[643,3934,3820],{"class":675},[643,3936,3937],{"class":645,"line":833},[643,3938,3939],{"class":649},"  \u002F** Gives the user a new id, taking everything that references it along. *\u002F\n",[643,3941,3942,3945,3947,3950,3952,3954,3956,3959,3961,3963,3965,3967,3969,3971],{"class":645,"line":853},[643,3943,3944],{"class":701},"  changeId",[643,3946,668],{"class":675},[643,3948,3949],{"class":679},"from",[643,3951,1119],{"class":675},[643,3953,3804],{"class":1183},[643,3955,706],{"class":675},[643,3957,3958],{"class":679}," to",[643,3960,1119],{"class":675},[643,3962,3804],{"class":1183},[643,3964,3742],{"class":675},[643,3966,3745],{"class":1183},[643,3968,3748],{"class":675},[643,3970,3932],{"class":1183},[643,3972,3820],{"class":675},[643,3974,3975],{"class":645,"line":2783},[643,3976,1270],{"class":675},[594,3978,3979],{},"The resolver and user store helpers are also Nuxt build hooks:",[878,3981,3982,3992],{},[881,3983,3984],{},[884,3985,3986,3989],{},[887,3987,3988],{},"Hook",[887,3990,3991],{},"Payload",[894,3993,3994,4007],{},[884,3995,3996,4001],{},[899,3997,3998],{},[625,3999,4000],{},"auth:resolvers",[899,4002,4003,4006],{},[625,4004,4005],{},"AuthResolverDefinition[]"," (push to register)",[884,4008,4009,4014],{},[899,4010,4011],{},[625,4012,4013],{},"auth:user-store",[899,4015,4016,4019,4020,683],{},[625,4017,4018],{},"{ path?: string }"," (set ",[625,4021,4022],{},"path",[594,4024,4025,4026,910,4029,910,4032,910,4035,910,4038,1544,4041,1767,4044,4047,4048,910,4051,910,4054,910,4057,4060],{},"The utils also export ",[625,4027,4028],{},"parseScopeString",[625,4030,4031],{},"USER_TOKEN_PREFIX",[625,4033,4034],{},"generateUserToken",[625,4036,4037],{},"hashToken",[625,4039,4040],{},"parseBearer",[625,4042,4043],{},"looksLikeJwt",[625,4045,4046],{},"tokenStatus",", and the pure engine (",[625,4049,4050],{},"decide",[625,4052,4053],{},"matches",[625,4055,4056],{},"holds",[625,4058,4059],{},"compileRoles",") for tests.",[618,4062,4064],{"id":4063},"next-steps","Next steps",[1486,4066,4067,4072,4077],{},[1489,4068,4069,4071],{},[598,4070,482],{"href":483}," – authorize a route of your own, with a record.",[1489,4073,4074,4076],{},[598,4075,121],{"href":122}," – the handler order and error shapes for module routes.",[1489,4078,4079,4081],{},[598,4080,179],{"href":180}," – every route, hook and table of Auth.",[4083,4084,4085],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":639,"searchDepth":653,"depth":653,"links":4087},[4088,4093,4094,4095,4096,4097,4098,4099,4100,4101],{"id":620,"depth":653,"text":482,"children":4089},[4090,4092],{"id":1080,"depth":692,"text":4091},"The Permission type",{"id":1361,"depth":692,"text":1362},{"id":1449,"depth":653,"text":1450},{"id":1557,"depth":653,"text":1558},{"id":1758,"depth":653,"text":1759},{"id":1966,"depth":653,"text":1043},{"id":1997,"depth":653,"text":1998},{"id":2240,"depth":653,"text":2241},{"id":2412,"depth":653,"text":2413},{"id":2586,"depth":653,"text":1475},{"id":4063,"depth":653,"text":4064},"The server helpers that authorize your routes, define permissions, roles and scopes, and handle tokens and auth emails.","md",null,{},{"title":175},{"title":589,"description":4102},"Z2pgnxgR465JNzRbS0e7nzYNCnCSnb7uQXM7eWbPEgM",[4110,4112],{"title":173,"path":169,"stem":170,"description":4111,"children":-1},"Lets people sign in to your app and tells every server route who is calling and what they may do.",{"title":179,"path":180,"stem":181,"description":4113,"children":-1},"Look up Auth's composables, hooks, routes, database tables and translations.",1794503152473]