[{"data":1,"prerenderedAt":3768},["ShallowReactive",2],{"navigation_docs":3,"-modules-identity-users":587,"-modules-identity-users-surround":3763},[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":191,"body":589,"description":3757,"extension":3758,"links":3759,"meta":3760,"navigation":1679,"path":192,"seo":3761,"stem":193,"__hash__":3762},"docs\u002F3.modules\u002F1.identity\u002F2.users.md",{"type":590,"value":591,"toc":3725},"minimark",[592,607,624,629,726,730,800,804,1235,1259,1263,1269,1403,1418,1439,1454,1461,1465,1468,1536,1549,1561,1586,1590,1595,1832,1845,1847,1870,1977,1989,1998,2002,2019,2063,2070,2096,2147,2151,2157,2317,2325,2328,2349,2352,2364,2372,2414,2418,2432,2475,2480,2483,2534,2545,2548,2557,2568,2607,2619,2636,2639,2666,2711,2715,2738,2742,2756,2892,2920,2928,3373,3387,3399,3413,3430,3442,3486,3508,3544,3558,3561,3567,3571,3578,3613,3636,3641,3654,3657,3683,3687,3721],[593,594,595,596,600,601,603,604,606],"p",{},"Users keeps the people who have an account in your app. It lets ",[597,598,599],"a",{"href":87},"staff"," find, add, edit and\ndelete them in the admin area, and lets each signed-in user change their own name, email address and password. Install\nit in any app where people sign in; it installs ",[597,602,168],{"href":169},", which handles the sign-in itself. For\nthe pages where users make those changes, add ",[597,605,195],{"href":196},".",[593,608,609,610,614,615,618,619,623],{},"The package is ",[611,612,613],"code",{},"@frigear-nu\u002Fusers",". It owns the ",[611,616,617],{},"users"," table. Who is staff is decided by ",[597,620,622],{"href":621},"#permissions","permissions"," that\nroles grant, never by the module itself.",[625,626,628],"h2",{"id":627},"what-you-get","What you get",[630,631,632,656,673,690,702,720],"ul",{},[633,634,635,639,640,643,644,646,647,650,651,655],"li",{},[636,637,638],"strong",{},"Admin area"," (opt-in, ",[611,641,642],{},"settings.admin",", needs ",[597,645,217],{"href":351},"): a users list at ",[611,648,649],{},"\u002Fadmin\u002Fusers",", a\npage per user with tabs that other modules can add to, and a ",[652,653,654],"em",{},"New users"," card and stat on the dashboard.",[633,657,658,660,661,664,665,668,669,672],{},[636,659,101],{}," (with ",[611,662,663],{},"settings.verifyEmailChange","): ",[611,666,667],{},"\u002Fauth\u002Fverify-email-change"," and ",[611,670,671],{},"\u002Fauth\u002Frevert-email-change",", which the\nemail change links open.",[633,674,675,678,679,682,683,686,687,689],{},[636,676,677],{},"Server routes",": self-service routes under ",[611,680,681],{},"\u002Fapi\u002Fusers"," (on by default) and staff routes under ",[611,684,685],{},"\u002Fapi\u002Fadmin\u002Fusers","\n(opt-in, ",[611,688,642],{},").",[633,691,692,694,695,668,698,701],{},[636,693,344],{},": ",[611,696,697],{},"requireUser",[611,699,700],{},"getUser"," for your server routes, and helpers for staff routes of your\nown (on by default).",[633,703,704,694,707,668,710,713,714,717,718,689],{},[636,705,706],{},"Permissions",[611,708,709],{},"profile:read",[611,711,712],{},"profile:write"," for every signed-in user, and ",[611,715,716],{},"users:*"," for staff (with\n",[611,719,642],{},[633,721,722,725],{},[636,723,724],{},"Emails",": a confirmation link for a new address, a revert link to the old one, and a password change notice (on with\nthe matching feature).",[727,728],"module-runtime",{":client":729,":server":729},"true",[731,732,733,755,763],"note",{},[593,734,735,738,739,742,743,748,749,754],{},[636,736,737],{},"Installed for you:"," ",[611,740,741],{},"@frigear-nu\u002Fmodule-kit",", ",[597,744,745],{"href":169},[611,746,747],{},"@frigear-nu\u002Fauth"," and\n",[597,750,751],{"href":404},[611,752,753],{},"@frigear-nu\u002Fnotify"," (email and password change notices).",[593,756,757,738,760,606],{},[636,758,759],{},"You add:",[611,761,762],{},"@nuxthub\u002Fcore",[593,764,765,738,768,773,774,779,780,785,786,791,792,660,797,689],{},[636,766,767],{},"Optional:",[597,769,770],{"href":200},[611,771,772],{},"@frigear-nu\u002Fauthorization"," (roles, which give staff their\npermissions), ",[597,775,776],{"href":351},[611,777,778],{},"@frigear-nu\u002Fadmin"," (the admin pages),\n",[597,781,782],{"href":355},[611,783,784],{},"@frigear-nu\u002Fmenu"," (the admin menu item),\n",[597,787,788],{"href":359},[611,789,790],{},"@frigear-nu\u002Fwidget"," (tabs and the dashboard widget) and\n",[597,793,794],{"href":318},[611,795,796],{},"@frigear-nu\u002Fmcp",[611,798,799],{},"settings.mcp",[625,801,803],{"id":802},"quick-setup","Quick setup",[805,806,807,812,840,844,854,1085,1089,1100,1106,1118,1124,1128,1134,1156],"steps",{},[808,809,811],"h3",{"id":810},"install-users","Install Users",[813,814,819],"pre",{"className":815,"code":816,"language":817,"meta":818,"style":818},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @frigear-nu\u002Fusers @nuxthub\u002Fcore\n","bash","",[611,820,821],{"__ignoreMap":818},[822,823,826,830,834,837],"span",{"class":824,"line":825},"line",1,[822,827,829],{"class":828},"sBMFI","pnpm",[822,831,833],{"class":832},"sfazB"," add",[822,835,836],{"class":832}," @frigear-nu\u002Fusers",[822,838,839],{"class":832}," @nuxthub\u002Fcore\n",[808,841,843],{"id":842},"add-it-to-nuxtconfigts","Add it to nuxt.config.ts",[593,845,846,847,850,851,853],{},"This turns on user management for staff and confirmed email changes. The ",[611,848,849],{},"admin"," role is given every permission,\nincluding the ",[611,852,716],{}," ones:",[813,855,860],{"className":856,"code":857,"filename":858,"language":859,"meta":818,"style":818},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export default defineNuxtConfig({\n  modules: ['@nuxthub\u002Fcore', '@frigear-nu\u002Fauthorization', '@frigear-nu\u002Fusers'],\n  authorization: {\n    roles: {\n      admin: { label: 'Admin', grant: ['*'] },\n    },\n  },\n  users: {\n    settings: {\n      admin: true,\n      verifyEmailChange: true,\n      profilePath: '\u002Faccount\u002Fprofile',\n    },\n  },\n})\n","nuxt.config.ts","ts",[611,861,862,883,927,938,948,992,998,1004,1014,1024,1037,1049,1066,1071,1076],{"__ignoreMap":818},[822,863,864,868,871,875,879],{"class":824,"line":825},[822,865,867],{"class":866},"s7zQu","export",[822,869,870],{"class":866}," default",[822,872,874],{"class":873},"s2Zo4"," defineNuxtConfig",[822,876,878],{"class":877},"sTEyZ","(",[822,880,882],{"class":881},"sMK4o","{\n",[822,884,886,890,893,896,899,901,903,906,909,911,913,915,917,919,921,924],{"class":824,"line":885},2,[822,887,889],{"class":888},"swJcz","  modules",[822,891,892],{"class":881},":",[822,894,895],{"class":877}," [",[822,897,898],{"class":881},"'",[822,900,762],{"class":832},[822,902,898],{"class":881},[822,904,905],{"class":881},",",[822,907,908],{"class":881}," '",[822,910,772],{"class":832},[822,912,898],{"class":881},[822,914,905],{"class":881},[822,916,908],{"class":881},[822,918,613],{"class":832},[822,920,898],{"class":881},[822,922,923],{"class":877},"]",[822,925,926],{"class":881},",\n",[822,928,930,933,935],{"class":824,"line":929},3,[822,931,932],{"class":888},"  authorization",[822,934,892],{"class":881},[822,936,937],{"class":881}," {\n",[822,939,941,944,946],{"class":824,"line":940},4,[822,942,943],{"class":888},"    roles",[822,945,892],{"class":881},[822,947,937],{"class":881},[822,949,951,954,956,959,962,964,966,968,970,972,975,977,979,981,984,986,989],{"class":824,"line":950},5,[822,952,953],{"class":888},"      admin",[822,955,892],{"class":881},[822,957,958],{"class":881}," {",[822,960,961],{"class":888}," label",[822,963,892],{"class":881},[822,965,908],{"class":881},[822,967,217],{"class":832},[822,969,898],{"class":881},[822,971,905],{"class":881},[822,973,974],{"class":888}," grant",[822,976,892],{"class":881},[822,978,895],{"class":877},[822,980,898],{"class":881},[822,982,983],{"class":832},"*",[822,985,898],{"class":881},[822,987,988],{"class":877},"] ",[822,990,991],{"class":881},"},\n",[822,993,995],{"class":824,"line":994},6,[822,996,997],{"class":881},"    },\n",[822,999,1001],{"class":824,"line":1000},7,[822,1002,1003],{"class":881},"  },\n",[822,1005,1007,1010,1012],{"class":824,"line":1006},8,[822,1008,1009],{"class":888},"  users",[822,1011,892],{"class":881},[822,1013,937],{"class":881},[822,1015,1017,1020,1022],{"class":824,"line":1016},9,[822,1018,1019],{"class":888},"    settings",[822,1021,892],{"class":881},[822,1023,937],{"class":881},[822,1025,1027,1029,1031,1035],{"class":824,"line":1026},10,[822,1028,953],{"class":888},[822,1030,892],{"class":881},[822,1032,1034],{"class":1033},"sfNiH"," true",[822,1036,926],{"class":881},[822,1038,1040,1043,1045,1047],{"class":824,"line":1039},11,[822,1041,1042],{"class":888},"      verifyEmailChange",[822,1044,892],{"class":881},[822,1046,1034],{"class":1033},[822,1048,926],{"class":881},[822,1050,1052,1055,1057,1059,1062,1064],{"class":824,"line":1051},12,[822,1053,1054],{"class":888},"      profilePath",[822,1056,892],{"class":881},[822,1058,908],{"class":881},[822,1060,1061],{"class":832},"\u002Faccount\u002Fprofile",[822,1063,898],{"class":881},[822,1065,926],{"class":881},[822,1067,1069],{"class":824,"line":1068},13,[822,1070,997],{"class":881},[822,1072,1074],{"class":824,"line":1073},14,[822,1075,1003],{"class":881},[822,1077,1079,1082],{"class":824,"line":1078},15,[822,1080,1081],{"class":881},"}",[822,1083,1084],{"class":877},")\n",[808,1086,1088],{"id":1087},"generate-the-migration","Generate the migration",[1090,1091,1092,1093,1096,1097,1099],"warning",{},"Without a ",[597,1094,1095],{"href":87},"migration",", the database has no ",[611,1098,617],{}," table and nobody can sign up.",[593,1101,1102,1103,1105],{},"Users registers the ",[611,1104,617],{}," table, and Auth and other modules add columns to it. Generate a migration and commit it:",[813,1107,1109],{"className":815,"code":1108,"language":817,"meta":818,"style":818},"pnpm db:generate\n",[611,1110,1111],{"__ignoreMap":818},[822,1112,1113,1115],{"class":824,"line":825},[822,1114,829],{"class":828},[822,1116,1117],{"class":832}," db:generate\n",[593,1119,1120,1121,606],{},"See ",[597,1122,41],{"href":1123},"\u002Fget-started\u002Fdatabase#migrations",[808,1125,1127],{"id":1126},"give-someone-the-admin-role","Give someone the admin role",[1090,1129,1130,1131,1133],{},"Without a role that holds the ",[611,1132,716],{}," permissions, nobody can manage users. Every staff request is denied until a role\ngrants it.",[593,1135,1136,1137,1140,1141,1144,1145,1147,1148,1151,1152,1155],{},"Staff access is a ",[597,1138,1139],{"href":621},"permission"," granted through a ",[597,1142,1143],{"href":206},"role",".\nAssign the ",[611,1146,849],{}," role to your first user in the ",[597,1149,1150],{"href":218},"Authorization admin",", or give\nit by email through ",[611,1153,1154],{},"authorization:roles",". To let a smaller role in, grant it only what it needs:",[813,1157,1159],{"className":856,"code":1158,"filename":858,"language":859,"meta":818,"style":818},"authorization: {\n  roles: {\n    coordinator: { label: 'Coordinator', grant: ['users:read', 'users:list'] },\n  },\n},\n",[611,1160,1161,1170,1179,1227,1231],{"__ignoreMap":818},[822,1162,1163,1166,1168],{"class":824,"line":825},[822,1164,1165],{"class":828},"authorization",[822,1167,892],{"class":881},[822,1169,937],{"class":881},[822,1171,1172,1175,1177],{"class":824,"line":885},[822,1173,1174],{"class":828},"  roles",[822,1176,892],{"class":881},[822,1178,937],{"class":881},[822,1180,1181,1184,1186,1188,1190,1192,1194,1197,1199,1201,1203,1205,1207,1209,1212,1214,1216,1218,1221,1223,1225],{"class":824,"line":929},[822,1182,1183],{"class":828},"    coordinator",[822,1185,892],{"class":881},[822,1187,958],{"class":881},[822,1189,961],{"class":828},[822,1191,892],{"class":881},[822,1193,908],{"class":881},[822,1195,1196],{"class":832},"Coordinator",[822,1198,898],{"class":881},[822,1200,905],{"class":881},[822,1202,974],{"class":828},[822,1204,892],{"class":881},[822,1206,895],{"class":888},[822,1208,898],{"class":881},[822,1210,1211],{"class":832},"users:read",[822,1213,898],{"class":881},[822,1215,905],{"class":881},[822,1217,908],{"class":881},[822,1219,1220],{"class":832},"users:list",[822,1222,898],{"class":881},[822,1224,988],{"class":888},[822,1226,991],{"class":881},[822,1228,1229],{"class":824,"line":940},[822,1230,1003],{"class":881},[822,1232,1233],{"class":824,"line":950},[822,1234,991],{"class":881},[1236,1237,1240,1243,1244,742,1246,1249,1250,1253,1254,1256,1257,606],"callout",{"color":1238,"icon":1239},"success","i-lucide-check",[636,1241,1242],{},"What you should see:"," signed in as a user without ",[611,1245,1220],{},[611,1247,1248],{},"GET \u002Fapi\u002Fadmin\u002Fusers"," answers ",[611,1251,1252],{},"403",". With\n",[597,1255,217],{"href":351}," installed, an admin sees the users list at ",[611,1258,649],{},[625,1260,1262],{"id":1261},"options","Options",[593,1264,1265,1266,1268],{},"Options go under the ",[611,1267,617],{}," key.",[1270,1271,1272,1291],"table",{},[1273,1274,1275],"thead",{},[1276,1277,1278,1282,1285,1288],"tr",{},[1279,1280,1281],"th",{},"Option",[1279,1283,1284],{},"Type",[1279,1286,1287],{},"Default",[1279,1289,1290],{},"Purpose",[1292,1293,1294,1315,1338,1356,1377],"tbody",{},[1276,1295,1296,1301,1306,1309],{},[1297,1298,1299],"td",{},[611,1300,642],{},[1297,1302,1303],{},[611,1304,1305],{},"boolean | { page?: string | boolean }",[1297,1307,1308],{},"off",[1297,1310,1311,1312,1314],{},"User management for staff: API, an admin page and a ",[652,1313,654],{}," widget.",[1276,1316,1317,1322,1327,1331],{},[1297,1318,1319],{},[611,1320,1321],{},"settings.admin.page",[1297,1323,1324],{},[611,1325,1326],{},"string | boolean",[1297,1328,1329],{},[611,1330,729],{},[1297,1332,1333,1334,1337],{},"The admin page path. ",[611,1335,1336],{},"false"," registers the API only.",[1276,1339,1340,1344,1349,1353],{},[1297,1341,1342],{},[611,1343,663],{},[1297,1345,1346],{},[611,1347,1348],{},"boolean",[1297,1350,1351],{},[611,1352,1336],{},[1297,1354,1355],{},"A new email address takes effect only after its owner confirms it.",[1276,1357,1358,1363,1368,1371],{},[1297,1359,1360],{},[611,1361,1362],{},"settings.profilePath",[1297,1364,1365],{},[611,1366,1367],{},"string",[1297,1369,1370],{},"–",[1297,1372,1373,1374,689],{},"Where a confirmed email change lands (",[611,1375,1376],{},"?email-changed=1",[1276,1378,1379,1383,1387,1391],{},[1297,1380,1381],{},[611,1382,799],{},[1297,1384,1385],{},[611,1386,1348],{},[1297,1388,1389],{},[611,1390,1336],{},[1297,1392,1393,1394,1397,1398,1400,1401,606],{},"Expose the ",[611,1395,1396],{},"get-my-profile"," MCP tool. Installs ",[611,1399,796],{},". See ",[597,1402,317],{"href":318},[593,1404,1405,1409,1410,738,1412,1414,1415,1417],{},[636,1406,1407,606],{},[611,1408,642],{}," Who may do what is decided by the ",[611,1411,716],{},[597,1413,622],{"href":621},", which no role holds by\ndefault. The page needs ",[611,1416,778],{},". Without it, only the API is registered, with a warning.",[593,1419,1420,738,1424,1426,1427,1429,1430,1432,1433,1436,1437,606],{},[636,1421,1422,606],{},[611,1423,1321],{},[611,1425,729],{}," is the ",[611,1428,849],{}," route of ",[597,1431,217],{"href":351}," plus ",[611,1434,1435],{},"\u002Fusers",", for example\n",[611,1438,649],{},[593,1440,1441,1445,1446,1449,1450,606],{},[636,1442,1443,606],{},[611,1444,663],{}," The emailed link opens a page where the signed-in owner presses ",[636,1447,1448],{},"Confirm",". The old\naddress then gets a notice with a link to undo the change. Off, the change applies immediately. See\n",[597,1451,1453],{"href":1452},"#email-change","Email change",[593,1455,1456,1460],{},[636,1457,1458,606],{},[611,1459,1362],{}," Without it, the confirm page shows the result itself.",[625,1462,1464],{"id":1463},"server","Server",[593,1466,1467],{},"Helpers for your own server routes. They are auto-imported:",[813,1469,1471],{"className":856,"code":1470,"language":859,"meta":818,"style":818},"\u002F\u002F The signed-in user, or 401. `sessionOnly` also refuses bearer credentials (403 `session_required`).\nconst user = await requireUser(event, { sessionOnly: true })\nconst maybeUser = await getUser(event) \u002F\u002F the user or null; never throws\n",[611,1472,1473,1479,1516],{"__ignoreMap":818},[822,1474,1475],{"class":824,"line":825},[822,1476,1478],{"class":1477},"sHwdD","\u002F\u002F The signed-in user, or 401. `sessionOnly` also refuses bearer credentials (403 `session_required`).\n",[822,1480,1481,1485,1488,1491,1494,1497,1500,1502,1504,1507,1509,1511,1514],{"class":824,"line":885},[822,1482,1484],{"class":1483},"spNyl","const",[822,1486,1487],{"class":877}," user ",[822,1489,1490],{"class":881},"=",[822,1492,1493],{"class":866}," await",[822,1495,1496],{"class":873}," requireUser",[822,1498,1499],{"class":877},"(event",[822,1501,905],{"class":881},[822,1503,958],{"class":881},[822,1505,1506],{"class":888}," sessionOnly",[822,1508,892],{"class":881},[822,1510,1034],{"class":1033},[822,1512,1513],{"class":881}," }",[822,1515,1084],{"class":877},[822,1517,1518,1520,1523,1525,1527,1530,1533],{"class":824,"line":929},[822,1519,1484],{"class":1483},[822,1521,1522],{"class":877}," maybeUser ",[822,1524,1490],{"class":881},[822,1526,1493],{"class":866},[822,1528,1529],{"class":873}," getUser",[822,1531,1532],{"class":877},"(event) ",[822,1534,1535],{"class":1477},"\u002F\u002F the user or null; never throws\n",[593,1537,1538,1539,1545,1546,606],{},"They say who is calling and nothing more. To decide what the caller may do, call\n",[597,1540,1542],{"href":1541},"\u002Fmodules\u002Fidentity\u002Fauth\u002Fserver#protect-a-route",[611,1543,1544],{},"authorize(event, permission)",", which also returns the\n",[597,1547,1548],{"href":87},"principal",[1550,1551,1552,1553,1556,1557,606],"caution",{},"Anything that changes who the user is or how they sign in must pass ",[611,1554,1555],{},"{ sessionOnly: true }",". See\n",[597,1558,1560],{"href":1559},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#sessiononly","sessionOnly",[593,1562,1563,1564,694,1567,742,1570,742,1573,742,1576,926,1579,742,1582,1585],{},"The module registers the user store that Auth uses (",[611,1565,1566],{},"AuthUserStore",[611,1568,1569],{},"findById",[611,1571,1572],{},"findByEmail",[611,1574,1575],{},"create",[611,1577,1578],{},"update",[611,1580,1581],{},"remove",[611,1583,1584],{},"changeId","). It reads the database on every call, so a role change applies to the next request.",[808,1587,1589],{"id":1588},"admin-helpers","Admin helpers",[593,1591,1592,1593,892],{},"For staff routes of your own below ",[611,1594,685],{},[813,1596,1598],{"className":856,"code":1597,"language":859,"meta":818,"style":818},"\u002F\u002F Checks the permission of an action (see the table below). Throws 401, or 403 `session_required`,\n\u002F\u002F `insufficient_scope` or `forbidden`. Returns the signed-in user.\nawait authorizeUsersAdmin(event, 'view' | 'list' | 'create' | 'update' | 'delete' | 'manage')\n\n\u002F\u002F Authorizes `action`, then loads `\u002Fapi\u002Fadmin\u002Fusers\u002F:id\u002F...` and checks the permission on that user.\n\u002F\u002F Throws 401 \u002F 403 before it looks anything up, then 400 \u002F 404, then 403 for a policy or target protection.\n\u002F\u002F Returns { actor, principal, target }.\nconst { actor, target } = await loadUsersAdminTarget(event, 'view' | 'update' | 'delete' | 'manage')\n\n\u002F\u002F A route another module adds below `:id\u002F` checks its own permission. Target protection still applies to writes.\nawait loadUsersAdminTarget(event, 'update', { permission: 'subscriptions:manage' })\n\n\u002F\u002F Whether the caller may do a write to this user: the permission and target protection. For the `can` flags of a response.\nawait canUsersAdminTarget(event, 'update', target)\n",[611,1599,1600,1605,1610,1675,1681,1686,1691,1696,1756,1760,1765,1801,1805,1810],{"__ignoreMap":818},[822,1601,1602],{"class":824,"line":825},[822,1603,1604],{"class":1477},"\u002F\u002F Checks the permission of an action (see the table below). Throws 401, or 403 `session_required`,\n",[822,1606,1607],{"class":824,"line":885},[822,1608,1609],{"class":1477},"\u002F\u002F `insufficient_scope` or `forbidden`. Returns the signed-in user.\n",[822,1611,1612,1615,1618,1620,1622,1624,1627,1629,1632,1634,1637,1639,1641,1643,1645,1647,1649,1651,1653,1655,1657,1659,1662,1664,1666,1668,1671,1673],{"class":824,"line":929},[822,1613,1614],{"class":866},"await",[822,1616,1617],{"class":873}," authorizeUsersAdmin",[822,1619,1499],{"class":877},[822,1621,905],{"class":881},[822,1623,908],{"class":881},[822,1625,1626],{"class":832},"view",[822,1628,898],{"class":881},[822,1630,1631],{"class":881}," |",[822,1633,908],{"class":881},[822,1635,1636],{"class":832},"list",[822,1638,898],{"class":881},[822,1640,1631],{"class":881},[822,1642,908],{"class":881},[822,1644,1575],{"class":832},[822,1646,898],{"class":881},[822,1648,1631],{"class":881},[822,1650,908],{"class":881},[822,1652,1578],{"class":832},[822,1654,898],{"class":881},[822,1656,1631],{"class":881},[822,1658,908],{"class":881},[822,1660,1661],{"class":832},"delete",[822,1663,898],{"class":881},[822,1665,1631],{"class":881},[822,1667,908],{"class":881},[822,1669,1670],{"class":832},"manage",[822,1672,898],{"class":881},[822,1674,1084],{"class":877},[822,1676,1677],{"class":824,"line":940},[822,1678,1680],{"emptyLinePlaceholder":1679},true,"\n",[822,1682,1683],{"class":824,"line":950},[822,1684,1685],{"class":1477},"\u002F\u002F Authorizes `action`, then loads `\u002Fapi\u002Fadmin\u002Fusers\u002F:id\u002F...` and checks the permission on that user.\n",[822,1687,1688],{"class":824,"line":994},[822,1689,1690],{"class":1477},"\u002F\u002F Throws 401 \u002F 403 before it looks anything up, then 400 \u002F 404, then 403 for a policy or target protection.\n",[822,1692,1693],{"class":824,"line":1000},[822,1694,1695],{"class":1477},"\u002F\u002F Returns { actor, principal, target }.\n",[822,1697,1698,1700,1702,1705,1707,1710,1712,1715,1717,1720,1722,1724,1726,1728,1730,1732,1734,1736,1738,1740,1742,1744,1746,1748,1750,1752,1754],{"class":824,"line":1006},[822,1699,1484],{"class":1483},[822,1701,958],{"class":881},[822,1703,1704],{"class":877}," actor",[822,1706,905],{"class":881},[822,1708,1709],{"class":877}," target ",[822,1711,1081],{"class":881},[822,1713,1714],{"class":881}," =",[822,1716,1493],{"class":866},[822,1718,1719],{"class":873}," loadUsersAdminTarget",[822,1721,1499],{"class":877},[822,1723,905],{"class":881},[822,1725,908],{"class":881},[822,1727,1626],{"class":832},[822,1729,898],{"class":881},[822,1731,1631],{"class":881},[822,1733,908],{"class":881},[822,1735,1578],{"class":832},[822,1737,898],{"class":881},[822,1739,1631],{"class":881},[822,1741,908],{"class":881},[822,1743,1661],{"class":832},[822,1745,898],{"class":881},[822,1747,1631],{"class":881},[822,1749,908],{"class":881},[822,1751,1670],{"class":832},[822,1753,898],{"class":881},[822,1755,1084],{"class":877},[822,1757,1758],{"class":824,"line":1016},[822,1759,1680],{"emptyLinePlaceholder":1679},[822,1761,1762],{"class":824,"line":1026},[822,1763,1764],{"class":1477},"\u002F\u002F A route another module adds below `:id\u002F` checks its own permission. Target protection still applies to writes.\n",[822,1766,1767,1769,1771,1773,1775,1777,1779,1781,1783,1785,1788,1790,1792,1795,1797,1799],{"class":824,"line":1039},[822,1768,1614],{"class":866},[822,1770,1719],{"class":873},[822,1772,1499],{"class":877},[822,1774,905],{"class":881},[822,1776,908],{"class":881},[822,1778,1578],{"class":832},[822,1780,898],{"class":881},[822,1782,905],{"class":881},[822,1784,958],{"class":881},[822,1786,1787],{"class":888}," permission",[822,1789,892],{"class":881},[822,1791,908],{"class":881},[822,1793,1794],{"class":832},"subscriptions:manage",[822,1796,898],{"class":881},[822,1798,1513],{"class":881},[822,1800,1084],{"class":877},[822,1802,1803],{"class":824,"line":1051},[822,1804,1680],{"emptyLinePlaceholder":1679},[822,1806,1807],{"class":824,"line":1068},[822,1808,1809],{"class":1477},"\u002F\u002F Whether the caller may do a write to this user: the permission and target protection. For the `can` flags of a response.\n",[822,1811,1812,1814,1817,1819,1821,1823,1825,1827,1829],{"class":824,"line":1073},[822,1813,1614],{"class":866},[822,1815,1816],{"class":873}," canUsersAdminTarget",[822,1818,1499],{"class":877},[822,1820,905],{"class":881},[822,1822,908],{"class":881},[822,1824,1578],{"class":832},[822,1826,898],{"class":881},[822,1828,905],{"class":881},[822,1830,1831],{"class":877}," target)\n",[593,1833,1834,1837,1838,668,1841,1844],{},[611,1835,1836],{},"USERS_PERMISSIONS"," maps each action to its permission, and ",[611,1839,1840],{},"USERS_READ_PERMISSION",[611,1842,1843],{},"USERS_MANAGE_PERMISSION"," are\nthe two a module most often needs.",[808,1846,706],{"id":622},[593,1848,1849,1850,1852,1853,1855,1856,694,1859,1862,1863,1866,1867,1869],{},"With ",[611,1851,642],{},", the module registers six ",[597,1854,622],{"href":200}," for staff. No role holds\nthem by default. Grant them through roles in your ",[611,1857,1858],{},"authorization.roles",[611,1860,1861],{},"admin: ['*']"," covers all six, because none is\n",[611,1864,1865],{},"sensitive",". ",[597,1868,205],{"href":206}," shows how.",[1270,1871,1872,1889],{},[1273,1873,1874],{},[1276,1875,1876,1879,1882,1886],{},[1279,1877,1878],{},"Permission",[1279,1880,1881],{},"Lets the role",[1279,1883,1884],{},[611,1885,1560],{},[1279,1887,1888],{},"Credentials",[1292,1890,1891,1906,1919,1933,1947,1963],{},[1276,1892,1893,1897,1900,1903],{},[1297,1894,1895],{},[611,1896,1211],{},[1297,1898,1899],{},"See one user: profile, sign-in methods and what other modules show on the page.",[1297,1901,1902],{},"no",[1297,1904,1905],{},"Sessions, and tokens that hold it.",[1276,1907,1908,1912,1915,1917],{},[1297,1909,1910],{},[611,1911,1220],{},[1297,1913,1914],{},"Search the user directory and see the sign-up numbers.",[1297,1916,1902],{},[1297,1918,1905],{},[1276,1920,1921,1926,1929,1931],{},[1297,1922,1923],{},[611,1924,1925],{},"users:create",[1297,1927,1928],{},"Create users and email them a sign-in link.",[1297,1930,1902],{},[1297,1932,1905],{},[1276,1934,1935,1940,1943,1945],{},[1297,1936,1937],{},[611,1938,1939],{},"users:update",[1297,1941,1942],{},"Change a user's name.",[1297,1944,1902],{},[1297,1946,1905],{},[1276,1948,1949,1954,1957,1960],{},[1297,1950,1951],{},[611,1952,1953],{},"users:delete",[1297,1955,1956],{},"Delete users and their sign-in methods.",[1297,1958,1959],{},"yes",[1297,1961,1962],{},"Sessions only.",[1276,1964,1965,1970,1973,1975],{},[1297,1966,1967],{},[611,1968,1969],{},"users:manage",[1297,1971,1972],{},"Change or verify a user's email address, and email them a sign-in link.",[1297,1974,1959],{},[1297,1976,1962],{},[593,1978,1979,1980,668,1982,1984,1985,1988],{},"Every signed-in user also holds ",[611,1981,709],{},[611,1983,712],{}," (read and change their own name, email and avatar). These\nare ",[611,1986,1987],{},"implicit",", so they work even without Authorization.",[593,1990,1991,1992,1994,1995,1997],{},"A ",[611,1993,1560],{}," permission is never held by a token, so staff cannot give one to an API token or an OAuth application.\nAnything that touches how someone signs in is behind ",[611,1996,1969],{},": changing a user's email, marking it verified, creating\na user as verified, and resending a sign-in link.",[808,1999,2001],{"id":2000},"target-protection","Target protection",[593,2003,2004,2005,2007,2008,742,2010,668,2012,2014,2015,2018],{},"A staff member can only change a user who holds no permission they lack. So a coordinator who holds ",[611,2006,1939],{},"\ncannot edit or delete an admin, even though they may edit other users. The rule applies to every write on another user\n(",[611,2009,1578],{},[611,2011,1661],{},[611,2013,1670],{},", and routes other modules add with ",[611,2016,2017],{},"loadUsersAdminTarget","). It does not apply to reads or to\nthe staff member's own account.",[630,2020,2021,2034,2044,2057],{},[633,2022,2023,2026,2027,2030,2031,2033],{},[636,2024,2025],{},"The target's permissions count in full."," Their roles, default roles, ",[611,2028,2029],{},"auto"," roles and ",[611,2032,1154],{}," roles, plus\nthe implicit permissions. A sensitive permission counts by name.",[633,2035,2036,2039,2040,2043],{},[636,2037,2038],{},"The actor holds a permission"," through a role or because ",[611,2041,2042],{},"authorization:before"," allows it.",[633,2045,2046,2052,2053,2056],{},[636,2047,2048,2049,606],{},"A refusal is ",[611,2050,2051],{},"403 forbidden"," Its ",[611,2054,2055],{},"data.missing"," lists the permissions the actor lacks.",[633,2058,2059,2062],{},[636,2060,2061],{},"No hook turns it off."," It is not a policy. It stays in force whatever your policies and hooks do.",[808,2064,2066,2067,2069],{"id":2065},"reading-users-with-a-token-usersread","Reading users with a token (",[611,2068,1211],{},")",[593,2071,1849,2072,2074,2075,2078,2079,2082,2083,2085,2086,2089,2090,2092,2093,2095],{},[611,2073,642],{},", another app can read your user directory, for example to sync users. An\n",[597,2076,2077],{"href":87},"API token"," or ",[597,2080,2081],{"href":87},"access token"," that holds ",[611,2084,1211],{}," may call\n",[611,2087,2088],{},"GET \u002Fapi\u002Fadmin\u002Fusers\u002F:id",". A token that holds ",[611,2091,1220],{}," may call ",[611,2094,1248],{},". Every other admin route needs\na session.",[630,2097,2098,2111,2119,2125,2133],{},[633,2099,2100,2103,2104,2106,2107,2110],{},[636,2101,2102],{},"Staff only, by what the user holds."," A token carries a permission only when its user could use it. A user without\n",[611,2105,1211],{}," cannot pick it for an API token, and ",[597,2108,225],{"href":2109},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Fserver#scopes"," leaves it out\nof their tokens and shows it as unavailable on the consent screen.",[633,2112,2113,2116,2117,689],{},[636,2114,2115],{},"And on every request."," The token's frozen permissions and the user's roles are both checked. A token whose user is no\nlonger staff is refused (",[611,2118,2051],{},[633,2120,2121,2122,606],{},"A token without the permission gets ",[611,2123,2124],{},"403 insufficient_scope",[633,2126,2127,2128,2130,2131,606],{},"A wildcard scope such as ",[611,2129,716],{}," covers the non-session permissions only, so it never gives a token ",[611,2132,1953],{},[633,2134,2135,2137,2138,668,2141,2144,2145,606],{},[611,2136,2088],{}," answers a token with ",[611,2139,2140],{},"can.update",[611,2142,2143],{},"can.delete"," set to ",[611,2146,1336],{},[808,2148,2150],{"id":2149},"extend-the-admin-user-page","Extend the admin user page",[593,2152,2153,2154,892],{},"This section is for module authors. A module adds its staff routes, a tab and an overview card to the admin user page\nin one call from its ",[611,2155,2156],{},"setup()",[813,2158,2160],{"className":856,"code":2159,"language":859,"meta":818,"style":818},"import { extendUsersAdmin } from '@frigear-nu\u002Fusers\u002Futils'\n\nawait extendUsersAdmin(nuxt, {\n  routes: [{ method: 'get', path: 'payments', handler: resolver.resolve('.\u002Fruntime\u002Fserver\u002Froutes\u002Fpayments.get') }], \u002F\u002F \u002Fapi\u002Fadmin\u002Fusers\u002F:id\u002Fpayments\n  tab: { \u002F* TabDefinition of @frigear-nu\u002Fwidget *\u002F }, \u002F\u002F area `admin.users`\n  widget: { \u002F* WidgetDefinition *\u002F }, \u002F\u002F area `admin.users.overview`\n})\n",[611,2161,2162,2185,2189,2202,2276,2294,2311],{"__ignoreMap":818},[822,2163,2164,2167,2169,2172,2174,2177,2179,2182],{"class":824,"line":825},[822,2165,2166],{"class":866},"import",[822,2168,958],{"class":881},[822,2170,2171],{"class":877}," extendUsersAdmin",[822,2173,1513],{"class":881},[822,2175,2176],{"class":866}," from",[822,2178,908],{"class":881},[822,2180,2181],{"class":832},"@frigear-nu\u002Fusers\u002Futils",[822,2183,2184],{"class":881},"'\n",[822,2186,2187],{"class":824,"line":885},[822,2188,1680],{"emptyLinePlaceholder":1679},[822,2190,2191,2193,2195,2198,2200],{"class":824,"line":929},[822,2192,1614],{"class":866},[822,2194,2171],{"class":873},[822,2196,2197],{"class":877},"(nuxt",[822,2199,905],{"class":881},[822,2201,937],{"class":881},[822,2203,2204,2207,2209,2211,2214,2217,2219,2221,2224,2226,2228,2231,2233,2235,2238,2240,2242,2245,2247,2250,2252,2255,2257,2259,2262,2264,2267,2269,2271,2273],{"class":824,"line":940},[822,2205,2206],{"class":888},"  routes",[822,2208,892],{"class":881},[822,2210,895],{"class":877},[822,2212,2213],{"class":881},"{",[822,2215,2216],{"class":888}," method",[822,2218,892],{"class":881},[822,2220,908],{"class":881},[822,2222,2223],{"class":832},"get",[822,2225,898],{"class":881},[822,2227,905],{"class":881},[822,2229,2230],{"class":888}," path",[822,2232,892],{"class":881},[822,2234,908],{"class":881},[822,2236,2237],{"class":832},"payments",[822,2239,898],{"class":881},[822,2241,905],{"class":881},[822,2243,2244],{"class":888}," handler",[822,2246,892],{"class":881},[822,2248,2249],{"class":877}," resolver",[822,2251,606],{"class":881},[822,2253,2254],{"class":873},"resolve",[822,2256,878],{"class":877},[822,2258,898],{"class":881},[822,2260,2261],{"class":832},".\u002Fruntime\u002Fserver\u002Froutes\u002Fpayments.get",[822,2263,898],{"class":881},[822,2265,2266],{"class":877},") ",[822,2268,1081],{"class":881},[822,2270,923],{"class":877},[822,2272,905],{"class":881},[822,2274,2275],{"class":1477}," \u002F\u002F \u002Fapi\u002Fadmin\u002Fusers\u002F:id\u002Fpayments\n",[822,2277,2278,2281,2283,2285,2288,2291],{"class":824,"line":950},[822,2279,2280],{"class":888},"  tab",[822,2282,892],{"class":881},[822,2284,958],{"class":881},[822,2286,2287],{"class":1477}," \u002F* TabDefinition of @frigear-nu\u002Fwidget *\u002F",[822,2289,2290],{"class":881}," },",[822,2292,2293],{"class":1477}," \u002F\u002F area `admin.users`\n",[822,2295,2296,2299,2301,2303,2306,2308],{"class":824,"line":994},[822,2297,2298],{"class":888},"  widget",[822,2300,892],{"class":881},[822,2302,958],{"class":881},[822,2304,2305],{"class":1477}," \u002F* WidgetDefinition *\u002F",[822,2307,2290],{"class":881},[822,2309,2310],{"class":1477}," \u002F\u002F area `admin.users.overview`\n",[822,2312,2313,2315],{"class":824,"line":1000},[822,2314,1081],{"class":881},[822,2316,1084],{"class":877},[593,2318,2319,2320,668,2322,2324],{},"The tab and widget need ",[611,2321,790],{},[611,2323,778],{},". Without them they are skipped. Routes are always\nregistered.",[808,2326,724],{"id":2327},"emails",[593,2329,2330,2331,1253,2333,2335,2336,742,2339,668,2342,2345,2346,606],{},"Email change and password change notices go through ",[597,2332,403],{"href":404},[597,2334,407],{"href":408}," installed, they are rendered from the module's Vue templates:\n",[611,2337,2338],{},"users-verify-email-change",[611,2340,2341],{},"users-email-changed",[611,2343,2344],{},"users-password-changed",". Override one by adding\n",[611,2347,2348],{},"app\u002Femails\u002F\u003Cname>.vue",[625,2350,267],{"id":2351},"app",[593,2353,2354,2355,2357,2358,668,2361,606],{},"The admin pages are a users list, a page per user and two dashboard cards (",[652,2356,654],{},"). They follow the\n",[597,2359,2360],{"href":102},"list and detail page patterns",[597,2362,2363],{"href":107},"forms and actions",[593,2365,2366,2371],{},[636,2367,2368,606],{},[611,2369,2370],{},"useUserActions()"," It owns the staff actions, so the list and the user page offer the same ones.",[630,2373,2374,2390,2400],{},[633,2375,2376,2379,2380,2383,2384,2387,2388,606],{},[611,2377,2378],{},"create()"," opens the ",[611,2381,2382],{},"UserCreateSlideover"," (a ",[611,2385,2386],{},"FormSlideover","). It resolves with the created user or ",[611,2389,1336],{},[633,2391,2392,2395,2396,2399],{},[611,2393,2394],{},"remove(user)"," confirms with ",[611,2397,2398],{},"useConfirmDialog",", deletes, shows a toast and refreshes.",[633,2401,2402,2405,2406,2409,2410,2413],{},[611,2403,2404],{},"menu(user, { extra, onRemoved })"," returns the ",[611,2407,2408],{},"RowMenu"," groups. Use ",[611,2411,2412],{},"extra"," to add a group of your own between the\nsign-in link and Delete.",[808,2415,2417],{"id":2416},"the-admin-user-page","The admin user page",[593,2419,2420,2421,2424,2425,2428,2429,2431],{},"The admin user page (",[611,2422,2423],{},"\u003Cadmin page>\u002F:id",") is a tab view over the ",[611,2426,2427],{},"admin.users"," tab area of\n",[597,2430,358],{"href":359},", which this module owns.",[630,2433,2434,2444,2459,2472],{},[633,2435,2436,2437,2439,2440,2443],{},"It registers an ",[636,2438,173],{}," tab with the profile form and account facts, and an ",[611,2441,2442],{},"admin.users.overview"," widget area\nat its top.",[633,2445,2446,2448,2449,2452,2453,2455,2456,2458],{},[597,2447,258],{"href":259}," adds a ",[652,2450,2451],{},"Membership"," tab and ",[597,2454,254],{"href":255}," a\n",[652,2457,254],{}," tab.",[633,2460,2461,2462,2464,2465,2468,2469,606],{},"Each tab receives the response of ",[611,2463,2088],{}," as a ",[611,2466,2467],{},"user"," prop and may emit ",[611,2470,2471],{},"refresh",[633,2473,2474],{},"Without Widget, the page shows the Overview tab alone.",[1090,2476,2477,2478,606],{},"Hiding a tab is not access control. Each tab's API calls must authorize with a permission, through\n",[611,2479,2017],{},[593,2481,2482],{},"What staff can change:",[630,2484,2485,2493,2501,2518,2521,2528,2531],{},[633,2486,2487,2490,2491,606],{},[636,2488,2489],{},"Name."," With ",[611,2492,1939],{},[633,2494,2495,2490,2498,2500],{},[636,2496,2497],{},"Email and verification.",[611,2499,1969],{},", which is session only. An email changed by staff is marked unverified,\nunless staff mark it verified in the same request.",[633,2502,2503,2506,2507,2509,2510,2513,2514,606],{},[636,2504,2505],{},"Roles are not here."," Staff assign and remove roles in the ",[597,2508,1150],{"href":218},",\nwith the ",[611,2511,2512],{},"roles:assign"," permission. See ",[597,2515,2517],{"href":2516},"#roles","Roles",[633,2519,2520],{},"Passwords are never returned.",[633,2522,2523,2524,2527],{},"A host that grants roles by email (an allowlist) judges the role the account has ",[652,2525,2526],{},"after"," the change. Let only users\nwho may assign roles change an address that is on an allowlist.",[633,2529,2530],{},"Creating a user can email them a sign-in link, which also verifies the address.",[633,2532,2533],{},"Deleting a user also removes everything Auth keeps for them: tokens, passkeys, reset codes and sign-in links.",[593,2535,2536,2537,2540,2541,2544],{},"With Menu and the admin page on, an item ",[611,2538,2539],{},"users-admin"," (\"Users\", order 10) is added to the ",[611,2542,2543],{},"admin.nav"," menu.",[808,2546,1453],{"id":2547},"email-change",[593,2549,1849,2550,2553,2554,606],{},[611,2551,2552],{},"verifyEmailChange",", the links in the two emails open pages. Opening a page changes nothing, because mail scanners\nand link previews follow links too. Only the button on the page applies the change, with a ",[611,2555,2556],{},"POST",[593,2558,2559,2564,2565,606],{},[636,2560,2561],{},[611,2562,2563],{},"\u002Fauth\u002Fverify-email-change?code="," is emailed to the new address. It reads the request through\n",[611,2566,2567],{},"GET \u002Fapi\u002Fusers\u002Femail-change",[630,2569,2570,2585,2588],{},[633,2571,2572,2573,2576,2577,2580,2581,2584],{},"Signed out, while the link still works, it redirects to the sign-in page. It passes ",[611,2574,2575],{},"?redirect="," (this URL, code\nincluded) and ",[611,2578,2579],{},"?reason=users.emailChange",". The sign-in page says why you have to sign in\n(",[611,2582,2583],{},"users.emailChange.signInReason",") and returns here afterwards. The code is not used up on the way.",[633,2586,2587],{},"Signed in as another account, it says so and offers to sign out.",[633,2589,2590,2591,2594,2595,2598,2599,2602,2603,2606],{},"Signed in as the owner, it shows the old and new address and a ",[636,2592,2593],{},"Confirm new email address"," button. That button\nposts to ",[611,2596,2597],{},"\u002Fapi\u002Fusers\u002Femail-change\u002Fconfirm",". It then goes to ",[611,2600,2601],{},"profilePath?email-changed=1",", or shows the result when\n",[611,2604,2605],{},"profilePath"," is not set.",[593,2608,2609,2614,2615,2618],{},[636,2610,2611],{},[611,2612,2613],{},"\u002Fauth\u002Frevert-email-change?code="," is emailed to the old address after the change. It shows both addresses and a\n",[636,2616,2617],{},"Restore"," button. It works signed out, because whoever changed the address may have locked the owner out; the code is\nthe proof. The result shows on the page.",[593,2620,2621,2622,2625,2626,2628,2629,2632,2633,2635],{},"Both pages use the ",[611,2623,2624],{},"AuthCard"," of ",[597,2627,168],{"href":169}," and need ",[611,2630,2631],{},"@nuxt\u002Fui",". Without ",[611,2634,2631],{},", only the API\nis registered, with a warning. Your app can replace either page by declaring its own page at that path.",[625,2637,2517],{"id":2638},"roles",[593,2640,2641,2642,2644,2645,2647,2648,2651,2652,2654,2655,2658,2659,2661,2662,2665],{},"The module does not read or write a ",[611,2643,1143],{}," field. The old ",[611,2646,1143],{}," is gone from the admin requests and from ",[611,2649,2650],{},"AdminUserDto",".\nA user holds several roles, stored by ",[597,2653,199],{"href":200}," in ",[611,2656,2657],{},"authorization_user_roles",",\ntogether with the default, ",[611,2660,2029],{}," and hook roles. Authorization still adds a ",[611,2663,2664],{},"users.role"," column in this release, only so\nexisting values can be moved. Nothing evaluates it, so removing a role there changes nothing.",[630,2667,2668,2687,2699],{},[633,2669,2670,2673,2674,2676,2677,2680,2681,2683,2684,606],{},[636,2671,2672],{},"Assign roles"," in the Authorization admin, or from code. See ",[597,2675,205],{"href":206},"\nand ",[597,2678,2679],{"href":218},"the admin",". Moving existing ",[611,2682,1143],{}," values is covered in\n",[597,2685,2686],{"href":152},"Migrate to the new access control",[633,2688,2689,2692,2693,2695,2696,2698],{},[636,2690,2691],{},"The users list"," still filters by role and shows each user's stored ",[611,2694,2638],{},", when Authorization is installed. Default and\n",[611,2697,2029],{}," roles are held without a stored row, so the filter does not find them.",[633,2700,2701,738,2704,2707,2708,2710],{},[636,2702,2703],{},"Create users without a role.",[611,2705,2706],{},"POST \u002Fapi\u002Fadmin\u002Fusers"," takes no ",[611,2709,1143],{},". Assign one afterwards.",[625,2712,2714],{"id":2713},"removed-hooks","Removed hooks",[593,2716,2717,2718,668,2721,2724,2725,2728,2729,2731,2732,2734,2735,2737],{},"The ",[611,2719,2720],{},"users:admin:authorize",[611,2722,2723],{},"users:admin:roles"," hooks, and the ",[611,2726,2727],{},"users:admin:*"," scope checks that came with them, are\ngone. A listener for one is never called. Grant the ",[597,2730,622],{"href":621}," to roles instead, and use\n",[611,2733,2042],{}," for a rule that has to override roles. ",[597,2736,2686],{"href":152},"\nlists each hook and its replacement.",[625,2739,2741],{"id":2740},"routes","Routes",[593,2743,2744,2745,2748,2749,2752,2753,2755],{},"Routes the module registers, and who may call them. Paths come from ",[611,2746,2747],{},"usersApi",", so app code and tests never spell them\nout. Import it from ",[611,2750,2751],{},"@frigear-nu\u002Fusers\u002Fapi"," in another module's app code, because that entry carries no build-time\nimports. ",[611,2754,2181],{}," exports it too.",[813,2757,2759],{"className":856,"code":2758,"language":859,"meta":818,"style":818},"import { usersApi } from '@frigear-nu\u002Fusers\u002Fapi'\n\nawait $fetch(usersApi.me.profile, { method: 'PATCH', body: { name: 'Ada' } })\nawait $fetch(usersApi.admin.user(42), { method: 'DELETE' }) \u002F\u002F 204\n",[611,2760,2761,2780,2784,2846],{"__ignoreMap":818},[822,2762,2763,2765,2767,2770,2772,2774,2776,2778],{"class":824,"line":825},[822,2764,2166],{"class":866},[822,2766,958],{"class":881},[822,2768,2769],{"class":877}," usersApi",[822,2771,1513],{"class":881},[822,2773,2176],{"class":866},[822,2775,908],{"class":881},[822,2777,2751],{"class":832},[822,2779,2184],{"class":881},[822,2781,2782],{"class":824,"line":885},[822,2783,1680],{"emptyLinePlaceholder":1679},[822,2785,2786,2788,2791,2794,2796,2799,2801,2804,2806,2808,2810,2812,2814,2817,2819,2821,2824,2826,2828,2831,2833,2835,2838,2840,2842,2844],{"class":824,"line":929},[822,2787,1614],{"class":866},[822,2789,2790],{"class":873}," $fetch",[822,2792,2793],{"class":877},"(usersApi",[822,2795,606],{"class":881},[822,2797,2798],{"class":877},"me",[822,2800,606],{"class":881},[822,2802,2803],{"class":877},"profile",[822,2805,905],{"class":881},[822,2807,958],{"class":881},[822,2809,2216],{"class":888},[822,2811,892],{"class":881},[822,2813,908],{"class":881},[822,2815,2816],{"class":832},"PATCH",[822,2818,898],{"class":881},[822,2820,905],{"class":881},[822,2822,2823],{"class":888}," body",[822,2825,892],{"class":881},[822,2827,958],{"class":881},[822,2829,2830],{"class":888}," name",[822,2832,892],{"class":881},[822,2834,908],{"class":881},[822,2836,2837],{"class":832},"Ada",[822,2839,898],{"class":881},[822,2841,1513],{"class":881},[822,2843,1513],{"class":881},[822,2845,1084],{"class":877},[822,2847,2848,2850,2852,2854,2856,2858,2860,2862,2864,2868,2870,2872,2874,2876,2878,2880,2883,2885,2887,2889],{"class":824,"line":940},[822,2849,1614],{"class":866},[822,2851,2790],{"class":873},[822,2853,2793],{"class":877},[822,2855,606],{"class":881},[822,2857,849],{"class":877},[822,2859,606],{"class":881},[822,2861,2467],{"class":873},[822,2863,878],{"class":877},[822,2865,2867],{"class":2866},"sbssI","42",[822,2869,2069],{"class":877},[822,2871,905],{"class":881},[822,2873,958],{"class":881},[822,2875,2216],{"class":888},[822,2877,892],{"class":881},[822,2879,908],{"class":881},[822,2881,2882],{"class":832},"DELETE",[822,2884,898],{"class":881},[822,2886,1513],{"class":881},[822,2888,2266],{"class":877},[822,2890,2891],{"class":1477},"\u002F\u002F 204\n",[593,2893,2894,2897,2898,2900,2901,694,2904,742,2907,742,2910,742,2913,742,2916,2919],{},[636,2895,2896],{},"Self-service routes"," need a cookie session (",[611,2899,1560],{},"). They refresh the session user afterwards. They answer\nthe caller's profile (",[611,2902,2903],{},"ProfileDto",[611,2905,2906],{},"id",[611,2908,2909],{},"name",[611,2911,2912],{},"email",[611,2914,2915],{},"avatar",[611,2917,2918],{},"emailVerifiedAt","), never the stored row.",[593,2921,2922,2924,2925,689],{},[636,2923,724],{}," are trimmed and lowercased wherever they are written. Every \"already in use\" check ignores case\n(",[611,2926,2927],{},"409 conflict",[1270,2929,2930,2949],{},[1273,2931,2932],{},[1276,2933,2934,2937,2942,2944,2947],{},[1279,2935,2936],{},"Method",[1279,2938,2939,2940,2069],{},"Path (",[611,2941,2747],{},[1279,2943,168],{},[1279,2945,2946],{},"Status",[1279,2948,1290],{},[1292,2950,2951,2987,3015,3039,3069,3095,3121,3149,3171,3205,3228,3254,3291,3320,3346],{},[1276,2952,2953,2957,2966,2969,2972],{},[1297,2954,2955],{},[611,2956,2816],{},[1297,2958,2959,2962,2963,2069],{},[611,2960,2961],{},"\u002Fapi\u002Fusers\u002Fme"," (",[611,2964,2965],{},"me.profile",[1297,2967,2968],{},"session only",[1297,2970,2971],{},"200",[1297,2973,2974,1866,2977,2979,2980,2982,2983,2986],{},[611,2975,2976],{},"{ name?, avatar? }",[611,2978,2909],{}," is at most 120 characters. ",[611,2981,2915],{}," is an http(s) URL, or ",[611,2984,2985],{},"null"," to clear it.",[1276,2988,2989,2994,3002,3004,3006],{},[1297,2990,2991],{},[611,2992,2993],{},"PUT",[1297,2995,2996,2962,2999,2069],{},[611,2997,2998],{},"\u002Fapi\u002Fusers\u002Fme\u002Femail",[611,3000,3001],{},"me.email",[1297,3003,2968],{},[1297,3005,2971],{},[1297,3007,3008,3011,3012,606],{},[611,3009,3010],{},"{ email }",". Returns the profile plus ",[611,3013,3014],{},"verificationRequired",[1276,3016,3017,3021,3029,3031,3033],{},[1297,3018,3019],{},[611,3020,2993],{},[1297,3022,3023,2962,3026,2069],{},[611,3024,3025],{},"\u002Fapi\u002Fusers\u002Fme\u002Fpassword",[611,3027,3028],{},"me.password",[1297,3030,2968],{},[1297,3032,2971],{},[1297,3034,3035,3038],{},[611,3036,3037],{},"{ currentPassword, newPassword, confirmNewPassword }",". Sets the password and emails a notice.",[1276,3040,3041,3046,3054,3057,3059],{},[1297,3042,3043],{},[611,3044,3045],{},"GET",[1297,3047,3048,2962,3051,2069],{},[611,3049,3050],{},"\u002Fapi\u002Fusers\u002Femail-change?code=",[611,3052,3053],{},"emailChange",[1297,3055,3056],{},"none",[1297,3058,2971],{},[1297,3060,3061,3062,3064,3065,3068],{},"Read-only preview of a request (",[611,3063,2552],{},"). ",[611,3066,3067],{},"404"," for an unknown code.",[1276,3070,3071,3075,3082,3085,3087],{},[1297,3072,3073],{},[611,3074,2556],{},[1297,3076,3077,2962,3079,2069],{},[611,3078,2597],{},[611,3080,3081],{},"emailChangeConfirm",[1297,3083,3084],{},"session only, owner",[1297,3086,2971],{},[1297,3088,3089,3092,3093,689],{},[611,3090,3091],{},"{ code }",". Applies the change (",[611,3094,2552],{},[1276,3096,3097,3101,3109,3111,3113],{},[1297,3098,3099],{},[611,3100,3045],{},[1297,3102,3103,2962,3106,2069],{},[611,3104,3105],{},"\u002Fapi\u002Fusers\u002Femail-change\u002Frevert?code=",[611,3107,3108],{},"emailChangeRevert",[1297,3110,3056],{},[1297,3112,2971],{},[1297,3114,3115,3116,2962,3119,689],{},"Read-only preview of a revert: ",[611,3117,3118],{},"{ status: 'pending' | 'reverted' | 'expired', oldEmail, newEmail }",[611,3120,2552],{},[1276,3122,3123,3127,3134,3137,3139],{},[1297,3124,3125],{},[611,3126,2556],{},[1297,3128,3129,2962,3132,2069],{},[611,3130,3131],{},"\u002Fapi\u002Fusers\u002Femail-change\u002Frevert",[611,3133,3108],{},[1297,3135,3136],{},"none (the code)",[1297,3138,2971],{},[1297,3140,3141,3143,3144,2962,3147,689],{},[611,3142,3091],{},". Restores the old address. Returns ",[611,3145,3146],{},"{ email, signedOut }",[611,3148,2552],{},[1276,3150,3151,3155,3161,3166,3168],{},[1297,3152,3153],{},[611,3154,3045],{},[1297,3156,3157,2962,3159,2069],{},[611,3158,685],{},[611,3160,2427],{},[1297,3162,3163,3164],{},"session or token with ",[611,3165,1220],{},[1297,3167,2971],{},[1297,3169,3170],{},"A paginated list of users.",[1276,3172,3173,3177,3183,3190,3193],{},[1297,3174,3175],{},[611,3176,2556],{},[1297,3178,3179,2962,3181,2069],{},[611,3180,685],{},[611,3182,2427],{},[1297,3184,3185,2962,3187,3189],{},[611,3186,1925],{},[611,3188,1969],{}," to mark it verified)",[1297,3191,3192],{},"201",[1297,3194,3195,3198,3199,1866,3201,3204],{},[611,3196,3197],{},"{ name, email, emailVerified?, sendWelcomeEmail? }",". Returns the ",[611,3200,2650],{},[611,3202,3203],{},"409"," when the email is taken.",[1276,3206,3207,3211,3219,3223,3225],{},[1297,3208,3209],{},[611,3210,3045],{},[1297,3212,3213,2962,3216,2069],{},[611,3214,3215],{},"\u002Fapi\u002Fadmin\u002Fusers\u002Fnew",[611,3217,3218],{},"admin.new",[1297,3220,3221],{},[611,3222,1220],{},[1297,3224,2971],{},[1297,3226,3227],{},"The newest sign-ups of the last 30 days, for the dashboard widget.",[1276,3229,3230,3234,3242,3246,3248],{},[1297,3231,3232],{},[611,3233,3045],{},[1297,3235,3236,2962,3239,2069],{},[611,3237,3238],{},"\u002Fapi\u002Fadmin\u002Fusers\u002Fstats",[611,3240,3241],{},"admin.stats",[1297,3243,3244],{},[611,3245,1220],{},[1297,3247,2971],{},[1297,3249,3250,3253],{},[611,3251,3252],{},"{ total, newUsers, days }"," for the dashboard stat card.",[1276,3255,3256,3260,3268,3272,3274],{},[1297,3257,3258],{},[611,3259,3045],{},[1297,3261,3262,2962,3265,2069],{},[611,3263,3264],{},"\u002Fapi\u002Fadmin\u002Fusers\u002F:id",[611,3266,3267],{},"admin.user(id)",[1297,3269,3163,3270],{},[611,3271,1211],{},[1297,3273,2971],{},[1297,3275,3276,3277,1432,3279,742,3282,742,3285,668,3288,606],{},"One user: the ",[611,3278,2650],{},[611,3280,3281],{},"hasPassword",[611,3283,3284],{},"passkeys",[611,3286,3287],{},"subscription",[611,3289,3290],{},"can",[1276,3292,3293,3297,3303,3311,3313],{},[1297,3294,3295],{},[611,3296,2816],{},[1297,3298,3299,2962,3301,2069],{},[611,3300,3264],{},[611,3302,3267],{},[1297,3304,3305,3307,3308,3310],{},[611,3306,1939],{}," + target protection (",[611,3309,1969],{}," for email)",[1297,3312,2971],{},[1297,3314,3315,3198,3318,606],{},[611,3316,3317],{},"{ name?, email?, emailVerified? }",[611,3319,2650],{},[1276,3321,3322,3326,3332,3337,3340],{},[1297,3323,3324],{},[611,3325,2882],{},[1297,3327,3328,2962,3330,2069],{},[611,3329,3264],{},[611,3331,3267],{},[1297,3333,3334,3336],{},[611,3335,1953],{}," (session only) + target protection",[1297,3338,3339],{},"204",[1297,3341,3342,3343,606],{},"Deletes a user. Not yourself: ",[611,3344,3345],{},"400",[1276,3347,3348,3352,3360,3364,3366],{},[1297,3349,3350],{},[611,3351,2556],{},[1297,3353,3354,2962,3357,2069],{},[611,3355,3356],{},"\u002Fapi\u002Fadmin\u002Fusers\u002F:id\u002Fresend",[611,3358,3359],{},"admin.resend(id)",[1297,3361,3362,3336],{},[611,3363,1969],{},[1297,3365,2971],{},[1297,3367,3368,3369,3372],{},"Resends the sign-in link. ",[611,3370,3371],{},"501 not_configured"," when sign-in links are off.",[593,3374,3375,2490,3380,3382,3383,3386],{},[636,3376,3377,606],{},[611,3378,3379],{},"PUT \u002Fapi\u002Fusers\u002Fme\u002Femail",[611,3381,2552],{},", it stores the request and emails the new address instead\n(",[611,3384,3385],{},"verificationRequired: true","). A new request expires the link of an earlier one still waiting. Whenever the address\nchanges, here, on confirmation or on a revert, unused password reset codes and sign-in links are deleted: they went to\nthe previous address.",[593,3388,3389,3394,3395,3398],{},[636,3390,3391,606],{},[611,3392,3393],{},"PUT \u002Fapi\u002Fusers\u002Fme\u002Fpassword"," A wrong current password answers ",[611,3396,3397],{},"401",". A failing mail never undoes the change.",[593,3400,3401,3405,3406,1866,3409,3412],{},[636,3402,3403,606],{},[611,3404,2567],{}," It answers ",[611,3407,3408],{},"{ status: 'pending' | 'confirmed' | 'expired', signedIn, owner, newEmail, oldEmail }",[611,3410,3411],{},"oldEmail"," is masked unless you are the owner.",[593,3414,3415,3420,3421,3423,3424,3426,3427,3429],{},[636,3416,3417,606],{},[611,3418,3419],{},"POST \u002Fapi\u002Fusers\u002Femail-change\u002Fconfirm"," The code is single use and valid for 24 hours. The route verifies the\naddress, refreshes the session and emails the old address a revert link. It answers ",[611,3422,1252],{}," when the request belongs to\nanother account, ",[611,3425,3345],{}," when the code is used or expired, and ",[611,3428,3203],{}," when the address was taken meanwhile.",[593,3431,3432,3437,3438,3441],{},[636,3433,3434,606],{},[611,3435,3436],{},"POST \u002Fapi\u002Fusers\u002Femail-change\u002Frevert"," The code is single use and valid for 7 days. The route ends every session\nof that account and revokes its API tokens (unless Auth's ",[611,3439,3440],{},"apiTokens.revokeOnReset"," is off).",[593,3443,3444,3405,3448,3451,3452,742,3455,3458,3459,742,3462,742,3464,742,3467,668,3470,3473,3474,3476,3477,3479,3480,3482,3483,3485],{},[636,3445,3446,606],{},[611,3447,1248],{},[611,3449,3450],{},"{ items, total, page, pageSize, hasRoles, hasSubscriptions, roles }",". The query\ntakes ",[611,3453,3454],{},"page",[611,3456,3457],{},"pageSize"," (up to 100), ",[611,3460,3461],{},"q",[611,3463,1143],{},[611,3465,3466],{},"verified",[611,3468,3469],{},"sort",[611,3471,3472],{},"order",". Items are ",[611,3475,2650],{}," plus\n",[611,3478,3287],{},", and the stored ",[611,3481,2638],{}," when Authorization is installed. ",[611,3484,2638],{}," lists the roles the filter offers.",[593,3487,3488,3490,3491,742,3493,742,3495,742,3497,742,3499,742,3501,668,3504,3507],{},[611,3489,2650],{}," is an allowlist: ",[611,3492,2906],{},[611,3494,2909],{},[611,3496,2912],{},[611,3498,2915],{},[611,3500,2918],{},[611,3502,3503],{},"lastLoginAt",[611,3505,3506],{},"createdAt",", with\ndates as ISO strings. It never carries the password hash, a role or columns other modules add.",[593,3509,3510,3511,3513,3514,3516,3517,3519,3520,3522,3523,3525,3526,3528,3529,2962,3532,742,3535,742,3538,3541,3542,606],{},"The staff routes exist with ",[611,3512,642],{}," and check the ",[611,3515,716],{}," permissions. Routes on one user authorize\nbefore they look the user up. So a visitor gets ",[611,3518,3397],{}," and a denied caller ",[611,3521,1252],{},", even for an unknown id. Only then come\n",[611,3524,3345],{}," (a malformed id) and ",[611,3527,3067],{},". Errors carry a snake_case ",[611,3530,3531],{},"data.code",[611,3533,3534],{},"not_found",[611,3536,3537],{},"forbidden",[611,3539,3540],{},"conflict",", ...).\nSee ",[597,3543,121],{"href":122},[593,3545,3546,3547,668,3549,3551,3552,3554,3555,3557],{},"The emailed links point at the pages ",[611,3548,667],{},[611,3550,671],{}," (see\n",[597,3553,1453],{"href":1452},"). No ",[611,3556,3045],{}," changes anything.",[625,3559,41],{"id":3560},"database",[593,3562,3563,3564,3566],{},"Tables the module adds. Generate and commit a ",[597,3565,1095],{"href":1123}," after installing.",[808,3568,3569],{"id":617},[611,3570,617],{},[593,3572,2717,3573,3577],{},[597,3574,3576],{"href":3575},"..\u002F..\u002F..\u002F..\u002Fpackages\u002Fusers\u002Fsrc\u002Fruntime\u002Fdb\u002Fschema.sqlite.ts","default SQLite schema"," has:",[630,3579,3580,3585,3590,3595,3600,3606],{},[633,3581,3582,3583,905],{},"an auto-incrementing ",[611,3584,2906],{},[633,3586,3587,3588,905],{},"a required ",[611,3589,2909],{},[633,3591,3592,3593,905],{},"a unique, required ",[611,3594,2912],{},[633,3596,3597,3598,905],{},"an optional ",[611,3599,2915],{},[633,3601,3602,3603,905],{},"a nullable ",[611,3604,3605],{},"acceptedTermsAt",[633,3607,3608,668,3610,606],{},[611,3609,3506],{},[611,3611,3612],{},"updatedAt",[593,3614,3615,3616,3618,3619,742,3622,742,3624,668,3626,3629,3630,3632,3633,3635],{},"Other modules add columns through the ",[597,3617,381],{"href":382}," extension helpers. Auth adds\n",[611,3620,3621],{},"password",[611,3623,2918],{},[611,3625,3503],{},[611,3627,3628],{},"sessionVersion",". Roles are not read from a column: Authorization keeps them in\n",[611,3631,2657],{}," (the old ",[611,3634,1143],{}," column stays only for the data migration).",[808,3637,3639],{"id":3638},"account_email_changes",[611,3640,3638],{},[593,3642,3643,3644,3646,3647,3649,3650,3653],{},"Registered with ",[611,3645,2552],{},". It holds one row per \"change my email\" request: the old and new address, the\nconfirmation ",[611,3648,611],{},", the ",[611,3651,3652],{},"revertCode"," and their timestamps. The table is named after Account, although Users owns it.",[625,3655,395],{"id":3656},"translations",[593,3658,3659,3660,3663,3664,668,3667,3670,3671,3674,3675,3678,3679,606],{},"Override any string in your app's ",[611,3661,3662],{},"i18n\u002Flocales\u002F\u003Ccode>.json",". The module registers ",[611,3665,3666],{},"da",[611,3668,3669],{},"en"," locale files under\n",[611,3672,3673],{},"users.admin.*"," for the admin pages and ",[611,3676,3677],{},"users.emailChange.*"," for the email change pages. See\n",[597,3680,3682],{"href":3681},"\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fi18n#overriding-module-strings-in-your-app","overriding module strings",[625,3684,3686],{"id":3685},"next-steps","Next steps",[630,3688,3689,3694,3699,3710,3715],{},[633,3690,3691,3693],{},[597,3692,199],{"href":200}," – roles and permissions, and who may assign them.",[633,3695,3696,3698],{},[597,3697,1150],{"href":218}," – give a user a role.",[633,3700,3701,3703,3704,742,3707,3709],{},[597,3702,168],{"href":176}," – ",[611,3705,3706],{},"authorize",[611,3708,697],{}," and the principal.",[633,3711,3712,3714],{},[597,3713,217],{"href":351}," – the admin area the user pages live in.",[633,3716,3717,3720],{},[597,3718,3719],{"href":68},"Auth and scopes"," – how permissions decide who is staff.",[3722,3723,3724],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":818,"searchDepth":885,"depth":885,"links":3726},[3727,3728,3734,3735,3744,3748,3749,3750,3751,3755,3756],{"id":627,"depth":885,"text":628},{"id":802,"depth":885,"text":803,"children":3729},[3730,3731,3732,3733],{"id":810,"depth":929,"text":811},{"id":842,"depth":929,"text":843},{"id":1087,"depth":929,"text":1088},{"id":1126,"depth":929,"text":1127},{"id":1261,"depth":885,"text":1262},{"id":1463,"depth":885,"text":1464,"children":3736},[3737,3738,3739,3740,3742,3743],{"id":1588,"depth":929,"text":1589},{"id":622,"depth":929,"text":706},{"id":2000,"depth":929,"text":2001},{"id":2065,"depth":929,"text":3741},"Reading users with a token (users:read)",{"id":2149,"depth":929,"text":2150},{"id":2327,"depth":929,"text":724},{"id":2351,"depth":885,"text":267,"children":3745},[3746,3747],{"id":2416,"depth":929,"text":2417},{"id":2547,"depth":929,"text":1453},{"id":2638,"depth":885,"text":2517},{"id":2713,"depth":885,"text":2714},{"id":2740,"depth":885,"text":2741},{"id":3560,"depth":885,"text":41,"children":3752},[3753,3754],{"id":617,"depth":929,"text":617},{"id":3638,"depth":929,"text":3638},{"id":3656,"depth":885,"text":395},{"id":3685,"depth":885,"text":3686},"Stores your app's users, lets staff manage them and lets each user change their own name, email and password.","md",null,{},{"title":191,"description":3757},"82VgrP2ONAPaVE3o1STzCW1PyHdsbXZzO9yNR2SbpZc",[3764,3766],{"title":187,"path":188,"stem":189,"description":3765,"children":-1},"Let people sign in with GitHub, Google, frigear.nu or any OpenID Connect provider, and keep their tokens to call it.",{"title":195,"path":196,"stem":197,"description":3767,"children":-1},"Gives signed-in users an account area where they change their profile, password, passkeys and API tokens.",1794503145856]