[{"data":1,"prerenderedAt":3864},["ShallowReactive",2],{"navigation_docs":3,"-modules-identity-oauth-server-server":587,"-modules-identity-oauth-server-server-surround":3859},[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":3852,"extension":3853,"links":3854,"meta":3855,"navigation":3856,"path":231,"seo":3857,"stem":232,"__hash__":3858},"docs\u002F3.modules\u002F1.identity\u002F6.oauth-server\u002F2.server.md","OAuth Server helpers",{"type":591,"value":592,"toc":3823},"minimark",[593,601,606,612,843,860,880,892,904,912,915,1165,1531,1567,1572,1586,1655,1669,1673,1679,1736,1740,1747,1750,1814,1823,1827,1849,2036,2047,2151,2184,2188,2211,2270,2280,2398,2402,2405,2452,2469,2472,2479,2585,2619,2622,2626,2682,2689,2698,2803,2809,2819,2823,2841,2853,2857,2880,2884,2922,2926,2936,2954,3238,3242,3260,3264,3274,3406,3418,3422,3440,3458,3461,3464,3567,3620,3626,3667,3711,3727,3730,3740,3744,3750,3777,3783,3787,3819],[594,595,596,597,600],"p",{},"This page shows how to work with OAuth applications from your own code: create them on the server, define and cap the\nscopes they may ask for, let them call your API as the user, and sign one mobile app in to several services. It is for app developers and module authors, in\nserver mode only. Set up the module first: ",[598,599,173],"a",{"href":226},".",[602,603,605],"h2",{"id":604},"manage-applications-from-server-code","Manage applications from server code",[594,607,608,609,600],{},"Use these helpers when an OAuth application should be created or changed by your own route, not in the admin area.\nThey are auto-imported in ",[598,610,611],{"href":87},"Nitro",[613,614,615,628],"table",{},[616,617,618],"thead",{},[619,620,621,625],"tr",{},[622,623,624],"th",{},"Helper",[622,626,627],{},"What it does",[629,630,631,666,676,692,705,715,725,735,745,762,772,782,800,813,823,833],"tbody",{},[619,632,633,640],{},[634,635,636],"td",{},[637,638,639],"code",{},"requireOAuthAdmin(event)",[634,641,642,643,646,647,650,651,654,655,658,659,662,663,600],{},"Lets only users with the ",[637,644,645],{},"oauth:manage"," ",[598,648,649],{"href":200},"permission"," through, by cookie session. Returns ",[637,652,653],{},"{ userId }",", or throws ",[637,656,657],{},"401",", ",[637,660,661],{},"403 forbidden"," or ",[637,664,665],{},"403 session_required",[619,667,668,673],{},[634,669,670],{},[637,671,672],{},"getApplication(id)",[634,674,675],{},"Reads an application. It never includes the secret hash.",[619,677,678,683],{},[634,679,680],{},[637,681,682],{},"requireApplication(id)",[634,684,685,688,689,600],{},[637,686,687],{},"getApplication",", or a ",[637,690,691],{},"404 not_found",[619,693,694,699],{},[634,695,696],{},[637,697,698],{},"createApplication(input, ownerId?)",[634,700,701,702,600],{},"Creates an application and returns ",[637,703,704],{},"{ application, secret }",[619,706,707,712],{},[634,708,709],{},[637,710,711],{},"updateApplication(id, patch)",[634,713,714],{},"Changes an application, validated like the admin API.",[619,716,717,722],{},[634,718,719],{},[637,720,721],{},"deleteApplication(id)",[634,723,724],{},"Deletes an application, validated like the admin API.",[619,726,727,732],{},[634,728,729],{},[637,730,731],{},"rotateApplicationSecret(id)",[634,733,734],{},"Replaces the secret. The old one stops working and refresh tokens are revoked.",[619,736,737,742],{},[634,738,739],{},[637,740,741],{},"toAdminApplicationDto(application, activeGrants?)",[634,743,744],{},"The admin DTO of an application.",[619,746,747,752],{},[634,748,749],{},[637,750,751],{},"applicationCallbackUrl(application)",[634,753,754,755,758,759,600],{},"The redirect URI the application must accept: ",[637,756,757],{},"websiteUrl"," plus ",[637,760,761],{},"callbackPath",[619,763,764,769],{},[634,765,766],{},[637,767,768],{},"authenticateClient(event, body)",[634,770,771],{},"Checks who sent a token or revoke request.",[619,773,774,779],{},[634,775,776],{},[637,777,778],{},"listUserApplications(userId, event?)",[634,780,781],{},"The active applications a user may sign in to, highest priority first.",[619,783,784,789],{},[634,785,786],{},[637,787,788],{},"userCanAccessApplication(userId, application, event?)",[634,790,791,792,795,796,799],{},"The ",[637,793,794],{},"allowedRoles"," check plus the ",[637,797,798],{},"oauth:application:access"," hook.",[619,801,802,807],{},[634,803,804],{},[637,805,806],{},"resolveUserRoles(userId, event?)",[634,808,809,810,812],{},"The user's effective roles from ",[598,811,199],{"href":206},", inheritance included.",[619,814,815,820],{},[634,816,817],{},[637,818,819],{},"revokeUserGrants(userId, applicationId?)",[634,821,822],{},"Ends a user's refresh tokens, for all applications or one.",[619,824,825,830],{},[634,826,827],{},[637,828,829],{},"revokeApplicationGrants(applicationId)",[634,831,832],{},"Signs everyone out of an application.",[619,834,835,840],{},[634,836,837],{},[637,838,839],{},"getOAuthUser(userId)",[634,841,842],{},"The user without the password hash, with the columns your app added.",[594,844,845,851,852,855,856,600],{},[846,847,848],"strong",{},[637,849,850],{},"createApplication"," shows the secret only in its return value. It is ",[637,853,854],{},"null"," for a\n",[598,857,859],{"href":858},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients#public-applications","public application",[594,861,862,871,872,855,875,879],{},[846,863,864,867,868],{},[637,865,866],{},"updateApplication"," and ",[637,869,870],{},"deleteApplication"," answer ",[637,873,874],{},"409 managed_by_config",[598,876,878],{"href":877},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients#configured-clients","configured client",", as the admin API does.",[594,881,882,887,888,891],{},[846,883,884],{},[637,885,886],{},"rotateApplicationSecret"," throws ",[637,889,890],{},"400"," for a public application, which has no secret.",[594,893,894,899,900,903],{},[846,895,896],{},[637,897,898],{},"authenticateClient"," checks the secret of a confidential application. A public one sends its ",[637,901,902],{},"client_id"," only.",[594,905,906,911],{},[846,907,908],{},[637,909,910],{},"revokeUserGrants"," ends sign-ins at once. A user who loses a role is stopped at the next refresh anyway, so call it\nwhen that is too slow.",[594,913,914],{},"This route lets staff create an application:",[916,917,923],"pre",{"className":918,"code":919,"filename":920,"language":921,"meta":922,"style":922},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export default defineEventHandler(async (event) => {\n  const { userId } = await requireOAuthAdmin(event)\n  const { application, secret } = await createApplication({\n    name: 'Shop',\n    websiteUrl: 'https:\u002F\u002Fshop.example.com',\n    allowedRoles: ['member'], \u002F\u002F effective roles, so a role that inherits `member` may sign in too\n    tags: ['shop'],\n  }, userId)\n  return { clientId: application.id, clientSecret: secret }\n})\n","server\u002Fapi\u002Fshop\u002Fconnect.post.ts","ts","",[637,924,925,966,998,1028,1050,1067,1094,1115,1125,1157],{"__ignoreMap":922},[926,927,930,934,937,941,945,949,953,957,960,963],"span",{"class":928,"line":929},"line",1,[926,931,933],{"class":932},"s7zQu","export",[926,935,936],{"class":932}," default",[926,938,940],{"class":939},"s2Zo4"," defineEventHandler",[926,942,944],{"class":943},"sTEyZ","(",[926,946,948],{"class":947},"spNyl","async",[926,950,952],{"class":951},"sMK4o"," (",[926,954,956],{"class":955},"sHdIc","event",[926,958,959],{"class":951},")",[926,961,962],{"class":947}," =>",[926,964,965],{"class":951}," {\n",[926,967,969,972,975,978,981,984,987,990,993,995],{"class":928,"line":968},2,[926,970,971],{"class":947},"  const",[926,973,974],{"class":951}," {",[926,976,977],{"class":943}," userId",[926,979,980],{"class":951}," }",[926,982,983],{"class":951}," =",[926,985,986],{"class":932}," await",[926,988,989],{"class":939}," requireOAuthAdmin",[926,991,944],{"class":992},"swJcz",[926,994,956],{"class":943},[926,996,997],{"class":992},")\n",[926,999,1001,1003,1005,1008,1011,1014,1016,1018,1020,1023,1025],{"class":928,"line":1000},3,[926,1002,971],{"class":947},[926,1004,974],{"class":951},[926,1006,1007],{"class":943}," application",[926,1009,1010],{"class":951},",",[926,1012,1013],{"class":943}," secret",[926,1015,980],{"class":951},[926,1017,983],{"class":951},[926,1019,986],{"class":932},[926,1021,1022],{"class":939}," createApplication",[926,1024,944],{"class":992},[926,1026,1027],{"class":951},"{\n",[926,1029,1031,1034,1037,1040,1044,1047],{"class":928,"line":1030},4,[926,1032,1033],{"class":992},"    name",[926,1035,1036],{"class":951},":",[926,1038,1039],{"class":951}," '",[926,1041,1043],{"class":1042},"sfazB","Shop",[926,1045,1046],{"class":951},"'",[926,1048,1049],{"class":951},",\n",[926,1051,1053,1056,1058,1060,1063,1065],{"class":928,"line":1052},5,[926,1054,1055],{"class":992},"    websiteUrl",[926,1057,1036],{"class":951},[926,1059,1039],{"class":951},[926,1061,1062],{"class":1042},"https:\u002F\u002Fshop.example.com",[926,1064,1046],{"class":951},[926,1066,1049],{"class":951},[926,1068,1070,1073,1075,1078,1080,1083,1085,1088,1090],{"class":928,"line":1069},6,[926,1071,1072],{"class":992},"    allowedRoles",[926,1074,1036],{"class":951},[926,1076,1077],{"class":992}," [",[926,1079,1046],{"class":951},[926,1081,1082],{"class":1042},"member",[926,1084,1046],{"class":951},[926,1086,1087],{"class":992},"]",[926,1089,1010],{"class":951},[926,1091,1093],{"class":1092},"sHwdD"," \u002F\u002F effective roles, so a role that inherits `member` may sign in too\n",[926,1095,1097,1100,1102,1104,1106,1109,1111,1113],{"class":928,"line":1096},7,[926,1098,1099],{"class":992},"    tags",[926,1101,1036],{"class":951},[926,1103,1077],{"class":992},[926,1105,1046],{"class":951},[926,1107,1108],{"class":1042},"shop",[926,1110,1046],{"class":951},[926,1112,1087],{"class":992},[926,1114,1049],{"class":951},[926,1116,1118,1121,1123],{"class":928,"line":1117},8,[926,1119,1120],{"class":951},"  },",[926,1122,977],{"class":943},[926,1124,997],{"class":992},[926,1126,1128,1131,1133,1136,1138,1140,1142,1145,1147,1150,1152,1154],{"class":928,"line":1127},9,[926,1129,1130],{"class":932},"  return",[926,1132,974],{"class":951},[926,1134,1135],{"class":992}," clientId",[926,1137,1036],{"class":951},[926,1139,1007],{"class":943},[926,1141,600],{"class":951},[926,1143,1144],{"class":943},"id",[926,1146,1010],{"class":951},[926,1148,1149],{"class":992}," clientSecret",[926,1151,1036],{"class":951},[926,1153,1013],{"class":943},[926,1155,1156],{"class":951}," }\n",[926,1158,1160,1163],{"class":928,"line":1159},10,[926,1161,1162],{"class":951},"}",[926,1164,997],{"class":943},[916,1166,1168],{"className":918,"code":1167,"language":921,"meta":922,"style":922},"interface ApplicationInput {\n  clientType?: 'confidential' | 'public' \u002F\u002F default confidential; fixed after creation\n  name: string\n  websiteUrl?: string | null \u002F\u002F an origin without a path; https (http only for localhost). Optional for public applications\n  description?: string | null\n  icon?: string | null\n  priority?: number\n  loginUrl?: string | null\n  previewUrlPattern?: string | null \u002F\u002F e.g. https:\u002F\u002F*.vercel.app\n  redirectUris?: string[] \u002F\u002F extra exact redirect URIs; public applications may list custom-scheme URIs\n  allowedRoles?: string[] \u002F\u002F effective roles, inheritance included; empty = every signed-in user\n  tags?: string[]\n  wildcardScopes?: boolean \u002F\u002F default false; may the application ask for wildcard scopes (`*`, `tickets:*`)?\n  isActive?: boolean\n  isListed?: boolean \u002F\u002F default true; false keeps it out of the users' applications list\n}\n\ninterface OAuthApplication extends Omit\u003CRequired\u003CApplicationInput>, 'isActive' | 'isListed'> {\n  id: string \u002F\u002F `app_` + random id: the OAuth `client_id`\n  websiteUrl: string | null\n  ownerId: number | null\n  isActive: boolean\n  isListed: boolean\n  createdAt: Date\n  updatedAt: Date\n}\n",[637,1169,1170,1181,1209,1219,1237,1251,1264,1274,1287,1303,1318,1333,1346,1360,1371,1384,1390,1397,1446,1459,1472,1487,1496,1505,1516,1526],{"__ignoreMap":922},[926,1171,1172,1175,1179],{"class":928,"line":929},[926,1173,1174],{"class":947},"interface",[926,1176,1178],{"class":1177},"sBMFI"," ApplicationInput",[926,1180,965],{"class":951},[926,1182,1183,1186,1189,1191,1194,1196,1199,1201,1204,1206],{"class":928,"line":968},[926,1184,1185],{"class":992},"  clientType",[926,1187,1188],{"class":951},"?:",[926,1190,1039],{"class":951},[926,1192,1193],{"class":1042},"confidential",[926,1195,1046],{"class":951},[926,1197,1198],{"class":951}," |",[926,1200,1039],{"class":951},[926,1202,1203],{"class":1042},"public",[926,1205,1046],{"class":951},[926,1207,1208],{"class":1092}," \u002F\u002F default confidential; fixed after creation\n",[926,1210,1211,1214,1216],{"class":928,"line":1000},[926,1212,1213],{"class":992},"  name",[926,1215,1036],{"class":951},[926,1217,1218],{"class":1177}," string\n",[926,1220,1221,1224,1226,1229,1231,1234],{"class":928,"line":1030},[926,1222,1223],{"class":992},"  websiteUrl",[926,1225,1188],{"class":951},[926,1227,1228],{"class":1177}," string",[926,1230,1198],{"class":951},[926,1232,1233],{"class":1177}," null",[926,1235,1236],{"class":1092}," \u002F\u002F an origin without a path; https (http only for localhost). Optional for public applications\n",[926,1238,1239,1242,1244,1246,1248],{"class":928,"line":1052},[926,1240,1241],{"class":992},"  description",[926,1243,1188],{"class":951},[926,1245,1228],{"class":1177},[926,1247,1198],{"class":951},[926,1249,1250],{"class":1177}," null\n",[926,1252,1253,1256,1258,1260,1262],{"class":928,"line":1069},[926,1254,1255],{"class":992},"  icon",[926,1257,1188],{"class":951},[926,1259,1228],{"class":1177},[926,1261,1198],{"class":951},[926,1263,1250],{"class":1177},[926,1265,1266,1269,1271],{"class":928,"line":1096},[926,1267,1268],{"class":992},"  priority",[926,1270,1188],{"class":951},[926,1272,1273],{"class":1177}," number\n",[926,1275,1276,1279,1281,1283,1285],{"class":928,"line":1117},[926,1277,1278],{"class":992},"  loginUrl",[926,1280,1188],{"class":951},[926,1282,1228],{"class":1177},[926,1284,1198],{"class":951},[926,1286,1250],{"class":1177},[926,1288,1289,1292,1294,1296,1298,1300],{"class":928,"line":1127},[926,1290,1291],{"class":992},"  previewUrlPattern",[926,1293,1188],{"class":951},[926,1295,1228],{"class":1177},[926,1297,1198],{"class":951},[926,1299,1233],{"class":1177},[926,1301,1302],{"class":1092}," \u002F\u002F e.g. https:\u002F\u002F*.vercel.app\n",[926,1304,1305,1308,1310,1312,1315],{"class":928,"line":1159},[926,1306,1307],{"class":992},"  redirectUris",[926,1309,1188],{"class":951},[926,1311,1228],{"class":1177},[926,1313,1314],{"class":943},"[] ",[926,1316,1317],{"class":1092},"\u002F\u002F extra exact redirect URIs; public applications may list custom-scheme URIs\n",[926,1319,1321,1324,1326,1328,1330],{"class":928,"line":1320},11,[926,1322,1323],{"class":992},"  allowedRoles",[926,1325,1188],{"class":951},[926,1327,1228],{"class":1177},[926,1329,1314],{"class":943},[926,1331,1332],{"class":1092},"\u002F\u002F effective roles, inheritance included; empty = every signed-in user\n",[926,1334,1336,1339,1341,1343],{"class":928,"line":1335},12,[926,1337,1338],{"class":992},"  tags",[926,1340,1188],{"class":951},[926,1342,1228],{"class":1177},[926,1344,1345],{"class":943},"[]\n",[926,1347,1349,1352,1354,1357],{"class":928,"line":1348},13,[926,1350,1351],{"class":992},"  wildcardScopes",[926,1353,1188],{"class":951},[926,1355,1356],{"class":1177}," boolean",[926,1358,1359],{"class":1092}," \u002F\u002F default false; may the application ask for wildcard scopes (`*`, `tickets:*`)?\n",[926,1361,1363,1366,1368],{"class":928,"line":1362},14,[926,1364,1365],{"class":992},"  isActive",[926,1367,1188],{"class":951},[926,1369,1370],{"class":1177}," boolean\n",[926,1372,1374,1377,1379,1381],{"class":928,"line":1373},15,[926,1375,1376],{"class":992},"  isListed",[926,1378,1188],{"class":951},[926,1380,1356],{"class":1177},[926,1382,1383],{"class":1092}," \u002F\u002F default true; false keeps it out of the users' applications list\n",[926,1385,1387],{"class":928,"line":1386},16,[926,1388,1389],{"class":951},"}\n",[926,1391,1393],{"class":928,"line":1392},17,[926,1394,1396],{"emptyLinePlaceholder":1395},true,"\n",[926,1398,1400,1402,1405,1408,1411,1414,1417,1419,1422,1425,1427,1430,1432,1434,1436,1439,1441,1444],{"class":928,"line":1399},18,[926,1401,1174],{"class":947},[926,1403,1404],{"class":1177}," OAuthApplication",[926,1406,1407],{"class":947}," extends",[926,1409,1410],{"class":1177}," Omit",[926,1412,1413],{"class":951},"\u003C",[926,1415,1416],{"class":1177},"Required",[926,1418,1413],{"class":951},[926,1420,1421],{"class":1177},"ApplicationInput",[926,1423,1424],{"class":951},">,",[926,1426,1039],{"class":951},[926,1428,1429],{"class":1042},"isActive",[926,1431,1046],{"class":951},[926,1433,1198],{"class":951},[926,1435,1039],{"class":951},[926,1437,1438],{"class":1042},"isListed",[926,1440,1046],{"class":951},[926,1442,1443],{"class":951},">",[926,1445,965],{"class":951},[926,1447,1449,1452,1454,1456],{"class":928,"line":1448},19,[926,1450,1451],{"class":992},"  id",[926,1453,1036],{"class":951},[926,1455,1228],{"class":1177},[926,1457,1458],{"class":1092}," \u002F\u002F `app_` + random id: the OAuth `client_id`\n",[926,1460,1462,1464,1466,1468,1470],{"class":928,"line":1461},20,[926,1463,1223],{"class":992},[926,1465,1036],{"class":951},[926,1467,1228],{"class":1177},[926,1469,1198],{"class":951},[926,1471,1250],{"class":1177},[926,1473,1475,1478,1480,1483,1485],{"class":928,"line":1474},21,[926,1476,1477],{"class":992},"  ownerId",[926,1479,1036],{"class":951},[926,1481,1482],{"class":1177}," number",[926,1484,1198],{"class":951},[926,1486,1250],{"class":1177},[926,1488,1490,1492,1494],{"class":928,"line":1489},22,[926,1491,1365],{"class":992},[926,1493,1036],{"class":951},[926,1495,1370],{"class":1177},[926,1497,1499,1501,1503],{"class":928,"line":1498},23,[926,1500,1376],{"class":992},[926,1502,1036],{"class":951},[926,1504,1370],{"class":1177},[926,1506,1508,1511,1513],{"class":928,"line":1507},24,[926,1509,1510],{"class":992},"  createdAt",[926,1512,1036],{"class":951},[926,1514,1515],{"class":1177}," Date\n",[926,1517,1519,1522,1524],{"class":928,"line":1518},25,[926,1520,1521],{"class":992},"  updatedAt",[926,1523,1036],{"class":951},[926,1525,1515],{"class":1177},[926,1527,1529],{"class":928,"line":1528},26,[926,1530,1389],{"class":951},[594,1532,1533,1536,1537,1540,1541,1543,1544,867,1547,1550,1551,1553,1554,1557,1558,867,1561,1550,1564,1566],{},[637,1534,1535],{},"OAuthApplication"," never includes the secret hash. The admin API returns ",[637,1538,1539],{},"AdminApplicationDto",". That is an\n",[637,1542,1535],{}," with ISO date strings, ",[637,1545,1546],{},"activeGrants?",[637,1548,1549],{},"callbackUrl",", which is ",[637,1552,854],{}," without a website.\nCreating an application returns ",[637,1555,1556],{},"CreatedApplicationDto",". It adds ",[637,1559,1560],{},"clientId",[637,1562,1563],{},"clientSecret",[637,1565,854],{}," for a\npublic application.",[1568,1569,1571],"h3",{"id":1570},"configured-clients-in-your-own-queries","Configured clients in your own queries",[594,1573,1574,1577,1578,1581,1582,1585],{},[598,1575,1576],{"href":877},"Configured clients"," live in ",[637,1579,1580],{},"nuxt.config.ts",",\nwith a copy in the database. These helpers keep the two in step when you query ",[637,1583,1584],{},"oauth_applications"," yourself.",[613,1587,1588,1596],{},[616,1589,1590],{},[619,1591,1592,1594],{},[622,1593,624],{},[622,1595,627],{},[629,1597,1598,1608,1618,1628,1645],{},[619,1599,1600,1605],{},[634,1601,1602],{},[637,1603,1604],{},"ensureOAuthConfiguredApplications()",[634,1606,1607],{},"Creates the missing configured clients in the database, once per server instance.",[619,1609,1610,1615],{},[634,1611,1612],{},[637,1613,1614],{},"withOAuthConfiguredValues(row)",[634,1616,1617],{},"Applies the config-owned fields of a configured client to a row.",[619,1619,1620,1625],{},[634,1621,1622],{},[637,1623,1624],{},"isOAuthManagedApplication(application)",[634,1626,1627],{},"Whether config manages the application: a configured id and a public row.",[619,1629,1630,1635],{},[634,1631,1632],{},[637,1633,1634],{},"getOAuthConfiguredClients()",[634,1636,1637,1638,1641,1642,600],{},"The validated ",[637,1639,1640],{},"clients",", as ",[637,1643,1644],{},"OAuthNormalizedClient[]",[619,1646,1647,1652],{},[634,1648,1649],{},[637,1650,1651],{},"isOAuthConfiguredClient(id)",[634,1653,1654],{},"Whether an application is one of the configured clients.",[1656,1657,1658,1659,1661,1662,1664,1665,1668],"warning",{},"Call ",[637,1660,1604],{}," before you query ",[637,1663,1584],{}," yourself, and pass each row through\n",[637,1666,1667],{},"withOAuthConfiguredValues",". The lookups above already do both.",[1568,1670,1672],{"id":1671},"imports-outside-nitro","Imports outside Nitro",[594,1674,1675,1676,1036],{},"The pure helpers, the typed route paths and all types are importable from ",[637,1677,1678],{},"@frigear-nu\u002Foauth-server\u002Futils",[1680,1681,1682,1706,1718],"ul",{},[1683,1684,1685,1686,658,1689,658,1692,658,1695,658,1698,1701,1702,1705],"li",{},"the helpers ",[637,1687,1688],{},"matchesAllowedRoles",[637,1690,1691],{},"validateRedirectUri",[637,1693,1694],{},"parseScope",[637,1696,1697],{},"resolveScope",[637,1699,1700],{},"outdatedScopes"," and\n",[637,1703,1704],{},"normalizeWebsiteUrl",";",[1683,1707,1708,1709,658,1712,867,1715,1705],{},"the constants ",[637,1710,1711],{},"DEFAULT_SCOPES",[637,1713,1714],{},"OIDC_SCOPES",[637,1716,1717],{},"DEFAULT_CALLBACK_PATH",[1683,1719,1720,1721,867,1724,1727,1728,1731,1732,600],{},"the typed paths ",[637,1722,1723],{},"oauthServerApi",[637,1725,1726],{},"oidc",", also exported from ",[637,1729,1730],{},"@frigear-nu\u002Foauth-server\u002Fapi",". See\n",[598,1733,1735],{"href":1734},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients#routes","Routes",[602,1737,1739],{"id":1738},"scopes","Scopes",[594,1741,1742,1743,1746],{},"A scope is what an application asks a user to allow. It is also how an application's token is limited: a token can do\nonly what its scopes stand for. ",[598,1744,1745],{"href":68},"Auth and scopes"," explains the vocabulary. This\nsection covers the scopes the issuer accepts and how it decides which ones a user may grant.",[594,1748,1749],{},"The issuer accepts three kinds of scope:",[1680,1751,1752,1770,1798],{},[1683,1753,1754,646,1757,658,1760,658,1763,867,1766,1769],{},[846,1755,1756],{},"OpenID Connect scopes.",[637,1758,1759],{},"openid",[637,1761,1762],{},"profile",[637,1764,1765],{},"email",[637,1767,1768],{},"offline_access",". They stand for claims, not for\npermissions.",[1683,1771,1772,1775,1776,1778,1779,1782,1783,658,1786,1789,1790,1793,1794,1797],{},[846,1773,1774],{},"Permission scopes."," Every ",[598,1777,649],{"href":200}," a token may hold, such as ",[637,1780,1781],{},"tickets:read",",\nand its wildcard (",[637,1784,1785],{},"tickets:*",[637,1787,1788],{},"*","). Permissions marked ",[637,1791,1792],{},"sessionOnly"," never travel in a token, so they are not scopes.\nA wildcard never covers a ",[637,1795,1796],{},"sensitive"," permission: those are named one by one.",[1683,1799,1800,1803,1804,1807,1808,1701,1810,1813],{},[846,1801,1802],{},"Named scopes."," One word that stands for a group of permissions, such as ",[637,1805,1806],{},"tickets"," for ",[637,1809,1781],{},[637,1811,1812],{},"tickets:write",". They come from three places: your config, a module, or the database.",[594,1815,1816,1819,1820,600],{},[637,1817,1818],{},"scopes_supported"," in the discovery document lists all of them. A request for any other scope is answered with\n",[637,1821,1822],{},"invalid_scope",[1568,1824,1826],{"id":1825},"named-scopes-in-config","Named scopes in config",[594,1828,1829,1830,1833,1834,1837,1838,1841,1842,1845,1846,600],{},"Name a scope in ",[637,1831,1832],{},"oauthServer.scopes"," and list the permissions it stands for. The consent screen shows the ",[637,1835,1836],{},"label",", the\n",[637,1839,1840],{},"description"," and the ",[637,1843,1844],{},"icon",", and groups the scope under its ",[637,1847,1848],{},"service",[916,1850,1852],{"className":918,"code":1851,"filename":1580,"language":921,"meta":922,"style":922},"oauthServer: {\n  scopes: [\n    {\n      name: 'tickets',\n      label: 'Your tickets',\n      description: 'See your tickets and buy new ones',\n      icon: 'i-lucide-ticket',\n      permissions: ['tickets:read', 'tickets:write'],\n    },\n    \u002F\u002F A permission scope only needs a description on the consent screen\n    { name: 'vouchers:read', label: 'See your vouchers', service: 'Vouchers' },\n  ],\n}\n",[637,1853,1854,1863,1873,1878,1893,1909,1925,1940,1967,1972,1977,2025,2032],{"__ignoreMap":922},[926,1855,1856,1859,1861],{"class":928,"line":929},[926,1857,1858],{"class":1177},"oauthServer",[926,1860,1036],{"class":951},[926,1862,965],{"class":951},[926,1864,1865,1868,1870],{"class":928,"line":968},[926,1866,1867],{"class":1177},"  scopes",[926,1869,1036],{"class":951},[926,1871,1872],{"class":992}," [\n",[926,1874,1875],{"class":928,"line":1000},[926,1876,1877],{"class":951},"    {\n",[926,1879,1880,1883,1885,1887,1889,1891],{"class":928,"line":1030},[926,1881,1882],{"class":992},"      name",[926,1884,1036],{"class":951},[926,1886,1039],{"class":951},[926,1888,1806],{"class":1042},[926,1890,1046],{"class":951},[926,1892,1049],{"class":951},[926,1894,1895,1898,1900,1902,1905,1907],{"class":928,"line":1052},[926,1896,1897],{"class":992},"      label",[926,1899,1036],{"class":951},[926,1901,1039],{"class":951},[926,1903,1904],{"class":1042},"Your tickets",[926,1906,1046],{"class":951},[926,1908,1049],{"class":951},[926,1910,1911,1914,1916,1918,1921,1923],{"class":928,"line":1069},[926,1912,1913],{"class":992},"      description",[926,1915,1036],{"class":951},[926,1917,1039],{"class":951},[926,1919,1920],{"class":1042},"See your tickets and buy new ones",[926,1922,1046],{"class":951},[926,1924,1049],{"class":951},[926,1926,1927,1930,1932,1934,1936,1938],{"class":928,"line":1096},[926,1928,1929],{"class":992},"      icon",[926,1931,1036],{"class":951},[926,1933,1039],{"class":951},[926,1935,493],{"class":1042},[926,1937,1046],{"class":951},[926,1939,1049],{"class":951},[926,1941,1942,1945,1947,1949,1951,1953,1955,1957,1959,1961,1963,1965],{"class":928,"line":1117},[926,1943,1944],{"class":992},"      permissions",[926,1946,1036],{"class":951},[926,1948,1077],{"class":992},[926,1950,1046],{"class":951},[926,1952,1781],{"class":1042},[926,1954,1046],{"class":951},[926,1956,1010],{"class":951},[926,1958,1039],{"class":951},[926,1960,1812],{"class":1042},[926,1962,1046],{"class":951},[926,1964,1087],{"class":992},[926,1966,1049],{"class":951},[926,1968,1969],{"class":928,"line":1127},[926,1970,1971],{"class":951},"    },\n",[926,1973,1974],{"class":928,"line":1159},[926,1975,1976],{"class":1092},"    \u002F\u002F A permission scope only needs a description on the consent screen\n",[926,1978,1979,1982,1985,1987,1989,1992,1994,1996,1999,2001,2003,2006,2008,2010,2013,2015,2017,2020,2022],{"class":928,"line":1320},[926,1980,1981],{"class":951},"    {",[926,1983,1984],{"class":992}," name",[926,1986,1036],{"class":951},[926,1988,1039],{"class":951},[926,1990,1991],{"class":1042},"vouchers:read",[926,1993,1046],{"class":951},[926,1995,1010],{"class":951},[926,1997,1998],{"class":992}," label",[926,2000,1036],{"class":951},[926,2002,1039],{"class":951},[926,2004,2005],{"class":1042},"See your vouchers",[926,2007,1046],{"class":951},[926,2009,1010],{"class":951},[926,2011,2012],{"class":992}," service",[926,2014,1036],{"class":951},[926,2016,1039],{"class":951},[926,2018,2019],{"class":1042},"Vouchers",[926,2021,1046],{"class":951},[926,2023,2024],{"class":951}," },\n",[926,2026,2027,2030],{"class":928,"line":1335},[926,2028,2029],{"class":992},"  ]",[926,2031,1049],{"class":951},[926,2033,2034],{"class":928,"line":1348},[926,2035,1389],{"class":951},[594,2037,2038,2039,2042,2043,2046],{},"A module offers a named scope from its ",[637,2040,2041],{},"setup()",". This is the same registry, served to ",[598,2044,2045],{"href":87},"API tokens","\ntoo:",[916,2048,2050],{"className":918,"code":2049,"language":921,"meta":922,"style":922},"import { addScope } from '@frigear-nu\u002Fauth\u002Futils'\n\naddScope(nuxt, { name: 'inventory', label: 'The inventory', service: 'Inventory', permissions: ['inventory:read'] })\n",[637,2051,2052,2075,2079],{"__ignoreMap":922},[926,2053,2054,2057,2059,2062,2064,2067,2069,2072],{"class":928,"line":929},[926,2055,2056],{"class":932},"import",[926,2058,974],{"class":951},[926,2060,2061],{"class":943}," addScope",[926,2063,980],{"class":951},[926,2065,2066],{"class":932}," from",[926,2068,1039],{"class":951},[926,2070,2071],{"class":1042},"@frigear-nu\u002Fauth\u002Futils",[926,2073,2074],{"class":951},"'\n",[926,2076,2077],{"class":928,"line":968},[926,2078,1396],{"emptyLinePlaceholder":1395},[926,2080,2081,2084,2087,2089,2091,2093,2095,2097,2100,2102,2104,2106,2108,2110,2113,2115,2117,2119,2121,2123,2126,2128,2130,2133,2135,2137,2139,2142,2144,2147,2149],{"class":928,"line":1000},[926,2082,2083],{"class":939},"addScope",[926,2085,2086],{"class":943},"(nuxt",[926,2088,1010],{"class":951},[926,2090,974],{"class":951},[926,2092,1984],{"class":992},[926,2094,1036],{"class":951},[926,2096,1039],{"class":951},[926,2098,2099],{"class":1042},"inventory",[926,2101,1046],{"class":951},[926,2103,1010],{"class":951},[926,2105,1998],{"class":992},[926,2107,1036],{"class":951},[926,2109,1039],{"class":951},[926,2111,2112],{"class":1042},"The inventory",[926,2114,1046],{"class":951},[926,2116,1010],{"class":951},[926,2118,2012],{"class":992},[926,2120,1036],{"class":951},[926,2122,1039],{"class":951},[926,2124,2125],{"class":1042},"Inventory",[926,2127,1046],{"class":951},[926,2129,1010],{"class":951},[926,2131,2132],{"class":992}," permissions",[926,2134,1036],{"class":951},[926,2136,1077],{"class":943},[926,2138,1046],{"class":951},[926,2140,2141],{"class":1042},"inventory:read",[926,2143,1046],{"class":951},[926,2145,2146],{"class":943},"] ",[926,2148,1162],{"class":951},[926,2150,997],{"class":943},[1680,2152,2153,2165,2178],{},[1683,2154,2155,2161,2162,2164],{},[846,2156,2157,2158,2160],{},"A named scope has no sensitive or ",[637,2159,1792],{}," permissions."," Those are rejected when the app builds. A scope may\nhave no permissions at all, as ",[637,2163,1759],{}," has none.",[1683,2166,2167,2173,2174,2177],{},[846,2168,2169,2170,2172],{},"Permission names in ",[637,2171,1738],{}," only describe."," They must name a permission that exists, or the build fails. Adding\n",[637,2175,2176],{},"permissions"," to one is an error.",[1683,2179,2180,2183],{},[846,2181,2182],{},"One name, one scope."," Two entries with the same name are merged: their permissions are added together and the later label and description win. So you can reword a module's scope.",[1568,2185,2187],{"id":2186},"named-scopes-in-the-database","Named scopes in the database",[594,2189,2190,2191,2194,2195,2198,2199,2201,2202,2206,2207,2210],{},"Staff can define a named scope without a deploy. Open ",[846,2192,2193],{},"OAuth scopes"," in the admin area (",[637,2196,2197],{},"\u002Fadmin\u002Foauth\u002Fscopes",", with\n",[598,2200,217],{"href":351},"), or call the ",[598,2203,2205],{"href":2204},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients#module-api","scopes API",".\nA database scope is stored in ",[637,2208,2209],{},"oauth_scopes"," and has a name, a label, a description, an icon and its permissions.",[1680,2212,2213,2231,2244,2254],{},[1683,2214,2215,2218,2219,658,2221,867,2224,2227,2228,2230],{},[846,2216,2217],{},"A name is one lower-case word."," Letters, digits, ",[637,2220,600],{},[637,2222,2223],{},"_",[637,2225,2226],{},"-",". It cannot be an OIDC scope or the name of a\nconfig or module scope. Those answer ",[637,2229,874],{},", because config is read-only here.",[1683,2232,2233,2236,2237,662,2239,2241,2242,600],{},[846,2234,2235],{},"Permissions are checked when you save."," Each must exist and must not be ",[637,2238,1796],{},[637,2240,1792],{},", or the save\nanswers ",[637,2243,890],{},[1683,2245,2246,2249,2250,2253],{},[846,2247,2248],{},"Growth asks users again."," When an edit makes the scope stand for more permissions than before, its ",[637,2251,2252],{},"version"," goes\nup. A user who consented to the old version gets tokens without the scope until they approve it again.",[1683,2255,2256,2259,2260,2262,2263,2266,2267,2269],{},[846,2257,2258],{},"Turning one off."," Set ",[637,2261,1429],{}," to ",[637,2264,2265],{},"false"," to stop accepting a scope without losing it. Deleting a scope makes\nnew requests for it fail with ",[637,2268,1822],{},", and existing sign-ins lose it at their next refresh.",[594,2271,2272,2273,2276,2277,2279],{},"Config and module scopes show in the admin list with a ",[846,2274,2275],{},"Managed"," badge. Anyone with ",[637,2278,645],{}," can read them,\nand nobody can edit them there.",[916,2281,2283],{"className":918,"code":2282,"language":921,"meta":922,"style":922},"\u002F\u002F Your own staff route\nawait createOAuthScope({ name: 'reports', label: 'Your reports', permissions: ['reports:read'] })\nawait updateOAuthScope('reports', { permissions: ['reports:read', 'reports:export'] }) \u002F\u002F version goes up\n",[637,2284,2285,2290,2348],{"__ignoreMap":922},[926,2286,2287],{"class":928,"line":929},[926,2288,2289],{"class":1092},"\u002F\u002F Your own staff route\n",[926,2291,2292,2295,2298,2300,2303,2305,2307,2309,2312,2314,2316,2318,2320,2322,2325,2327,2329,2331,2333,2335,2337,2340,2342,2344,2346],{"class":928,"line":968},[926,2293,2294],{"class":932},"await",[926,2296,2297],{"class":939}," createOAuthScope",[926,2299,944],{"class":943},[926,2301,2302],{"class":951},"{",[926,2304,1984],{"class":992},[926,2306,1036],{"class":951},[926,2308,1039],{"class":951},[926,2310,2311],{"class":1042},"reports",[926,2313,1046],{"class":951},[926,2315,1010],{"class":951},[926,2317,1998],{"class":992},[926,2319,1036],{"class":951},[926,2321,1039],{"class":951},[926,2323,2324],{"class":1042},"Your reports",[926,2326,1046],{"class":951},[926,2328,1010],{"class":951},[926,2330,2132],{"class":992},[926,2332,1036],{"class":951},[926,2334,1077],{"class":943},[926,2336,1046],{"class":951},[926,2338,2339],{"class":1042},"reports:read",[926,2341,1046],{"class":951},[926,2343,2146],{"class":943},[926,2345,1162],{"class":951},[926,2347,997],{"class":943},[926,2349,2350,2352,2355,2357,2359,2361,2363,2365,2367,2369,2371,2373,2375,2377,2379,2381,2383,2386,2388,2390,2392,2395],{"class":928,"line":1000},[926,2351,2294],{"class":932},[926,2353,2354],{"class":939}," updateOAuthScope",[926,2356,944],{"class":943},[926,2358,1046],{"class":951},[926,2360,2311],{"class":1042},[926,2362,1046],{"class":951},[926,2364,1010],{"class":951},[926,2366,974],{"class":951},[926,2368,2132],{"class":992},[926,2370,1036],{"class":951},[926,2372,1077],{"class":943},[926,2374,1046],{"class":951},[926,2376,2339],{"class":1042},[926,2378,1046],{"class":951},[926,2380,1010],{"class":951},[926,2382,1039],{"class":951},[926,2384,2385],{"class":1042},"reports:export",[926,2387,1046],{"class":951},[926,2389,2146],{"class":943},[926,2391,1162],{"class":951},[926,2393,2394],{"class":943},") ",[926,2396,2397],{"class":1092},"\u002F\u002F version goes up\n",[1568,2399,2401],{"id":2400},"which-scopes-a-user-may-grant","Which scopes a user may grant",[594,2403,2404],{},"A scope reaches an application only when the signed-in user may hand it over. A scope is grantable when all of these\nhold:",[2406,2407,2408,2414,2425,2437],"ol",{},[1683,2409,2410,2413],{},[846,2411,2412],{},"It is known."," An OIDC scope, a permission scope or a named scope.",[1683,2415,2416,2419,2420,2424],{},[846,2417,2418],{},"It is within the user's cap."," See ",[598,2421,2423],{"href":2422},"#capping-scopes-by-role","Capping scopes by role",". No cap is set by default.",[1683,2426,2427,2430,2431,2434,2435,600],{},[846,2428,2429],{},"It is not a wildcard, unless the application allows it."," Turn on ",[637,2432,2433],{},"wildcardScopes"," for the application, or for a\n",[598,2436,878],{"href":877},[1683,2438,2439,2442,2443,2448,2449,2451],{},[846,2440,2441],{},"The user may delegate every permission it stands for."," The user's roles, and the\n",[598,2444,2445],{"href":210},[637,2446,2447],{},"authorization:before"," hook, are checked. ",[598,2450,209],{"href":210},"\nare not, because they decide on a record and a scope has none.",[594,2453,2454,2455,2458,2459,2462,2463,2465,2466,2468],{},"An application can never get more than its user holds. For example, ",[637,2456,2457],{},"users:read"," is held by staff only, so a plain user\nwho approves ",[637,2460,2461],{},"scope=openid users:read"," gets a token with ",[637,2464,1759],{}," only, and the consent screen shows ",[637,2467,2457],{}," as\nunavailable.",[1568,2470,2423],{"id":2471},"capping-scopes-by-role",[594,2473,2474,2475,2478],{},"Without a cap, a user may hand over everything they may do. ",[637,2476,2477],{},"settings.roleScopes"," is a stricter ceiling: it names the\nmost each role may grant to any application.",[916,2480,2482],{"className":918,"code":2481,"filename":1580,"language":921,"meta":922,"style":922},"oauthServer: {\n  settings: {\n    roleScopes: {\n      admin: ['*'],\n      user: ['openid', 'profile', 'email', 'tickets'],\n    },\n  },\n}\n",[637,2483,2484,2492,2501,2510,2529,2572,2576,2581],{"__ignoreMap":922},[926,2485,2486,2488,2490],{"class":928,"line":929},[926,2487,1858],{"class":1177},[926,2489,1036],{"class":951},[926,2491,965],{"class":951},[926,2493,2494,2497,2499],{"class":928,"line":968},[926,2495,2496],{"class":1177},"  settings",[926,2498,1036],{"class":951},[926,2500,965],{"class":951},[926,2502,2503,2506,2508],{"class":928,"line":1000},[926,2504,2505],{"class":1177},"    roleScopes",[926,2507,1036],{"class":951},[926,2509,965],{"class":951},[926,2511,2512,2515,2517,2519,2521,2523,2525,2527],{"class":928,"line":1030},[926,2513,2514],{"class":1177},"      admin",[926,2516,1036],{"class":951},[926,2518,1077],{"class":992},[926,2520,1046],{"class":951},[926,2522,1788],{"class":1042},[926,2524,1046],{"class":951},[926,2526,1087],{"class":992},[926,2528,1049],{"class":951},[926,2530,2531,2534,2536,2538,2540,2542,2544,2546,2548,2550,2552,2554,2556,2558,2560,2562,2564,2566,2568,2570],{"class":928,"line":1052},[926,2532,2533],{"class":1177},"      user",[926,2535,1036],{"class":951},[926,2537,1077],{"class":992},[926,2539,1046],{"class":951},[926,2541,1759],{"class":1042},[926,2543,1046],{"class":951},[926,2545,1010],{"class":951},[926,2547,1039],{"class":951},[926,2549,1762],{"class":1042},[926,2551,1046],{"class":951},[926,2553,1010],{"class":951},[926,2555,1039],{"class":951},[926,2557,1765],{"class":1042},[926,2559,1046],{"class":951},[926,2561,1010],{"class":951},[926,2563,1039],{"class":951},[926,2565,1806],{"class":1042},[926,2567,1046],{"class":951},[926,2569,1087],{"class":992},[926,2571,1049],{"class":951},[926,2573,2574],{"class":928,"line":1069},[926,2575,1971],{"class":951},[926,2577,2578],{"class":928,"line":1096},[926,2579,2580],{"class":951},"  },\n",[926,2582,2583],{"class":928,"line":1117},[926,2584,1389],{"class":951},[1680,2586,2587,2593,2601,2609],{},[1683,2588,2589,2592],{},[846,2590,2591],{},"Unset means no cap."," Only the user's permissions limit what they grant.",[1683,2594,2595,2598,2599,600],{},[846,2596,2597],{},"Set means a whitelist."," A user's cap is the union of the lists of all their effective roles, inheritance included.\nA role that is not listed adds nothing, so a user with only unlisted roles may grant nothing, not even ",[637,2600,1759],{},[1683,2602,2603,646,2606,2608],{},[846,2604,2605],{},"Entries are scopes, permissions or wildcards.",[637,2607,1788],{}," covers every non-sensitive permission and the OIDC scopes. A\nsensitive permission is inside the cap only when an entry names it.",[1683,2610,2611,2614,2615,2618],{},[846,2612,2613],{},"Role names are checked at build time."," A name that is not a configured role fails the build. During\n",[637,2616,2617],{},"nuxt prepare"," it logs a warning instead.",[594,2620,2621],{},"The cap and the user's permissions both apply. The cap can only narrow what the user may do, never widen it.",[1568,2623,2625],{"id":2624},"what-the-user-sees-and-what-the-application-gets","What the user sees and what the application gets",[1680,2627,2628,2652,2665,2673],{},[1683,2629,2630,2636,2637,2640,2641,2644,2645,2648,2649,600],{},[846,2631,2632,2633,600],{},"At ",[637,2634,2635],{},"\u002Foauth\u002Fauthorize"," A scope this server does not know sends the user straight back to the application with\n",[637,2638,2639],{},"error=invalid_scope"," and your ",[637,2642,2643],{},"state",". A request with no ",[637,2646,2647],{},"scope"," gets ",[637,2650,2651],{},"openid profile email",[1683,2653,2654,2657,2658,2660,2661,2664],{},[846,2655,2656],{},"On the consent screen."," Scopes the user may grant are listed, grouped by ",[637,2659,1848],{},". Scopes the user may not grant,\nbecause they lack the permission or exceed their cap, show under ",[846,2662,2663],{},"Not available to your account",". Approving drops them, and the\napplication still gets a token.",[1683,2666,2667,646,2670,2672],{},[846,2668,2669],{},"In the token response.",[637,2671,2647],{}," is exactly what was granted, not what was asked for. An application reads it to see\nwhat it got.",[1683,2674,2675,2678,2679,2681],{},[846,2676,2677],{},"On refresh."," Scopes are checked again, silently. A user who lost a permission, or whose role no longer allows a\nscope, gets tokens without it. The refresh does not fail with ",[637,2680,1822],{},". The refresh token keeps the scope, so it\ncomes back when the user gets the permission back.",[1568,2683,2685,2686,2688],{"id":2684},"the-frozen-permissions-claim","The frozen ",[637,2687,2176],{}," claim",[594,2690,2691,2692,2694,2695,2697],{},"An access token carries a ",[637,2693,2176],{}," claim next to ",[637,2696,2647],{},". It is what the granted scopes stand for, limited to what the\nuser may delegate at the moment the token is issued. It is then frozen:",[916,2699,2703],{"className":2700,"code":2701,"language":2702,"meta":922,"style":922},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"sub\": \"42\",\n  \"client_id\": \"app_ab12\",\n  \"scope\": \"openid tickets\",\n  \"permissions\": [\"tickets:read\", \"tickets:write\"]\n}\n","json",[637,2704,2705,2709,2732,2751,2770,2799],{"__ignoreMap":922},[926,2706,2707],{"class":928,"line":929},[926,2708,1027],{"class":951},[926,2710,2711,2714,2717,2720,2722,2725,2728,2730],{"class":928,"line":968},[926,2712,2713],{"class":951},"  \"",[926,2715,2716],{"class":947},"sub",[926,2718,2719],{"class":951},"\"",[926,2721,1036],{"class":951},[926,2723,2724],{"class":951}," \"",[926,2726,2727],{"class":1042},"42",[926,2729,2719],{"class":951},[926,2731,1049],{"class":951},[926,2733,2734,2736,2738,2740,2742,2744,2747,2749],{"class":928,"line":1000},[926,2735,2713],{"class":951},[926,2737,902],{"class":947},[926,2739,2719],{"class":951},[926,2741,1036],{"class":951},[926,2743,2724],{"class":951},[926,2745,2746],{"class":1042},"app_ab12",[926,2748,2719],{"class":951},[926,2750,1049],{"class":951},[926,2752,2753,2755,2757,2759,2761,2763,2766,2768],{"class":928,"line":1030},[926,2754,2713],{"class":951},[926,2756,2647],{"class":947},[926,2758,2719],{"class":951},[926,2760,1036],{"class":951},[926,2762,2724],{"class":951},[926,2764,2765],{"class":1042},"openid tickets",[926,2767,2719],{"class":951},[926,2769,1049],{"class":951},[926,2771,2772,2774,2776,2778,2780,2782,2784,2786,2788,2790,2792,2794,2796],{"class":928,"line":1052},[926,2773,2713],{"class":951},[926,2775,2176],{"class":947},[926,2777,2719],{"class":951},[926,2779,1036],{"class":951},[926,2781,1077],{"class":951},[926,2783,2719],{"class":951},[926,2785,1781],{"class":1042},[926,2787,2719],{"class":951},[926,2789,1010],{"class":951},[926,2791,2724],{"class":951},[926,2793,1812],{"class":1042},[926,2795,2719],{"class":951},[926,2797,2798],{"class":951},"]\n",[926,2800,2801],{"class":928,"line":1069},[926,2802,1389],{"class":951},[594,2804,2805,2806,2808],{},"Your API uses the frozen set to decide whether a token may do something, so it never has to look at scopes, named\nscopes or the ",[637,2807,2209],{}," table. A user who loses a permission after the token was issued is stopped on every\nrequest, because the user's roles are checked as well. A user who gains one does not widen an old token.",[594,2810,2811,2812,2815,2816,2818],{},"The claim is a reserved claim: ",[637,2813,2814],{},"oauth:claims"," cannot change it. A token issued before permissions were frozen has no claim.\nYour API then expands its ",[637,2817,2647],{}," against the user's current permissions. The same frozen set is stored for API tokens.",[602,2820,2822],{"id":2821},"api-access","API access",[594,2824,2825,2826,2829,2830,2833,2834,2836,2837,2840],{},"An OAuth application can call this app's API as the user who signed in to it. It sends the access token as\n",[637,2827,2828],{},"Authorization: Bearer \u003Caccess_token>",". Routes that call ",[637,2831,2832],{},"authorize(event, permission)"," (see\n",[598,2835,168],{"href":176},") then see that user. ",[637,2838,2839],{},"principal.client.id"," names the application.",[594,2842,2843,2844,646,2846,2849,2850,600],{},"A route allows the call only when the permission is in the token's frozen ",[637,2845,2176],{},[846,2847,2848],{},"and"," the user's roles still\ngrant it. Otherwise it answers ",[637,2851,2852],{},"403 insufficient_scope",[1568,2854,2856],{"id":2855},"how-scopes-read-on-the-consent-screen","How scopes read on the consent screen",[594,2858,2859,658,2861,867,2863,2865,2866,2868,2869,2872,2873,2875,2876,2879],{},[637,2860,1759],{},[637,2862,1762],{},[637,2864,1765],{}," have built-in, translated labels. Describe your own scopes in ",[637,2867,1832],{},", as\n",[598,2870,1826],{"href":2871},"#named-scopes-in-config"," shows. Without a description, a permission scope shows the\n",[637,2874,1840],{}," it was registered with through ",[637,2877,2878],{},"addPermission",", and any other scope shows its name.",[1568,2881,2883],{"id":2882},"what-an-access-token-cannot-do","What an access token cannot do",[1680,2885,2886,2900,2912],{},[1683,2887,2888,2891,2892,2894,2895,2897,2898,600],{},[846,2889,2890],{},"Live checks."," On every call the application must still be active. The user must still exist and pass the\napplication's ",[637,2893,794],{}," check and the ",[637,2896,798],{}," hook. Otherwise the call answers ",[637,2899,657],{},[1683,2901,2902,2905,2906,2908,2909,2911],{},[846,2903,2904],{},"No account changes."," Permissions that are ",[637,2907,1792],{},", such as changing a password or a passkey, never reach a\ntoken. A route that needs one answers ",[637,2910,665],{}," to an access token.",[1683,2913,2914,2917,2918,2921],{},[846,2915,2916],{},"Access tokens only."," ID tokens (",[637,2919,2920],{},"typ: JWT",") are not accepted as credentials.",[602,2923,2925],{"id":2924},"one-app-for-every-service","One app for every service",[594,2927,2928,2929,2931,2932,2935],{},"One mobile app can sign in once at this issuer and call several services that accept its access tokens with\n",[598,2930,241],{"href":242},". ",[598,2933,81],{"href":2934},"\u002Fguide\u002Fconcepts\u002Fidentity#one-app-for-every-service","\nshows the whole flow. This section covers the issuer's config.",[594,2937,2938,2939,2942,2943,2946,2947,867,2950,2953],{},"Set ",[637,2940,2941],{},"issuer"," (or ",[637,2944,2945],{},"NUXT_OAUTH_SERVER_ISSUER",") to the public origin in production. Without it the issuer is the origin of the\nrequest, ",[637,2948,2949],{},"X-Forwarded-Host",[637,2951,2952],{},"X-Forwarded-Proto"," are ignored, and a warning is logged once outside development.",[916,2955,2957],{"className":918,"code":2956,"filename":1580,"language":921,"meta":922,"style":922},"oauthServer: {\n  issuer: 'https:\u002F\u002Ffrigear.nu',\n  \u002F\u002F Every access token is for all services, and short-lived: services cannot revoke it.\n  accessTokenAudience: 'frigear-services',\n  accessTokenTtl: 600,\n  scopes: [\n    { name: 'vouchers:read', description: 'See your vouchers', service: 'Vouchers' },\n    { name: 'vouchers:write', description: 'Redeem your vouchers', service: 'Vouchers' },\n    { name: 'shop:order', description: 'Place orders in the shop', service: 'Shop' },\n    { name: 'inventory:read', description: 'See the inventory', service: 'Inventory' },\n  ],\n  cors: { origins: ['capacitor:\u002F\u002Flocalhost', 'https:\u002F\u002Flocalhost'] },\n}\n",[637,2958,2959,2967,2983,2988,3004,3017,3025,3066,3108,3150,3191,3197,3234],{"__ignoreMap":922},[926,2960,2961,2963,2965],{"class":928,"line":929},[926,2962,1858],{"class":1177},[926,2964,1036],{"class":951},[926,2966,965],{"class":951},[926,2968,2969,2972,2974,2976,2979,2981],{"class":928,"line":968},[926,2970,2971],{"class":1177},"  issuer",[926,2973,1036],{"class":951},[926,2975,1039],{"class":951},[926,2977,2978],{"class":1042},"https:\u002F\u002Ffrigear.nu",[926,2980,1046],{"class":951},[926,2982,1049],{"class":951},[926,2984,2985],{"class":928,"line":1000},[926,2986,2987],{"class":1092},"  \u002F\u002F Every access token is for all services, and short-lived: services cannot revoke it.\n",[926,2989,2990,2993,2995,2997,3000,3002],{"class":928,"line":1030},[926,2991,2992],{"class":1177},"  accessTokenAudience",[926,2994,1036],{"class":951},[926,2996,1039],{"class":951},[926,2998,2999],{"class":1042},"frigear-services",[926,3001,1046],{"class":951},[926,3003,1049],{"class":951},[926,3005,3006,3009,3011,3015],{"class":928,"line":1052},[926,3007,3008],{"class":1177},"  accessTokenTtl",[926,3010,1036],{"class":951},[926,3012,3014],{"class":3013},"sbssI"," 600",[926,3016,1049],{"class":951},[926,3018,3019,3021,3023],{"class":928,"line":1069},[926,3020,1867],{"class":1177},[926,3022,1036],{"class":951},[926,3024,1872],{"class":992},[926,3026,3027,3029,3031,3033,3035,3037,3039,3041,3044,3046,3048,3050,3052,3054,3056,3058,3060,3062,3064],{"class":928,"line":1096},[926,3028,1981],{"class":951},[926,3030,1984],{"class":992},[926,3032,1036],{"class":951},[926,3034,1039],{"class":951},[926,3036,1991],{"class":1042},[926,3038,1046],{"class":951},[926,3040,1010],{"class":951},[926,3042,3043],{"class":992}," description",[926,3045,1036],{"class":951},[926,3047,1039],{"class":951},[926,3049,2005],{"class":1042},[926,3051,1046],{"class":951},[926,3053,1010],{"class":951},[926,3055,2012],{"class":992},[926,3057,1036],{"class":951},[926,3059,1039],{"class":951},[926,3061,2019],{"class":1042},[926,3063,1046],{"class":951},[926,3065,2024],{"class":951},[926,3067,3068,3070,3072,3074,3076,3079,3081,3083,3085,3087,3089,3092,3094,3096,3098,3100,3102,3104,3106],{"class":928,"line":1117},[926,3069,1981],{"class":951},[926,3071,1984],{"class":992},[926,3073,1036],{"class":951},[926,3075,1039],{"class":951},[926,3077,3078],{"class":1042},"vouchers:write",[926,3080,1046],{"class":951},[926,3082,1010],{"class":951},[926,3084,3043],{"class":992},[926,3086,1036],{"class":951},[926,3088,1039],{"class":951},[926,3090,3091],{"class":1042},"Redeem your vouchers",[926,3093,1046],{"class":951},[926,3095,1010],{"class":951},[926,3097,2012],{"class":992},[926,3099,1036],{"class":951},[926,3101,1039],{"class":951},[926,3103,2019],{"class":1042},[926,3105,1046],{"class":951},[926,3107,2024],{"class":951},[926,3109,3110,3112,3114,3116,3118,3121,3123,3125,3127,3129,3131,3134,3136,3138,3140,3142,3144,3146,3148],{"class":928,"line":1127},[926,3111,1981],{"class":951},[926,3113,1984],{"class":992},[926,3115,1036],{"class":951},[926,3117,1039],{"class":951},[926,3119,3120],{"class":1042},"shop:order",[926,3122,1046],{"class":951},[926,3124,1010],{"class":951},[926,3126,3043],{"class":992},[926,3128,1036],{"class":951},[926,3130,1039],{"class":951},[926,3132,3133],{"class":1042},"Place orders in the shop",[926,3135,1046],{"class":951},[926,3137,1010],{"class":951},[926,3139,2012],{"class":992},[926,3141,1036],{"class":951},[926,3143,1039],{"class":951},[926,3145,1043],{"class":1042},[926,3147,1046],{"class":951},[926,3149,2024],{"class":951},[926,3151,3152,3154,3156,3158,3160,3162,3164,3166,3168,3170,3172,3175,3177,3179,3181,3183,3185,3187,3189],{"class":928,"line":1159},[926,3153,1981],{"class":951},[926,3155,1984],{"class":992},[926,3157,1036],{"class":951},[926,3159,1039],{"class":951},[926,3161,2141],{"class":1042},[926,3163,1046],{"class":951},[926,3165,1010],{"class":951},[926,3167,3043],{"class":992},[926,3169,1036],{"class":951},[926,3171,1039],{"class":951},[926,3173,3174],{"class":1042},"See the inventory",[926,3176,1046],{"class":951},[926,3178,1010],{"class":951},[926,3180,2012],{"class":992},[926,3182,1036],{"class":951},[926,3184,1039],{"class":951},[926,3186,2125],{"class":1042},[926,3188,1046],{"class":951},[926,3190,2024],{"class":951},[926,3192,3193,3195],{"class":928,"line":1320},[926,3194,2029],{"class":992},[926,3196,1049],{"class":951},[926,3198,3199,3202,3204,3206,3209,3211,3213,3215,3218,3220,3222,3224,3227,3229,3231],{"class":928,"line":1335},[926,3200,3201],{"class":1177},"  cors",[926,3203,1036],{"class":951},[926,3205,974],{"class":951},[926,3207,3208],{"class":1177}," origins",[926,3210,1036],{"class":951},[926,3212,1077],{"class":992},[926,3214,1046],{"class":951},[926,3216,3217],{"class":1042},"capacitor:\u002F\u002Flocalhost",[926,3219,1046],{"class":951},[926,3221,1010],{"class":951},[926,3223,1039],{"class":951},[926,3225,3226],{"class":1042},"https:\u002F\u002Flocalhost",[926,3228,1046],{"class":951},[926,3230,2146],{"class":992},[926,3232,3233],{"class":951},"},\n",[926,3235,3236],{"class":928,"line":1348},[926,3237,1389],{"class":951},[1568,3239,3241],{"id":3240},"the-scope-registry","The scope registry",[594,3243,3244,3245,2942,3247,3250,3251,1641,3253,3256,3257,3259],{},"Every service's permissions must exist on the issuer. Register them with ",[637,3246,2878],{},[637,3248,3249],{},"authentication.permissions",")\nand describe them in ",[637,3252,1832],{},[598,3254,1739],{"href":3255},"#scopes"," shows. ",[637,3258,1738],{}," also names groups of them. Entries with the\nsame name are merged, so you can reword a module's scope.",[1568,3261,3263],{"id":3262},"keeping-scopes-for-staff","Keeping scopes for staff",[594,3265,3266,3267,3269,3270,3273],{},"There is no staff-only flag on a scope. A scope can only be granted by a user who holds every permission it stands for,\nso give the permission only to the roles that should have it. Here ",[637,3268,2141],{}," is held by the ",[637,3271,3272],{},"staff"," role alone:",[916,3275,3277],{"className":918,"code":3276,"filename":1580,"language":921,"meta":922,"style":922},"authorization: {\n  roles: {\n    staff: { label: 'Staff', grant: ['inventory:read'] },\n  },\n},\noauthServer: {\n  scopes: [{ name: 'inventory:read', description: 'See the inventory', service: 'Inventory' }],\n},\n",[637,3278,3279,3288,3297,3336,3340,3344,3352,3402],{"__ignoreMap":922},[926,3280,3281,3284,3286],{"class":928,"line":929},[926,3282,3283],{"class":1177},"authorization",[926,3285,1036],{"class":951},[926,3287,965],{"class":951},[926,3289,3290,3293,3295],{"class":928,"line":968},[926,3291,3292],{"class":1177},"  roles",[926,3294,1036],{"class":951},[926,3296,965],{"class":951},[926,3298,3299,3302,3304,3306,3308,3310,3312,3315,3317,3319,3322,3324,3326,3328,3330,3332,3334],{"class":928,"line":1000},[926,3300,3301],{"class":1177},"    staff",[926,3303,1036],{"class":951},[926,3305,974],{"class":951},[926,3307,1998],{"class":1177},[926,3309,1036],{"class":951},[926,3311,1039],{"class":951},[926,3313,3314],{"class":1042},"Staff",[926,3316,1046],{"class":951},[926,3318,1010],{"class":951},[926,3320,3321],{"class":1177}," grant",[926,3323,1036],{"class":951},[926,3325,1077],{"class":992},[926,3327,1046],{"class":951},[926,3329,2141],{"class":1042},[926,3331,1046],{"class":951},[926,3333,2146],{"class":992},[926,3335,3233],{"class":951},[926,3337,3338],{"class":928,"line":1030},[926,3339,2580],{"class":951},[926,3341,3342],{"class":928,"line":1052},[926,3343,3233],{"class":951},[926,3345,3346,3348,3350],{"class":928,"line":1069},[926,3347,1858],{"class":1177},[926,3349,1036],{"class":951},[926,3351,965],{"class":951},[926,3353,3354,3356,3358,3360,3362,3364,3366,3368,3370,3372,3374,3376,3378,3380,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400],{"class":928,"line":1096},[926,3355,1867],{"class":1177},[926,3357,1036],{"class":951},[926,3359,1077],{"class":992},[926,3361,2302],{"class":951},[926,3363,1984],{"class":992},[926,3365,1036],{"class":951},[926,3367,1039],{"class":951},[926,3369,2141],{"class":1042},[926,3371,1046],{"class":951},[926,3373,1010],{"class":951},[926,3375,3043],{"class":992},[926,3377,1036],{"class":951},[926,3379,1039],{"class":951},[926,3381,3174],{"class":1042},[926,3383,1046],{"class":951},[926,3385,1010],{"class":951},[926,3387,2012],{"class":992},[926,3389,1036],{"class":951},[926,3391,1039],{"class":951},[926,3393,2125],{"class":1042},[926,3395,1046],{"class":951},[926,3397,980],{"class":951},[926,3399,1087],{"class":992},[926,3401,1049],{"class":951},[926,3403,3404],{"class":928,"line":1117},[926,3405,3233],{"class":951},[594,3407,3408,3409,3411,3412,3417],{},"Everyone else loses the scope at consent, at the code exchange and at every refresh. Their token simply lacks it, and the\nservice answers ",[637,3410,2852],{},". Use ",[598,3413,3414],{"href":2422},[637,3415,3416],{},"roleScopes"," to also cap what a role may hand to\nan application.",[1568,3419,3421],{"id":3420},"consent-and-claims","Consent and claims",[594,3423,3424,3425,3427,3428,3431,3432,3437,3438,600],{},"The consent screen groups the requested scopes by ",[637,3426,1848],{},", with the account's own scopes first. The mobile app's role\nguards see a ",[637,3429,3430],{},"role"," only when the ID token carries one. Add it with the\n",[598,3433,3435],{"href":3434},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Fhooks#enriching-tokens-and-userinfo",[637,3436,2814],{}," hook. Access tokens carry no\nprofile. A service reads it from userinfo when it first sees a user, so the app asks for ",[637,3439,1765],{},[3441,3442,3443,3444,3450,3451,3453,3454,600],"note",{},"Resource indicators (",[598,3445,3449],{"href":3446,"rel":3447},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc8707",[3448],"nofollow","RFC 8707","), one audience per service, are not supported\nyet. Every service accepts every token of the shared audience and relies on the frozen ",[637,3452,2176],{},". A service that must\nsee changes at once can ask the issuer through ",[598,3455,3457],{"href":3456},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients#token-introspection","introspection",[602,3459,267],{"id":3460},"app",[594,3462,3463],{},"The module's composables power the admin screen and the user's applications page. Use them to build your own.",[916,3465,3467],{"className":918,"code":3466,"language":921,"meta":922,"style":922},"const { query } = useTableQuery({ sort: { id: 'createdAt', desc: true } })\nconst admin = await useOAuthApplications(query) \u002F\u002F admin CRUD and one page of the list (applications, total, loading, refreshing, error, refresh)\nconst mine = await useMyApplications() \u002F\u002F the signed-in user's applications: { applications, loading, error, refresh }\n",[637,3468,3469,3526,3547],{"__ignoreMap":922},[926,3470,3471,3474,3476,3479,3481,3483,3486,3488,3490,3493,3495,3497,3500,3502,3504,3507,3509,3511,3514,3516,3520,3522,3524],{"class":928,"line":929},[926,3472,3473],{"class":947},"const",[926,3475,974],{"class":951},[926,3477,3478],{"class":943}," query ",[926,3480,1162],{"class":951},[926,3482,983],{"class":951},[926,3484,3485],{"class":939}," useTableQuery",[926,3487,944],{"class":943},[926,3489,2302],{"class":951},[926,3491,3492],{"class":992}," sort",[926,3494,1036],{"class":951},[926,3496,974],{"class":951},[926,3498,3499],{"class":992}," id",[926,3501,1036],{"class":951},[926,3503,1039],{"class":951},[926,3505,3506],{"class":1042},"createdAt",[926,3508,1046],{"class":951},[926,3510,1010],{"class":951},[926,3512,3513],{"class":992}," desc",[926,3515,1036],{"class":951},[926,3517,3519],{"class":3518},"sfNiH"," true",[926,3521,980],{"class":951},[926,3523,980],{"class":951},[926,3525,997],{"class":943},[926,3527,3528,3530,3533,3536,3538,3541,3544],{"class":928,"line":968},[926,3529,3473],{"class":947},[926,3531,3532],{"class":943}," admin ",[926,3534,3535],{"class":951},"=",[926,3537,986],{"class":932},[926,3539,3540],{"class":939}," useOAuthApplications",[926,3542,3543],{"class":943},"(query) ",[926,3545,3546],{"class":1092},"\u002F\u002F admin CRUD and one page of the list (applications, total, loading, refreshing, error, refresh)\n",[926,3548,3549,3551,3554,3556,3558,3561,3564],{"class":928,"line":1000},[926,3550,3473],{"class":947},[926,3552,3553],{"class":943}," mine ",[926,3555,3535],{"class":951},[926,3557,986],{"class":932},[926,3559,3560],{"class":939}," useMyApplications",[926,3562,3563],{"class":943},"() ",[926,3565,3566],{"class":1092},"\u002F\u002F the signed-in user's applications: { applications, loading, error, refresh }\n",[613,3568,3569,3578],{},[616,3570,3571],{},[619,3572,3573,3576],{},[622,3574,3575],{},"Composable",[622,3577,627],{},[629,3579,3580,3590,3600,3610],{},[619,3581,3582,3587],{},[634,3583,3584],{},[637,3585,3586],{},"useOAuthApplications(query?)",[634,3588,3589],{},"Admin create, update and delete, plus one page of the application list.",[619,3591,3592,3597],{},[634,3593,3594],{},[637,3595,3596],{},"useMyApplications()",[634,3598,3599],{},"The applications the signed-in user may sign in to.",[619,3601,3602,3607],{},[634,3603,3604],{},[637,3605,3606],{},"useOAuthApplicationActions()",[634,3608,3609],{},"The admin actions for one application, and its row menu.",[619,3611,3612,3617],{},[634,3613,3614],{},[637,3615,3616],{},"oauthApplicationsApi",[634,3618,3619],{},"The same endpoints as plain calls.",[594,3621,3622,3623,600],{},"All of them are auto-imported. They are also exported from ",[637,3624,3625],{},"@frigear-nu\u002Foauth-server\u002Fcomposables",[594,3627,3628,3633,3634,3636,3637,3640,3641,3644,3645,3648,3649,3652,3653,3656,3657,3659,3660,1701,3663,3666],{},[846,3629,3630],{},[637,3631,3632],{},"useOAuthApplications"," is awaitable, like ",[637,3635,3596],{},", so the admin page renders its list on the server.\nThe list is paginated: pass ",[637,3638,3639],{},"useTableQuery().query",", with the parameters of the ",[598,3642,3643],{"href":2204},"admin list\nroute",". Read ",[637,3646,3647],{},"applications"," (the items of the page) and\n",[637,3650,3651],{},"total",". It has no ",[637,3654,3655],{},"default"," data, so ",[637,3658,3647],{}," is empty until loaded and after an error. Check ",[637,3661,3662],{},"loading",[637,3664,3665],{},"error"," first.",[594,3668,3669,3674,3675,867,3678,3681,3682,3685,3686,3689,3690,3693,3694,3697,3698,658,3701,658,3704,867,3707,3710],{},[846,3670,3671],{},[637,3672,3673],{},"useOAuthApplicationActions"," owns ",[637,3676,3677],{},"create",[637,3679,3680],{},"edit"," (a ",[637,3683,3684],{},"FormSlideover"," opened through ",[637,3687,3688],{},"useOverlay()","), ",[637,3691,3692],{},"remove","\nand ",[637,3695,3696],{},"signOutEveryone"," (confirm dialogs), ",[637,3699,3700],{},"toggle",[637,3702,3703],{},"rotateSecret",[637,3705,3706],{},"copyClientId",[637,3708,3709],{},"menu(application)"," for the row\nmenu. Creating an application or replacing its secret shows the credentials once, in a modal.",[594,3712,3713,3717,3718,3720,3721,867,3723,3726],{},[846,3714,3715],{},[637,3716,3616],{}," resolves ",[637,3719,3692],{}," with ",[637,3722,854],{},[637,3724,3725],{},"revokeGrants"," with the application.",[1568,3728,101],{"id":3729},"pages",[594,3731,3732,3733,3735,3736,3739],{},"The admin, consent and applications pages follow the ",[598,3734,91],{"href":97},". The\nedit form of an application lives in the URL, ",[637,3737,3738],{},"\u002Fadmin\u002Foauth?application=\u003Cid>",", so you can link to it from ⌘K or\nanother page.",[1568,3741,3743],{"id":3742},"menu-items","Menu items",[594,3745,3746,3747,3749],{},"With ",[598,3748,354],{"href":355},", the module adds these items:",[1680,3751,3752,3762,3772],{},[1683,3753,3754,3757,3758,3761],{},[846,3755,3756],{},"Applications"," (order 50) to the ",[637,3759,3760],{},"account"," menu: the account tile and the left navigation.",[1683,3763,3764,3767,3768,3771],{},[846,3765,3766],{},"OAuth applications"," (order 40) to the Settings group of the admin sidebar (",[637,3769,3770],{},"admin.nav.settings","), when the admin\npage is on.",[1683,3773,3774,3776],{},[846,3775,2193],{}," (order 41) next to it, when the scopes page is on.",[594,3778,3779,3780,3782],{},"Both admin items show only to users with ",[637,3781,645],{},". They are only links: the pages and the API check the permission\nagain, and it needs a cookie session.",[602,3784,3786],{"id":3785},"next-steps","Next steps",[1680,3788,3789,3794,3803,3808,3814],{},[1683,3790,3791,3793],{},[598,3792,234],{"href":235}," – add claims, veto access and audit tokens.",[1683,3795,3796,3798,3799,867,3801,600],{},[598,3797,199],{"href":200}," – roles and permissions behind ",[637,3800,794],{},[637,3802,3416],{},[1683,3804,3805,3807],{},[598,3806,241],{"href":242}," – set up a service that accepts these access tokens.",[1683,3809,3810,3813],{},[598,3811,3812],{"href":238},"Routes and clients"," – look up every endpoint and how a sign-in\nworks.",[1683,3815,3816,3818],{},[598,3817,136],{"href":137}," – register an app in the admin area or in\nconfig.",[3820,3821,3822],"style",{},"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 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 .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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":922,"searchDepth":968,"depth":968,"links":3824},[3825,3829,3838,3842,3847,3851],{"id":604,"depth":968,"text":605,"children":3826},[3827,3828],{"id":1570,"depth":1000,"text":1571},{"id":1671,"depth":1000,"text":1672},{"id":1738,"depth":968,"text":1739,"children":3830},[3831,3832,3833,3834,3835,3836],{"id":1825,"depth":1000,"text":1826},{"id":2186,"depth":1000,"text":2187},{"id":2400,"depth":1000,"text":2401},{"id":2471,"depth":1000,"text":2423},{"id":2624,"depth":1000,"text":2625},{"id":2684,"depth":1000,"text":3837},"The frozen permissions claim",{"id":2821,"depth":968,"text":2822,"children":3839},[3840,3841],{"id":2855,"depth":1000,"text":2856},{"id":2882,"depth":1000,"text":2883},{"id":2924,"depth":968,"text":2925,"children":3843},[3844,3845,3846],{"id":3240,"depth":1000,"text":3241},{"id":3262,"depth":1000,"text":3263},{"id":3420,"depth":1000,"text":3421},{"id":3460,"depth":968,"text":267,"children":3848},[3849,3850],{"id":3729,"depth":1000,"text":101},{"id":3742,"depth":1000,"text":3743},{"id":3785,"depth":968,"text":3786},"Create OAuth applications from code, define scopes, let applications call your API and sign one app in to every service.","md",null,{},{"title":175},{"title":589,"description":3852},"HMvfUyz97rBzvpGwGlL6_YSay4mlgt0M_V07Y3U8slY",[3860,3862],{"title":173,"path":226,"stem":227,"description":3861,"children":-1},"Let people sign in to your other apps and services with the account they already have in this app.",{"title":234,"path":235,"stem":236,"description":3863,"children":-1},"Decide who may manage OAuth applications, which roles may sign in where and what goes into tokens.",1794503155730]