[{"data":1,"prerenderedAt":2898},["ShallowReactive",2],{"navigation_docs":3,"-guide-going-further-oauth-application":587,"-guide-going-further-oauth-application-surround":2893},[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":136,"body":589,"description":2886,"extension":2887,"links":2888,"meta":2889,"navigation":2890,"path":137,"seo":2891,"stem":138,"__hash__":2892},"docs\u002F2.guide\u002F4.going-further\u002F2.oauth-application.md",{"type":590,"value":591,"toc":2874},"minimark",[592,613,622,625,645,650,665,744,804,808,814,847,979,999,1013,1042,1046,1056,1067,1079,1417,1431,1507,1510,1521,1569,1575,1602,1663,1671,1674,1678,1686,1779,2152,2155,2166,2177,2234,2244,2248,2251,2265,2340,2360,2364,2370,2580,2599,2637,2646,2655,2660,2664,2843,2847,2870],[593,594,595,596,600,601,604,605,609,610,612],"p",{},"An app can sign its users in at your ",[597,598,599],"a",{"href":87},"issuer"," only after it is registered there. That\nregistration is an ",[597,602,603],{"href":87},"OAuth application",". It gives the app a ",[606,607,608],"code",{},"client_id",", the redirect URIs\nthe issuer accepts and, optionally, the roles that may sign in. This page registers one at an issuer that runs\n",[597,611,225],{"href":226},", and picks its type.",[614,615,617,621],"callout",{"icon":616},"i-lucide-users",[618,619,620],"strong",{},"For:"," whoever runs the issuer, and app developers who connect an app to it.",[593,623,624],{},"The other side of the sign-in depends on the app:",[626,627,628,636],"ul",{},[629,630,631,632,635],"li",{},"A browser or mobile app uses ",[597,633,634],{"href":184},"Auth in client mode",".",[629,637,638,639,641,642,635],{},"A web app with its own server uses ",[597,640,168],{"href":169}," in ",[597,643,644],{"href":87},"server mode",[646,647,649],"h2",{"id":648},"pick-the-application-type","Pick the application type",[593,651,652,653,656,657,660,661,635],{},"An application is ",[618,654,655],{},"confidential"," or ",[618,658,659],{},"public",". The type is fixed when the application is created. Why the two exist\nis on ",[597,662,664],{"href":663},"\u002Fguide\u002Fconcepts\u002Fidentity#public-and-confidential-applications","Public and confidential applications",[666,667,668,684],"table",{},[669,670,671],"thead",{},[672,673,674,678,681],"tr",{},[675,676,677],"th",{},"Your app",[675,679,680],{},"Type",[675,682,683],{},"Why",[685,686,687,705,718],"tbody",{},[672,688,689,697,702],{},[690,691,692,693],"td",{},"A web app with its own server, such as Nuxt with ",[597,694,696],{"href":695},"\u002Fguide\u002Fconcepts\u002Fidentity#sign-in-with-frigearnu","Sign in with frigear.nu",[690,698,699],{},[618,700,701],{},"Confidential",[690,703,704],{},"The secret stays on your server. It is sent on every token request, next to PKCE (Proof Key for Code Exchange).",[672,706,707,710,715],{},[690,708,709],{},"A single-page app (SPA), a Capacitor or mobile app, a desktop app or a CLI",[690,711,712],{},[618,713,714],{},"Public",[690,716,717],{},"Anything shipped to a browser or a device can be extracted, so a secret there is not a secret. PKCE protects the code exchange, and refresh token rotation protects the refresh token.",[672,719,720,723,726],{},[690,721,722],{},"Machine to machine, with no user",[690,724,725],{},"Not supported",[690,727,728,731,732,735,736,739,740,743],{},[606,729,730],{},"\u002Foauth\u002Ftoken"," takes only the ",[606,733,734],{},"authorization_code"," and ",[606,737,738],{},"refresh_token"," grants. Every token belongs to a signed-in user, and there is no ",[606,741,742],{},"client_credentials"," grant.",[626,745,746,759,765,779,788,798],{},[629,747,748,751,752,755,756,635],{},[618,749,750],{},"PKCE with S256 is mandatory for both types."," A request without ",[606,753,754],{},"code_challenge_method=S256"," is a ",[606,757,758],{},"400",[629,760,761,764],{},[618,762,763],{},"A public application has no secret at all."," The server stores a throwaway hash and never shows it.",[629,766,767,774,775,778],{},[618,768,769,770,773],{},"A ",[606,771,772],{},"client_secret"," sent to a public application is refused",", with ",[606,776,777],{},"401 invalid_client",". It is not ignored.",[629,780,781,784,785,787],{},[618,782,783],{},"A confidential application without its secret"," gets ",[606,786,777],{}," on every token and revoke request.",[629,789,790,793,794,797],{},[618,791,792],{},"Client mode is public by design."," A production build that embeds\n",[606,795,796],{},"NUXT_PUBLIC_AUTHENTICATION_CLIENT_CLIENT_SECRET"," fails.",[629,799,800,803],{},[618,801,802],{},"The type cannot be changed afterwards."," Register a new application instead.",[646,805,807],{"id":806},"create-it-in-the-admin-area","Create it in the admin area",[593,809,810,811,813],{},"Use the admin area for a confidential application, or for a public one that does not need the same ",[606,812,608],{}," in\nevery environment.",[815,816,817,820],"warning",{},[593,818,819],{},"Without these two things nobody can manage applications:",[626,821,822,831],{},[629,823,824,825,774,828,830],{},"The admin page: ",[606,826,827],{},"oauthServer.settings.pages.admin: true",[597,829,217],{"href":351}," installed.",[629,832,833,834,837,838,841,842,846],{},"A user whose ",[597,835,836],{"href":87},"role"," holds the ",[606,839,840],{},"oauth:manage"," permission (admins do by default), or a\n",[597,843,845],{"href":844},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#the-before-hook","before hook"," listener that allows it. Nobody else can manage\napplications. Access control fails closed.",[848,849,850,868,880,887,904,912,927,946,973],"ol",{},[629,851,852,853,856,857,860,861,864,865,635],{},"Open ",[618,854,855],{},"OAuth applications"," in the ",[618,858,859],{},"Settings"," group of the admin sidebar (",[606,862,863],{},"\u002Fadmin\u002Foauth","). Choose ",[618,866,867],{},"Add\napplication",[629,869,870,871,874,875,656,878,635],{},"Pick the ",[618,872,873],{},"Client type",": ",[876,877,701],"em",{},[876,879,714],{},[629,881,882,883,886],{},"Fill in the ",[618,884,885],{},"Name",", which the consent screen shows.",[629,888,882,889,892,893,896,897,900,901,635],{},[618,890,891],{},"Website URL",": the origin of the app, without a path, such as ",[606,894,895],{},"https:\u002F\u002Fapp.example.com",". Use ",[606,898,899],{},"http","\nonly for localhost. A confidential application needs it. A public application needs it or at least one redirect\nURI, so a native app can leave it empty and list its callback under ",[618,902,903],{},"Redirect URIs",[629,905,882,906,908,909,635],{},[618,907,903],{},". See ",[597,910,903],{"href":911},"#redirect-uris",[629,913,914,915,918,919,922,923,926],{},"Optional: a ",[618,916,917],{},"Preview URL pattern",", such as ",[606,920,921],{},"https:\u002F\u002F*.vercel.app",". It accepts the callback path on preview\ndeployments, where ",[606,924,925],{},"*"," is one hostname label.",[629,928,929,930,933,934,937,938,941,942,945],{},"Set the ",[618,931,932],{},"Allowed roles",". Leave them empty to let every signed-in user in, or list the roles that may. They are\nthe roles of ",[606,935,936],{},"authorization.roles"," (including inherited and ",[606,939,940],{},"auto"," ones), plus any your app adds in the\n",[606,943,944],{},"authorization:roles"," hook.",[629,947,948,949,952,953,952,956,952,959,735,962,965,966,969,970,972],{},"Optional: ",[618,950,951],{},"Login URL",", ",[618,954,955],{},"Description",[618,957,958],{},"Icon",[618,960,961],{},"Priority",[618,963,964],{},"Tags",". The Login URL is where the ",[618,967,968],{},"Open","\nbutton of the user's application list goes, and it defaults to the website URL. ",[618,971,961],{}," orders that list.",[629,974,975,976,635],{},"Choose ",[618,977,978],{},"Add application",[980,981,982,983,986,987,990,991,994,995,635],"caution",{},"A modal shows the ",[618,984,985],{},"Client ID"," (",[606,988,989],{},"app_…",") and, for a confidential application, the ",[618,992,993],{},"Client secret",". The secret is\nshown only this once. Store it before you close the modal. See ",[597,996,998],{"href":997},"#secrets","Secrets",[614,1000,1003,1006,1007,1009,1010,635],{"icon":1001,"color":1002},"i-lucide-check","success",[618,1004,1005],{},"What you should see:"," the new application in the list at ",[606,1008,863],{},". Its edit form opens at\n",[606,1011,1012],{},"\u002Fadmin\u002Foauth?application=\u003Cid>",[593,1014,1015,1016,952,1019,952,1022,952,1025,952,1028,735,1031,1034,1035,1037,1038,1041],{},"A row's menu has ",[618,1017,1018],{},"Edit application",[618,1020,1021],{},"Copy client ID",[618,1023,1024],{},"Disable application",[618,1026,1027],{},"New client secret",[618,1029,1030],{},"Sign\neveryone out",[618,1032,1033],{},"Delete application",". ",[618,1036,1024],{}," sets ",[606,1039,1040],{},"isActive"," to false.",[646,1043,1045],{"id":1044},"create-it-in-nuxtconfig","Create it in nuxt.config",[593,1047,1048,1051,1052,1055],{},[606,1049,1050],{},"oauthServer.clients"," defines public applications in config. These are\n",[597,1053,1054],{"href":87},"configured clients",". Use them when:",[626,1057,1058,1064],{},[629,1059,1060,1061,1063],{},"the app ships with its ",[606,1062,608],{}," compiled in, such as a mobile app, an SPA or a starter, and the id must be the\nsame in every environment;",[629,1065,1066],{},"the registration should be versioned with the app.",[593,1068,1069,1070,656,1073,1034,1076,1078],{},"Ids are free-form, such as ",[606,1071,1072],{},"mobile",[606,1074,1075],{},"com.example.app",[606,1077,989],{}," is reserved for applications created in the admin\narea.",[1080,1081,1087],"pre",{"className":1082,"code":1083,"filename":1084,"language":1085,"meta":1086,"style":1086},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export default defineNuxtConfig({\n  oauthServer: {\n    cors: { paths: ['\u002Fapi\u002F'] },\n    clients: [\n      {\n        \u002F\u002F Mobile (Capacitor): custom-scheme redirect, webview origins for CORS.\n        id: 'bills-mobile',\n        name: 'Bills (mobile)',\n        redirectUris: ['nu.frigear.bills:\u002Fauth\u002Fcallback'],\n        \u002F\u002F Accepted under `nuxt dev` only.\n        devRedirectUris: ['http:\u002F\u002Flocalhost:3001\u002Fauth\u002Fcallback'],\n        corsOrigins: ['capacitor:\u002F\u002Flocalhost', 'https:\u002F\u002Flocalhost'],\n      },\n      {\n        \u002F\u002F Web SPA: the origin is taken from the redirect URI.\n        id: 'bills-web',\n        name: 'Bills (web)',\n        redirectUris: ['https:\u002F\u002Fbills.example.com\u002Fauth\u002Fcallback'],\n        devRedirectUris: ['http:\u002F\u002Flocalhost:3000\u002Fauth\u002Fcallback'],\n      },\n    ],\n  },\n})\n","nuxt.config.ts (the issuer)","ts","",[606,1088,1089,1113,1126,1160,1171,1177,1184,1203,1220,1242,1248,1269,1300,1306,1311,1317,1333,1349,1369,1389,1394,1402,1408],{"__ignoreMap":1086},[1090,1091,1094,1098,1101,1105,1109],"span",{"class":1092,"line":1093},"line",1,[1090,1095,1097],{"class":1096},"s7zQu","export",[1090,1099,1100],{"class":1096}," default",[1090,1102,1104],{"class":1103},"s2Zo4"," defineNuxtConfig",[1090,1106,1108],{"class":1107},"sTEyZ","(",[1090,1110,1112],{"class":1111},"sMK4o","{\n",[1090,1114,1116,1120,1123],{"class":1092,"line":1115},2,[1090,1117,1119],{"class":1118},"swJcz","  oauthServer",[1090,1121,1122],{"class":1111},":",[1090,1124,1125],{"class":1111}," {\n",[1090,1127,1129,1132,1134,1137,1140,1142,1145,1148,1152,1154,1157],{"class":1092,"line":1128},3,[1090,1130,1131],{"class":1118},"    cors",[1090,1133,1122],{"class":1111},[1090,1135,1136],{"class":1111}," {",[1090,1138,1139],{"class":1118}," paths",[1090,1141,1122],{"class":1111},[1090,1143,1144],{"class":1107}," [",[1090,1146,1147],{"class":1111},"'",[1090,1149,1151],{"class":1150},"sfazB","\u002Fapi\u002F",[1090,1153,1147],{"class":1111},[1090,1155,1156],{"class":1107},"] ",[1090,1158,1159],{"class":1111},"},\n",[1090,1161,1163,1166,1168],{"class":1092,"line":1162},4,[1090,1164,1165],{"class":1118},"    clients",[1090,1167,1122],{"class":1111},[1090,1169,1170],{"class":1107}," [\n",[1090,1172,1174],{"class":1092,"line":1173},5,[1090,1175,1176],{"class":1111},"      {\n",[1090,1178,1180],{"class":1092,"line":1179},6,[1090,1181,1183],{"class":1182},"sHwdD","        \u002F\u002F Mobile (Capacitor): custom-scheme redirect, webview origins for CORS.\n",[1090,1185,1187,1190,1192,1195,1198,1200],{"class":1092,"line":1186},7,[1090,1188,1189],{"class":1118},"        id",[1090,1191,1122],{"class":1111},[1090,1193,1194],{"class":1111}," '",[1090,1196,1197],{"class":1150},"bills-mobile",[1090,1199,1147],{"class":1111},[1090,1201,1202],{"class":1111},",\n",[1090,1204,1206,1209,1211,1213,1216,1218],{"class":1092,"line":1205},8,[1090,1207,1208],{"class":1118},"        name",[1090,1210,1122],{"class":1111},[1090,1212,1194],{"class":1111},[1090,1214,1215],{"class":1150},"Bills (mobile)",[1090,1217,1147],{"class":1111},[1090,1219,1202],{"class":1111},[1090,1221,1223,1226,1228,1230,1232,1235,1237,1240],{"class":1092,"line":1222},9,[1090,1224,1225],{"class":1118},"        redirectUris",[1090,1227,1122],{"class":1111},[1090,1229,1144],{"class":1107},[1090,1231,1147],{"class":1111},[1090,1233,1234],{"class":1150},"nu.frigear.bills:\u002Fauth\u002Fcallback",[1090,1236,1147],{"class":1111},[1090,1238,1239],{"class":1107},"]",[1090,1241,1202],{"class":1111},[1090,1243,1245],{"class":1092,"line":1244},10,[1090,1246,1247],{"class":1182},"        \u002F\u002F Accepted under `nuxt dev` only.\n",[1090,1249,1251,1254,1256,1258,1260,1263,1265,1267],{"class":1092,"line":1250},11,[1090,1252,1253],{"class":1118},"        devRedirectUris",[1090,1255,1122],{"class":1111},[1090,1257,1144],{"class":1107},[1090,1259,1147],{"class":1111},[1090,1261,1262],{"class":1150},"http:\u002F\u002Flocalhost:3001\u002Fauth\u002Fcallback",[1090,1264,1147],{"class":1111},[1090,1266,1239],{"class":1107},[1090,1268,1202],{"class":1111},[1090,1270,1272,1275,1277,1279,1281,1284,1286,1289,1291,1294,1296,1298],{"class":1092,"line":1271},12,[1090,1273,1274],{"class":1118},"        corsOrigins",[1090,1276,1122],{"class":1111},[1090,1278,1144],{"class":1107},[1090,1280,1147],{"class":1111},[1090,1282,1283],{"class":1150},"capacitor:\u002F\u002Flocalhost",[1090,1285,1147],{"class":1111},[1090,1287,1288],{"class":1111},",",[1090,1290,1194],{"class":1111},[1090,1292,1293],{"class":1150},"https:\u002F\u002Flocalhost",[1090,1295,1147],{"class":1111},[1090,1297,1239],{"class":1107},[1090,1299,1202],{"class":1111},[1090,1301,1303],{"class":1092,"line":1302},13,[1090,1304,1305],{"class":1111},"      },\n",[1090,1307,1309],{"class":1092,"line":1308},14,[1090,1310,1176],{"class":1111},[1090,1312,1314],{"class":1092,"line":1313},15,[1090,1315,1316],{"class":1182},"        \u002F\u002F Web SPA: the origin is taken from the redirect URI.\n",[1090,1318,1320,1322,1324,1326,1329,1331],{"class":1092,"line":1319},16,[1090,1321,1189],{"class":1118},[1090,1323,1122],{"class":1111},[1090,1325,1194],{"class":1111},[1090,1327,1328],{"class":1150},"bills-web",[1090,1330,1147],{"class":1111},[1090,1332,1202],{"class":1111},[1090,1334,1336,1338,1340,1342,1345,1347],{"class":1092,"line":1335},17,[1090,1337,1208],{"class":1118},[1090,1339,1122],{"class":1111},[1090,1341,1194],{"class":1111},[1090,1343,1344],{"class":1150},"Bills (web)",[1090,1346,1147],{"class":1111},[1090,1348,1202],{"class":1111},[1090,1350,1352,1354,1356,1358,1360,1363,1365,1367],{"class":1092,"line":1351},18,[1090,1353,1225],{"class":1118},[1090,1355,1122],{"class":1111},[1090,1357,1144],{"class":1107},[1090,1359,1147],{"class":1111},[1090,1361,1362],{"class":1150},"https:\u002F\u002Fbills.example.com\u002Fauth\u002Fcallback",[1090,1364,1147],{"class":1111},[1090,1366,1239],{"class":1107},[1090,1368,1202],{"class":1111},[1090,1370,1372,1374,1376,1378,1380,1383,1385,1387],{"class":1092,"line":1371},19,[1090,1373,1253],{"class":1118},[1090,1375,1122],{"class":1111},[1090,1377,1144],{"class":1107},[1090,1379,1147],{"class":1111},[1090,1381,1382],{"class":1150},"http:\u002F\u002Flocalhost:3000\u002Fauth\u002Fcallback",[1090,1384,1147],{"class":1111},[1090,1386,1239],{"class":1107},[1090,1388,1202],{"class":1111},[1090,1390,1392],{"class":1092,"line":1391},20,[1090,1393,1305],{"class":1111},[1090,1395,1397,1400],{"class":1092,"line":1396},21,[1090,1398,1399],{"class":1107},"    ]",[1090,1401,1202],{"class":1111},[1090,1403,1405],{"class":1092,"line":1404},22,[1090,1406,1407],{"class":1111},"  },\n",[1090,1409,1411,1414],{"class":1092,"line":1410},23,[1090,1412,1413],{"class":1111},"}",[1090,1415,1416],{"class":1107},")\n",[815,1418,1419,1420,1423,1424,1427,1428,635],{},"Config cannot hold a confidential application. ",[606,1421,1422],{},"type"," accepts only ",[606,1425,1426],{},"'public'",", because a secret does not belong in\nconfig, and an invalid entry fails the build. Register confidential applications in the admin area, or with\n",[606,1429,1430],{},"createApplication",[626,1432,1433,1467,1476,1482,1495,1501],{},[629,1434,1435,1438,1439,952,1442,952,1445,952,1448,952,1451,735,1454,1457,1458,1462,1463,635],{},[618,1436,1437],{},"Other fields."," ",[606,1440,1441],{},"websiteUrl",[606,1443,1444],{},"description",[606,1446,1447],{},"icon",[606,1449,1450],{},"allowedRoles",[606,1452,1453],{},"tags",[606,1455,1456],{},"corsOrigins",". See\n",[597,1459,1461],{"href":1460},"#cors-for-browser-and-capacitor-clients","CORS",". The full table is on\n",[597,1464,1466],{"href":1465},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients#configured-clients","Configured clients",[629,1468,1469,1438,1472,1475],{},[618,1470,1471],{},"Runtime override.",[606,1473,1474],{},"NUXT_OAUTH_SERVER_CLIENTS",", as JSON, replaces the list at runtime.",[629,1477,1478,1481],{},[618,1479,1480],{},"The stored row."," The row is created on the first OAuth request, and the config wins on every read.",[629,1483,1484,1487,1488,1491,1492,635],{},[618,1485,1486],{},"In the admin area."," The application shows ",[618,1489,1490],{},"Managed in nuxt.config",". Staff can disable it and change its priority\nand login URL. Any other change, or a delete, is ",[606,1493,1494],{},"409 managed_by_config",[629,1496,1497,1500],{},[618,1498,1499],{},"Turning it back."," Remove the entry from config to turn it into an ordinary application.",[629,1502,1503,1506],{},[618,1504,1505],{},"Id clashes."," An id that already belongs to a confidential application is ignored, with a warning in the server\nlog.",[646,1508,903],{"id":1509},"redirect-uris",[593,1511,1512,1513,1516,1517,1520],{},"The server accepts a ",[606,1514,1515],{},"redirect_uri"," only when it is an ",[618,1518,1519],{},"exact string match"," of one of these. No prefixes, wildcards,\nquery strings or fragments.",[626,1522,1523,1536,1563],{},[629,1524,1525,1531,1532,1535],{},[618,1526,1527,1528],{},"The website URL plus ",[606,1529,1530],{},"callbackPath",", which defaults to ",[606,1533,1534],{},"\u002Fauth\u002Ffrigear",". This applies to applications created in\nthe admin area. Configured clients do not get it implicitly: list every URI.",[629,1537,1538,986,1541,1544,1545,1548,1549,1551,1552,952,1555,1558,1559,1562],{},[618,1539,1540],{},"An entry of Redirect URIs",[606,1542,1543],{},"redirectUris",", at most 20). It is ",[606,1546,1547],{},"https",", or ",[606,1550,899],{}," for ",[606,1553,1554],{},"localhost",[606,1556,1557],{},"127.0.0.1","\nand ",[606,1560,1561],{},"[::1]",". Loopback is matched exactly, so list the port you use.",[629,1564,1565,1568],{},[618,1566,1567],{},"The Preview URL pattern",", for web redirects.",[593,1570,1571,1572,1574],{},"Native redirects are for public applications only. A confidential application is limited to ",[606,1573,1547],{}," and loopback.",[626,1576,1577,1583,1589],{},[629,1578,1579,1580,1582],{},"A native redirect is a private-use scheme with one slash, such as ",[606,1581,1234],{},", with the app id\nin lowercase.",[629,1584,1585,1586,635],{},"A webview origin also works, such as ",[606,1587,1588],{},"capacitor:\u002F\u002Flocalhost\u002Fauth\u002Fcallback",[629,1590,1591,1592,952,1595,952,1598,1601],{},"Web schemes, ",[606,1593,1594],{},"javascript:",[606,1596,1597],{},"data:",[606,1599,1600],{},"file:",", credentials and fragments are rejected.",[666,1603,1604,1613],{},[669,1605,1606],{},[672,1607,1608,1610],{},[675,1609,267],{},[675,1611,1612],{},"Register",[685,1614,1615,1635,1647],{},[672,1616,1617,1623],{},[690,1618,1619,1622],{},[597,1620,1621],{"href":184},"Auth client mode"," in a browser",[690,1624,1625,1628,1629,1631,1632,1634],{},[606,1626,1627],{},"\u003Corigin>\u002Fauth\u002Fcallback"," (its ",[606,1630,1530],{}," default), plus ",[606,1633,1382],{}," for development",[672,1636,1637,1642],{},[690,1638,1639,1641],{},[597,1640,1621],{"href":184}," in a native app",[690,1643,1644],{},[606,1645,1646],{},"\u003Cnative.scheme in lowercase>:\u002Fauth\u002Fcallback",[672,1648,1649,1654],{},[690,1650,1651,1652],{},"Sign in with frigear.nu, in ",[597,1653,168],{"href":169},[690,1655,1656,1659,1660,1662],{},[606,1657,1658],{},"\u003Corigin>\u002Fauth\u002Ffrigear",": the website URL plus the server's default ",[606,1661,1530],{},", so no extra entry",[593,1664,1665,1668,1669,635],{},[606,1666,1667],{},"devRedirectUris"," exist for configured clients only. For an application created in the admin area, add the localhost\nURI to ",[618,1670,903],{},[815,1672,1673],{},"Remove a localhost redirect URI before production. Nothing marks it as development.",[646,1675,1677],{"id":1676},"scopes-consent-and-roles","Scopes, consent and roles",[593,1679,1680,1681,1685],{},"An access token is a ",[597,1682,1684],{"href":1683},"\u002Fguide\u002Fconcepts\u002Fauth-and-scopes#scopes","scope"," of what the user may do, never more. Four things\ndecide what an application gets:",[666,1687,1688,1698],{},[669,1689,1690],{},[672,1691,1692,1695],{},[675,1693,1694],{},"Layer",[675,1696,1697],{},"What it does",[685,1699,1700,1732,1746,1759],{},[672,1701,1702,1707],{},[690,1703,1704],{},[618,1705,1706],{},"What exists",[690,1708,1709,1710,952,1713,952,1716,1719,1720,1723,1724,1727,1728,1731],{},"OpenID Connect scopes (",[606,1711,1712],{},"openid",[606,1714,1715],{},"profile",[606,1717,1718],{},"email","), named scopes (",[606,1721,1722],{},"oauthServer.scopes",", a module's ",[606,1725,1726],{},"addScope",", and scopes you add in the admin area), and permissions or ",[606,1729,1730],{},"resource:*"," wildcards.",[672,1733,1734,1739],{},[690,1735,1736],{},[618,1737,1738],{},"What the user may grant",[690,1740,1741,1742,1745],{},"Every permission the scope expands to must pass the user's roles and the before hook (",[606,1743,1744],{},"canGrantScope","). A scope that does not is shown as unavailable on the consent screen and left out of the token.",[672,1747,1748,1753],{},[690,1749,1750],{},[618,1751,1752],{},"What the role caps",[690,1754,1755,1758],{},[606,1756,1757],{},"oauthServer.settings.roleScopes"," lists the most each role may grant to a client.",[672,1760,1761,1766],{},[690,1762,1763],{},[618,1764,1765],{},"What the application may ask for",[690,1767,1768,1769,952,1771,1774,1775,1778],{},"Wildcards (",[606,1770,925],{},[606,1772,1773],{},"tickets:*",") only when the application has ",[606,1776,1777],{},"wildcardScopes"," on.",[626,1780,1781,1897,1917,2035,2057,2070,2087,2097,2114,2124],{},[629,1782,1783,1786,1787,1790,1791,1794,1795,1890,1893,1894,1896],{},[618,1784,1785],{},"Named scopes."," A scope is one word (",[606,1788,1789],{},"tickets",") that stands for permissions. Define it in config, or add it in the\nadmin area (Scopes, in the Settings group). A named scope never holds a sensitive or ",[606,1792,1793],{},"sessionOnly"," permission.",[1080,1796,1798],{"className":1082,"code":1797,"filename":1084,"language":1085,"meta":1086,"style":1086},"oauthServer: {\n  scopes: [\n    { name: 'tickets', label: 'Your tickets', permissions: ['tickets:read', 'tickets:write'] },\n  ],\n}\n",[606,1799,1800,1810,1819,1878,1885],{"__ignoreMap":1086},[1090,1801,1802,1806,1808],{"class":1092,"line":1093},[1090,1803,1805],{"class":1804},"sBMFI","oauthServer",[1090,1807,1122],{"class":1111},[1090,1809,1125],{"class":1111},[1090,1811,1812,1815,1817],{"class":1092,"line":1115},[1090,1813,1814],{"class":1804},"  scopes",[1090,1816,1122],{"class":1111},[1090,1818,1170],{"class":1118},[1090,1820,1821,1824,1827,1829,1831,1833,1835,1837,1840,1842,1844,1847,1849,1851,1854,1856,1858,1860,1863,1865,1867,1869,1872,1874,1876],{"class":1092,"line":1128},[1090,1822,1823],{"class":1111},"    {",[1090,1825,1826],{"class":1118}," name",[1090,1828,1122],{"class":1111},[1090,1830,1194],{"class":1111},[1090,1832,1789],{"class":1150},[1090,1834,1147],{"class":1111},[1090,1836,1288],{"class":1111},[1090,1838,1839],{"class":1118}," label",[1090,1841,1122],{"class":1111},[1090,1843,1194],{"class":1111},[1090,1845,1846],{"class":1150},"Your tickets",[1090,1848,1147],{"class":1111},[1090,1850,1288],{"class":1111},[1090,1852,1853],{"class":1118}," permissions",[1090,1855,1122],{"class":1111},[1090,1857,1144],{"class":1118},[1090,1859,1147],{"class":1111},[1090,1861,1862],{"class":1150},"tickets:read",[1090,1864,1147],{"class":1111},[1090,1866,1288],{"class":1111},[1090,1868,1194],{"class":1111},[1090,1870,1871],{"class":1150},"tickets:write",[1090,1873,1147],{"class":1111},[1090,1875,1156],{"class":1118},[1090,1877,1159],{"class":1111},[1090,1879,1880,1883],{"class":1092,"line":1162},[1090,1881,1882],{"class":1118},"  ]",[1090,1884,1202],{"class":1111},[1090,1886,1887],{"class":1092,"line":1173},[1090,1888,1889],{"class":1111},"}\n",[1891,1892],"br",{},"Scopes defined in config or by a module are read-only in the admin area (",[606,1895,1494],{},"). When a stored\nscope grows, its version is bumped and users are asked to consent again.",[629,1898,1899,1902,1903,1906,1907,1910,1911,1913,1914,635],{},[618,1900,1901],{},"Unknown scopes."," A scope the server does not know is ",[606,1904,1905],{},"invalid_scope"," at ",[606,1908,1909],{},"\u002Foauth\u002Fauthorize",", redirected back to the\napplication once its redirect URI is trusted. On a refresh nothing is rejected: scopes the user can no longer grant\nare dropped silently. No ",[606,1912,1684],{}," means ",[606,1915,1916],{},"openid profile email",[629,1918,1919,1438,1922,1925,1926,2032,2034],{},[618,1920,1921],{},"The role cap.",[606,1923,1924],{},"roleScopes"," maps a role to the scopes, permissions or wildcards it may grant. A user's cap is the\nunion over all their roles, inheritance included, and a role that is not listed contributes nothing. Unset, there is\nno cap beyond the user's permissions. Names must be configured roles; the build checks them.",[1080,1927,1929],{"className":1082,"code":1928,"filename":1084,"language":1085,"meta":1086,"style":1086},"oauthServer: {\n  settings: {\n    roleScopes: {\n      admin: ['*'],\n      user: ['openid', 'profile', 'tickets:read', 'tickets:write'],\n    },\n  },\n}\n",[606,1930,1931,1939,1948,1957,1976,2019,2024,2028],{"__ignoreMap":1086},[1090,1932,1933,1935,1937],{"class":1092,"line":1093},[1090,1934,1805],{"class":1804},[1090,1936,1122],{"class":1111},[1090,1938,1125],{"class":1111},[1090,1940,1941,1944,1946],{"class":1092,"line":1115},[1090,1942,1943],{"class":1804},"  settings",[1090,1945,1122],{"class":1111},[1090,1947,1125],{"class":1111},[1090,1949,1950,1953,1955],{"class":1092,"line":1128},[1090,1951,1952],{"class":1804},"    roleScopes",[1090,1954,1122],{"class":1111},[1090,1956,1125],{"class":1111},[1090,1958,1959,1962,1964,1966,1968,1970,1972,1974],{"class":1092,"line":1162},[1090,1960,1961],{"class":1804},"      admin",[1090,1963,1122],{"class":1111},[1090,1965,1144],{"class":1118},[1090,1967,1147],{"class":1111},[1090,1969,925],{"class":1150},[1090,1971,1147],{"class":1111},[1090,1973,1239],{"class":1118},[1090,1975,1202],{"class":1111},[1090,1977,1978,1981,1983,1985,1987,1989,1991,1993,1995,1997,1999,2001,2003,2005,2007,2009,2011,2013,2015,2017],{"class":1092,"line":1173},[1090,1979,1980],{"class":1804},"      user",[1090,1982,1122],{"class":1111},[1090,1984,1144],{"class":1118},[1090,1986,1147],{"class":1111},[1090,1988,1712],{"class":1150},[1090,1990,1147],{"class":1111},[1090,1992,1288],{"class":1111},[1090,1994,1194],{"class":1111},[1090,1996,1715],{"class":1150},[1090,1998,1147],{"class":1111},[1090,2000,1288],{"class":1111},[1090,2002,1194],{"class":1111},[1090,2004,1862],{"class":1150},[1090,2006,1147],{"class":1111},[1090,2008,1288],{"class":1111},[1090,2010,1194],{"class":1111},[1090,2012,1871],{"class":1150},[1090,2014,1147],{"class":1111},[1090,2016,1239],{"class":1118},[1090,2018,1202],{"class":1111},[1090,2020,2021],{"class":1092,"line":1179},[1090,2022,2023],{"class":1111},"    },\n",[1090,2025,2026],{"class":1092,"line":1186},[1090,2027,1407],{"class":1111},[1090,2029,2030],{"class":1092,"line":1205},[1090,2031,1889],{"class":1111},[1891,2033],{},"It is a ceiling, not a default: a role listed there still only grants what the user could do.",[629,2036,2037,2040,2041,656,2043,2045,2046,2049,2050,2053,2054,2056],{},[618,2038,2039],{},"Wildcard scopes."," An application gets ",[606,2042,925],{},[606,2044,1730],{}," only with ",[606,2047,2048],{},"wildcardScopes: true"," on it (default off). Sensitive permissions (",[606,2051,2052],{},":pii",") are never covered by a wildcard, and ",[606,2055,1793],{}," permissions are never\noffered as scopes at all.",[629,2058,2059,2066,2067,2069],{},[618,2060,2061,2062,2065],{},"The ",[606,2063,2064],{},"permissions"," claim."," The access token carries the scopes, expanded and intersected with the user's permissions\nat issue. A resource server reads this claim (",[597,2068,241],{"href":242},") instead of\nlooking up roles it does not have. A token never gains permissions later; it only loses them if the user\ndoes.",[629,2071,2072,2075,2076,2079,2080,2083,2084,2086],{},[618,2073,2074],{},"Introspection."," A resource server that cannot verify tokens locally calls ",[606,2077,2078],{},"POST \u002Foauth\u002Fintrospect"," (RFC 7662) with\nits client credentials. It authenticates as a confidential application and only gets an answer for tokens issued to\nit or for its audience: anything else is ",[606,2081,2082],{},"{ \"active\": false }",". The answer holds the live ",[606,2085,2064],{},", so a role\nchange shows at once, not at expiry.",[629,2088,2089,2092,2093,2096],{},[618,2090,2091],{},"Consent."," Every sign-in shows the consent screen (",[606,2094,2095],{},"\u002Fauthorize",") with the requested scopes. It cannot be skipped\nper application.",[629,2098,2099,2102,2103,2106,2107,2110,2111,2113],{},[618,2100,2101],{},"Allowed roles."," An application with roles refuses users who hold none of them: with ",[606,2104,2105],{},"403"," at the authorization\nrequest, and with ",[606,2108,2109],{},"access_denied"," at the token endpoint. Roles are resolved through the role provider, so assigned,\ninherited and ",[606,2112,940],{}," roles all count. The check runs again on every refresh. So removing a role, or disabling the\napplication, stops the next refresh.",[629,2115,2116,2119,2120,2123],{},[618,2117,2118],{},"Refresh tokens."," The server always issues one with a user grant. There is no ",[606,2121,2122],{},"offline_access"," scope: it is\ndropped as unsupported, so asking for it is harmless. Each refresh replaces the token, and using an old one revokes\nits whole family.",[629,2125,2126,1438,2129,2132,2133,2136,2137,2140,2141,2144,2145,1457,2148,635],{},[618,2127,2128],{},"Lifetimes.",[606,2130,2131],{},"accessTokenTtl"," (default 900 s) and ",[606,2134,2135],{},"codeTtl"," (600 s) are server options. How long a refresh token\nlives is set per application (",[618,2138,2139],{},"Session length"," in the admin area), else by the server's ",[606,2142,2143],{},"refreshToken"," option\n(30 days unused). The token response includes ",[606,2146,2147],{},"refresh_token_expires_in",[597,2149,2151],{"href":2150},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients#session-lifetimes","Session lifetimes",[646,2153,998],{"id":2154},"secrets",[593,2156,2157,2158,2161,2162,2165],{},"A confidential application's secret is ",[618,2159,2160],{},"shown once",": in the modal after creating the application, and after ",[618,2163,2164],{},"New\nclient secret",". Only a hash is stored, so a lost secret is replaced, not recovered.",[980,2167,2168,2169,2172,2173,2176],{},"Store the secret in the server's runtime config, set through the environment. For Sign in with frigear.nu that is\n",[606,2170,2171],{},"NUXT_FRIGEAR_SSO_CLIENT_SECRET",". Never put it in ",[606,2174,2175],{},"runtimeConfig.public",", a committed file, or a browser or mobile\nbundle.",[666,2178,2179,2188],{},[669,2180,2181],{},[672,2182,2183,2186],{},[675,2184,2185],{},"Action",[675,2187,1697],{},[685,2189,2190,2206,2216,2225],{},[672,2191,2192,2196],{},[690,2193,2194],{},[618,2195,1027],{},[690,2197,2198,2199,2202,2203,2205],{},"Rotates the secret (",[606,2200,2201],{},"POST \u002Fapi\u002Fadmin\u002Foauth-server\u002Fapplications\u002F:id\u002Fsecret","). The old secret stops working at once, and every refresh token of the application is revoked, so everyone is signed out. A public application has no secret to rotate (",[606,2204,758],{},").",[672,2207,2208,2213],{},[690,2209,2210],{},[618,2211,2212],{},"Sign everyone out",[690,2214,2215],{},"Revokes all refresh tokens and keeps the secret. Use it after a leak of tokens.",[672,2217,2218,2222],{},[690,2219,2220],{},[618,2221,1024],{},[690,2223,2224],{},"Blocks new sign-ins and refreshes.",[672,2226,2227,2231],{},[690,2228,2229],{},[618,2230,1033],{},[690,2232,2233],{},"Also removes the application's codes and tokens.",[593,2235,2236,2237,2240,2241,635],{},"Access tokens already issued stay valid until they expire, after 15 minutes by default. To end one user's sessions,\ncall ",[606,2238,2239],{},"revokeUserGrants(userId, applicationId?)",". See the\n",[597,2242,2243],{"href":231},"OAuth Server helpers",[646,2245,2247],{"id":2246},"cors-for-browser-and-capacitor-clients","CORS for browser and Capacitor clients",[593,2249,2250],{},"Native redirects need no cross-origin requests (CORS). Two kinds of app do call the server from a browser on another\norigin, so CORS applies to them:",[626,2252,2253,2262],{},[629,2254,2255,2256,2258,2259,2261],{},"the webview of a Capacitor app: ",[606,2257,1283],{}," on iOS, and ",[606,2260,1293],{}," on Android;",[629,2263,2264],{},"an SPA on another origin.",[666,2266,2267,2276],{},[669,2268,2269],{},[672,2270,2271,2274],{},[675,2272,2273],{},"Setting",[675,2275,1697],{},[685,2277,2278,2308,2326],{},[672,2279,2280,2285],{},[690,2281,2282],{},[606,2283,2284],{},"oauthServer.cors.origins",[690,2286,2287,2288,2290,2291,952,2293,952,2296,2299,2300,2303,2304,2307],{},"Exact origins, never ",[606,2289,925],{},", that may call ",[606,2292,730],{},[606,2294,2295],{},"\u002Foauth\u002Frevoke",[606,2297,2298],{},"\u002Foauth\u002Fuserinfo"," and the ",[606,2301,2302],{},"\u002F.well-known"," documents. ",[606,2305,2306],{},"NUXT_OAUTH_SERVER_CORS_ORIGINS"," takes a comma separated list.",[672,2309,2310,2315],{},[690,2311,2312,2313],{},"A configured client's ",[606,2314,1456],{},[690,2316,2317,2318,2321,2322,2325],{},"The client's own origins. By default, those of its redirect URIs that have one: a native ",[606,2319,2320],{},"nu.frigear.bills:\u002F…"," has none. ",[606,2323,2324],{},"false"," adds none. For a mobile app, list the two webview origins here.",[672,2327,2328,2333],{},[690,2329,2330],{},[606,2331,2332],{},"oauthServer.cors.paths",[690,2334,2335,2336,2339],{},"Path prefixes of your own API, such as ",[606,2337,2338],{},"['\u002Fapi\u002F']",", that the same origins may call with a bearer token.",[626,2341,2342,2348,2353],{},[629,2343,2344,2345,635],{},"An application created in the admin area adds no origins. List them in ",[606,2346,2347],{},"cors.origins",[629,2349,2350,2352],{},[606,2351,1909],{}," is never opened to CORS. It is a navigation, with cookies.",[629,2354,2355,2356,2359],{},"Cookies never travel cross-origin. So session-only routes answer ",[606,2357,2358],{},"401"," to these apps.",[646,2361,2363],{"id":2362},"connect-the-app","Connect the app",[593,2365,2366,2369],{},[618,2367,2368],{},"A public application, in a browser or mobile app."," Point Auth in client mode at the issuer. There is no secret\noption.",[1080,2371,2374],{"className":1082,"code":2372,"filename":2373,"language":1085,"meta":1086,"style":1086},"export default defineNuxtConfig({\n  modules: ['@nuxt\u002Fui', '@frigear-nu\u002Fauth'],\n  authentication: {\n    mode: 'client',\n    client: {\n      issuer: 'https:\u002F\u002Fid.example.com',\n      clientId: 'bills-mobile', \u002F\u002F or app_… for an admin-created application\n      scopes: ['openid', 'profile', 'email'], \u002F\u002F must contain 'openid'\n      discovery: false, \u002F\u002F the Frigear OAuth server's own paths\n      native: { scheme: 'nu.frigear.bills' }, \u002F\u002F nu.frigear.bills:\u002Fauth\u002Fcallback\n    },\n  },\n})\n","nuxt.config.ts (the app)",[606,2375,2376,2388,2417,2426,2442,2451,2467,2485,2523,2539,2566,2570,2574],{"__ignoreMap":1086},[1090,2377,2378,2380,2382,2384,2386],{"class":1092,"line":1093},[1090,2379,1097],{"class":1096},[1090,2381,1100],{"class":1096},[1090,2383,1104],{"class":1103},[1090,2385,1108],{"class":1107},[1090,2387,1112],{"class":1111},[1090,2389,2390,2393,2395,2397,2399,2402,2404,2406,2408,2411,2413,2415],{"class":1092,"line":1115},[1090,2391,2392],{"class":1118},"  modules",[1090,2394,1122],{"class":1111},[1090,2396,1144],{"class":1107},[1090,2398,1147],{"class":1111},[1090,2400,2401],{"class":1150},"@nuxt\u002Fui",[1090,2403,1147],{"class":1111},[1090,2405,1288],{"class":1111},[1090,2407,1194],{"class":1111},[1090,2409,2410],{"class":1150},"@frigear-nu\u002Fauth",[1090,2412,1147],{"class":1111},[1090,2414,1239],{"class":1107},[1090,2416,1202],{"class":1111},[1090,2418,2419,2422,2424],{"class":1092,"line":1128},[1090,2420,2421],{"class":1118},"  authentication",[1090,2423,1122],{"class":1111},[1090,2425,1125],{"class":1111},[1090,2427,2428,2431,2433,2435,2438,2440],{"class":1092,"line":1162},[1090,2429,2430],{"class":1118},"    mode",[1090,2432,1122],{"class":1111},[1090,2434,1194],{"class":1111},[1090,2436,2437],{"class":1150},"client",[1090,2439,1147],{"class":1111},[1090,2441,1202],{"class":1111},[1090,2443,2444,2447,2449],{"class":1092,"line":1173},[1090,2445,2446],{"class":1118},"    client",[1090,2448,1122],{"class":1111},[1090,2450,1125],{"class":1111},[1090,2452,2453,2456,2458,2460,2463,2465],{"class":1092,"line":1179},[1090,2454,2455],{"class":1118},"      issuer",[1090,2457,1122],{"class":1111},[1090,2459,1194],{"class":1111},[1090,2461,2462],{"class":1150},"https:\u002F\u002Fid.example.com",[1090,2464,1147],{"class":1111},[1090,2466,1202],{"class":1111},[1090,2468,2469,2472,2474,2476,2478,2480,2482],{"class":1092,"line":1186},[1090,2470,2471],{"class":1118},"      clientId",[1090,2473,1122],{"class":1111},[1090,2475,1194],{"class":1111},[1090,2477,1197],{"class":1150},[1090,2479,1147],{"class":1111},[1090,2481,1288],{"class":1111},[1090,2483,2484],{"class":1182}," \u002F\u002F or app_… for an admin-created application\n",[1090,2486,2487,2490,2492,2494,2496,2498,2500,2502,2504,2506,2508,2510,2512,2514,2516,2518,2520],{"class":1092,"line":1205},[1090,2488,2489],{"class":1118},"      scopes",[1090,2491,1122],{"class":1111},[1090,2493,1144],{"class":1107},[1090,2495,1147],{"class":1111},[1090,2497,1712],{"class":1150},[1090,2499,1147],{"class":1111},[1090,2501,1288],{"class":1111},[1090,2503,1194],{"class":1111},[1090,2505,1715],{"class":1150},[1090,2507,1147],{"class":1111},[1090,2509,1288],{"class":1111},[1090,2511,1194],{"class":1111},[1090,2513,1718],{"class":1150},[1090,2515,1147],{"class":1111},[1090,2517,1239],{"class":1107},[1090,2519,1288],{"class":1111},[1090,2521,2522],{"class":1182}," \u002F\u002F must contain 'openid'\n",[1090,2524,2525,2528,2530,2534,2536],{"class":1092,"line":1222},[1090,2526,2527],{"class":1118},"      discovery",[1090,2529,1122],{"class":1111},[1090,2531,2533],{"class":2532},"sfNiH"," false",[1090,2535,1288],{"class":1111},[1090,2537,2538],{"class":1182}," \u002F\u002F the Frigear OAuth server's own paths\n",[1090,2540,2541,2544,2546,2548,2551,2553,2555,2558,2560,2563],{"class":1092,"line":1244},[1090,2542,2543],{"class":1118},"      native",[1090,2545,1122],{"class":1111},[1090,2547,1136],{"class":1111},[1090,2549,2550],{"class":1118}," scheme",[1090,2552,1122],{"class":1111},[1090,2554,1194],{"class":1111},[1090,2556,2557],{"class":1150},"nu.frigear.bills",[1090,2559,1147],{"class":1111},[1090,2561,2562],{"class":1111}," },",[1090,2564,2565],{"class":1182}," \u002F\u002F nu.frigear.bills:\u002Fauth\u002Fcallback\n",[1090,2567,2568],{"class":1092,"line":1250},[1090,2569,2023],{"class":1111},[1090,2571,2572],{"class":1092,"line":1271},[1090,2573,1407],{"class":1111},[1090,2575,2576,2578],{"class":1092,"line":1302},[1090,2577,1413],{"class":1111},[1090,2579,1416],{"class":1107},[593,2581,2582,2585,2586,2589,2590,2592,2593,2595,2596,2598],{},[618,2583,2584],{},"A confidential application, in a web app with its own server."," Use Sign in with frigear.nu (",[606,2587,2588],{},"oauth.frigear","), with\nthe Client ID and secret from the modal. Create the application with the app's origin as the ",[618,2591,891],{},".\n",[606,2594,1658],{}," is then accepted without a ",[618,2597,903],{}," entry.",[1080,2600,2605],{"className":2601,"code":2602,"filename":2603,"language":2604,"meta":1086,"style":1086},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","NUXT_FRIGEAR_SSO_URL=https:\u002F\u002Fid.example.com\nNUXT_FRIGEAR_SSO_CLIENT_ID=app_…\nNUXT_FRIGEAR_SSO_CLIENT_SECRET=…\n",".env (the app)","bash",[606,2606,2607,2618,2628],{"__ignoreMap":1086},[1090,2608,2609,2612,2615],{"class":1092,"line":1093},[1090,2610,2611],{"class":1107},"NUXT_FRIGEAR_SSO_URL",[1090,2613,2614],{"class":1111},"=",[1090,2616,2617],{"class":1150},"https:\u002F\u002Fid.example.com\n",[1090,2619,2620,2623,2625],{"class":1092,"line":1115},[1090,2621,2622],{"class":1107},"NUXT_FRIGEAR_SSO_CLIENT_ID",[1090,2624,2614],{"class":1111},[1090,2626,2627],{"class":1150},"app_…\n",[1090,2629,2630,2632,2634],{"class":1092,"line":1128},[1090,2631,2171],{"class":1107},[1090,2633,2614],{"class":1111},[1090,2635,2636],{"class":1150},"…\n",[593,2638,2639,2640,2643,2644,635],{},"With both set, ",[606,2641,2642],{},"GET \u002Fauth\u002Ffrigear"," signs users in at the issuer. The options are on ",[597,2645,168],{"href":169},[593,2647,2648,2651,2652,2654],{},[618,2649,2650],{},"A service that only receives the tokens",", such as an API behind the app, is not an OAuth application. It verifies\nthe access token with ",[597,2653,241],{"href":242},", and needs no application of its own.",[593,2656,2657,2658,635],{},"For other issuers, and the full checklist of what the app needs, see\n",[597,2659,131],{"href":132},[646,2661,2663],{"id":2662},"troubleshooting","Troubleshooting",[666,2665,2666,2676],{},[669,2667,2668],{},[672,2669,2670,2673],{},[675,2671,2672],{},"Symptom",[675,2674,2675],{},"Likely cause",[685,2677,2678,2696,2708,2718,2732,2747,2761,2774,2791,2803,2817,2835],{},[672,2679,2680,2686],{},[690,2681,2682,1906,2684],{},[606,2683,777],{},[606,2685,730],{},[690,2687,2688,2689,2691,2692,2695],{},"A wrong ",[606,2690,608],{},"; a confidential application without its secret, or with an old one after rotation; or a ",[618,2693,2694],{},"secret sent to a public application",". Remove the secret from a public application's app.",[672,2697,2698,2701],{},[690,2699,2700],{},"The authorize page says \"The redirect_uri is not allowed for this application\"",[690,2702,2703,2704,2707],{},"Not an exact match. Compare scheme, case, port, trailing slash and path (",[606,2705,2706],{},"appid:\u002Fauth\u002Fcallback",", one slash). A native URI on a confidential application is never accepted.",[672,2709,2710,2713],{},[690,2711,2712],{},"\"Unknown or disabled application\"",[690,2714,2688,2715,2717],{},[606,2716,608],{},", or the application is disabled.",[672,2719,2720,2726],{},[690,2721,2722,2725],{},[606,2723,2724],{},"invalid_grant"," on a refresh",[690,2727,2728,2729,2731],{},"The refresh token was already used, was revoked or expired. Reuse happens when two tabs or devices share one, or a response was lost after rotation. Revocation comes from ",[618,2730,2212],{}," or a rotated secret. The whole family is revoked: sign in again.",[672,2733,2734,2739],{},[690,2735,2736,2738],{},[606,2737,2724],{},": redirect_uri does not match",[690,2740,2741,2742,2744,2745,2205],{},"The token request's ",[606,2743,1515],{}," differs from the authorization request's, or the code was reused or expired (",[606,2746,2135],{},[672,2748,2749,2755],{},[690,2750,2751,1906,2753],{},[606,2752,1905],{},[606,2754,1909],{},[690,2756,2757,2758,2760],{},"The request names a scope the server does not know. Define it in ",[606,2759,1722],{}," or the admin area, or ask for a permission that is registered.",[672,2762,2763,2766],{},[690,2764,2765],{},"A scope missing from the token, or unavailable on the consent screen",[690,2767,2768,2769,2771,2772,635],{},"The user's roles do not grant it, it lies outside their ",[606,2770,1924],{}," cap, or it is a wildcard for an application without ",[606,2773,1777],{},[672,2775,2776,2784],{},[690,2777,2778,2780,2781,2783],{},[606,2779,2109],{}," at the token endpoint, or ",[606,2782,2105],{}," at authorize",[690,2785,2786,2787,2790],{},"The user lacks an allowed role, the application is disabled, or ",[606,2788,2789],{},"oauth:application:access"," vetoed.",[672,2792,2793,2798],{},[690,2794,2795,2797],{},[606,2796,1494],{}," in the admin area",[690,2799,2800,2801,635],{},"The application is a configured client. Edit ",[606,2802,1050],{},[672,2804,2805,2810],{},[690,2806,2807],{},[606,2808,2809],{},"500 not_configured",[690,2811,2812,1548,2814,2816],{},[606,2813,1050],{},[606,2815,1474],{},", is invalid. The server log names the problem.",[672,2818,2819,2822],{},[690,2820,2821],{},"A browser CORS error on the token request or the API",[690,2823,2824,2825,2827,2828,2830,2831,2834],{},"The origin is not in ",[606,2826,2347],{}," or the client's ",[606,2829,1456],{},", or the API path is not under ",[606,2832,2833],{},"cors.paths",". The authorization endpoint is never CORS-enabled, by design.",[672,2836,2837,2840],{},[690,2838,2839],{},"A confidential application in a mobile app or SPA",[690,2841,2842],{},"Do not. Register a public application: a secret in the bundle is public, and Auth's client mode fails the build for it.",[646,2844,2846],{"id":2845},"next-steps","Next steps",[626,2848,2849,2854,2860,2865],{},[629,2850,2851,2853],{},[597,2852,141],{"href":142}," – set up the key the issuer signs this application's tokens with.",[629,2855,2856,2859],{},[597,2857,2858],{"href":238},"Routes and clients"," – look up every route and configured client\nfield.",[629,2861,2862,2864],{},[597,2863,67],{"href":68}," – how permissions, roles and scopes fit together.",[629,2866,2867,2869],{},[597,2868,81],{"href":82}," – see how the app, the issuer and the resource server relate.",[2871,2872,2873],"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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .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 .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1086,"searchDepth":1115,"depth":1115,"links":2875},[2876,2877,2878,2879,2880,2881,2882,2883,2884,2885],{"id":648,"depth":1115,"text":649},{"id":806,"depth":1115,"text":807},{"id":1044,"depth":1115,"text":1045},{"id":1509,"depth":1115,"text":903},{"id":1676,"depth":1115,"text":1677},{"id":2154,"depth":1115,"text":998},{"id":2246,"depth":1115,"text":2247},{"id":2362,"depth":1115,"text":2363},{"id":2662,"depth":1115,"text":2663},{"id":2845,"depth":1115,"text":2846},"Register an app at an issuer that runs OAuth Server, as a confidential application in the admin area or a public one in the admin area or nuxt.config.","md",null,{},{"icon":139},{"title":136,"description":2886},"lBP5oR2i-0sJRlqUq2M9Mhbh6__8bnlXNRk-nA2EkAU",[2894,2896],{"title":131,"path":132,"stem":133,"description":2895,"icon":134,"children":-1},"Sign a Frigear mobile or browser app in at any OpenID Connect issuer, such as OAuth Server, frigear.nu, Keycloak, Auth0 or Zitadel.",{"title":141,"path":142,"stem":143,"description":2897,"icon":144,"children":-1},"Give OAuth Server the private key it signs tokens with, per environment, and rotate it without signing anyone out.",1794503151416]