[{"data":1,"prerenderedAt":3371},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fauth-h3client":64,"navigation":217,"navLinks_footer":829,"\u002Fdocs\u002Fauth-h3client\u002Fsecurity_page":842,"\u002Fdocs\u002Fauth-h3client\u002Fsecurity_surround":2355,"\u002Fdocs\u002Fauth-h3client\u002Fsecurity":2358},{"id":4,"extension":5,"links":6,"meta":61,"stem":62,"__hash__":63},"navigationMenu\u002Fnavigation.json","json",[7,52,57],{"nested":8,"label":9,"icon":10,"to":11,"children":12},true,"Docs","i-lucide-book-open","\u002Fdocs\u002Fgetting-started",[13,19,26,32,39,45],{"label":14,"icon":15,"to":11,"description":16,"github":17,"badge":18},"Getting Started","i-lucide-rocket","An introduction to help you understand the core components.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fdocshub","Start Here",{"label":20,"icon":21,"to":22,"description":23,"github":24,"badge":25},"Auth H3 Client","i-lucide-key-round","\u002Fdocs\u002Fauth-h3client","Seamlessly enforce OAuth 2.0 authentication and session management integrated directly as the client of the IAM module.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fauth-h3client","Core",{"label":27,"icon":28,"to":29,"description":30,"github":31,"badge":25},"IAM","i-lucide-shield-check","\u002Fdocs\u002Fiam","Identity and Access Management featuring granular roles, permissions, and security policies.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fauth",{"label":33,"icon":34,"to":35,"description":36,"github":37,"badge":38},"Bot Detection","i-lucide-cpu","\u002Fdocs\u002Fbot-detection","Advanced behavioral analysis and request fingerprinting to stop malicious automated traffic.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fbot-detector","Security",{"label":40,"icon":41,"to":42,"description":43,"github":44,"badge":38},"Shield Base","i-lucide-database-zap","\u002Fdocs\u002Fshield-base","CLI and programmatic toolkit for compiling offline-ready IP intelligence databases from BGP, GeoIP, Tor, FireHOL, and other public threat feeds.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fshield-base-cli",{"label":46,"icon":47,"to":48,"description":49,"github":50,"badge":51},"Utils","i-lucide-wrench","\u002Fdocs\u002Futils","A standard library of highly optimized helpers for formatting, validation, and core logic.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Futils","Library",{"nested":53,"label":54,"icon":55,"to":56},false,"Blog","i-lucide-pen-line","\u002Fblog",{"nested":53,"label":58,"icon":59,"to":60},"Website","lucide:app-window-mac","https:\u002F\u002Friavzon.com",{},"navigation","gkaQ0xRGxSLrLyM3kttLe0oBwkrR1EBjlepF8LSbwF8",[65],{"title":9,"path":66,"stem":67,"children":68,"page":53},"\u002Fdocs","docs",[69],{"title":20,"path":22,"stem":70,"children":71},"docs\u002Fauth-h3client\u002Findex",[72,73,82,119,145,167,170,191,195],{"title":20,"path":22,"stem":70},{"title":14,"path":74,"stem":75,"children":76},"\u002Fdocs\u002Fauth-h3client\u002Fgetting-started","docs\u002Fauth-h3client\u002F00.getting-started\u002Findex",[77,78],{"title":14,"path":74,"stem":75},{"title":79,"path":80,"stem":81},"Nuxt Module","\u002Fdocs\u002Fauth-h3client\u002Fgetting-started\u002Fnuxt","docs\u002Fauth-h3client\u002F00.getting-started\u002F00.nuxt",{"title":83,"path":84,"stem":85,"children":86},"Essentials","\u002Fdocs\u002Fauth-h3client\u002Fessentials","docs\u002Fauth-h3client\u002F01.essentials\u002Findex",[87,88,92,96,100,104,108,111,115],{"title":83,"path":84,"stem":85},{"title":89,"path":90,"stem":91},"Session Management","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fsession","docs\u002Fauth-h3client\u002F01.essentials\u002F00.session",{"title":93,"path":94,"stem":95},"Route Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Froute-protection","docs\u002Fauth-h3client\u002F01.essentials\u002F01.route-protection",{"title":97,"path":98,"stem":99},"CSRF Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcsrf","docs\u002Fauth-h3client\u002F01.essentials\u002F02.csrf",{"title":101,"path":102,"stem":103},"Auth Flows","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fauth-flows","docs\u002Fauth-h3client\u002F01.essentials\u002F03.auth-flows",{"title":105,"path":106,"stem":107},"OAuth and OIDC","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Foauth","docs\u002Fauth-h3client\u002F01.essentials\u002F04.oauth",{"title":33,"path":109,"stem":110},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fbot-detection","docs\u002Fauth-h3client\u002F01.essentials\u002F05.bot-detection",{"title":112,"path":113,"stem":114},"Cookies","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcookies","docs\u002Fauth-h3client\u002F01.essentials\u002F06.cookies",{"title":116,"path":117,"stem":118},"Logging","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Flogging","docs\u002Fauth-h3client\u002F01.essentials\u002F07.logging",{"title":120,"path":121,"stem":122,"children":123},"MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa","docs\u002Fauth-h3client\u002F02.mfa\u002Findex",[124,125,129,133,137,141],{"title":120,"path":121,"stem":122},{"title":126,"path":127,"stem":128},"Built-in MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fbuilt-in-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F01.built-in-flow",{"title":130,"path":131,"stem":132},"Password Reset","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fpassword-reset","docs\u002Fauth-h3client\u002F02.mfa\u002F02.password-reset",{"title":134,"path":135,"stem":136},"Email Change","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Femail-change","docs\u002Fauth-h3client\u002F02.mfa\u002F03.email-change",{"title":138,"path":139,"stem":140},"Custom MFA Flow","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fcustom-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F04.custom-flow",{"title":142,"path":143,"stem":144},"Client-Side MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fclient-side","docs\u002Fauth-h3client\u002F02.mfa\u002F05.client-side",{"title":146,"path":147,"stem":148,"children":149},"Client-side","\u002Fdocs\u002Fauth-h3client\u002Fclient","docs\u002Fauth-h3client\u002F03.client\u002Findex",[150,151,155,159,163],{"title":146,"path":147,"stem":148},{"title":152,"path":153,"stem":154},"useAuthData","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-auth-data","docs\u002Fauth-h3client\u002F03.client\u002F00.use-auth-data",{"title":156,"path":157,"stem":158},"useMagicLink","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-magic-link","docs\u002Fauth-h3client\u002F03.client\u002F01.use-magic-link",{"title":160,"path":161,"stem":162},"executeRequest","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fexecute-request","docs\u002Fauth-h3client\u002F03.client\u002F02.execute-request",{"title":164,"path":165,"stem":166},"getCsrfToken","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fget-csrf-token","docs\u002Fauth-h3client\u002F03.client\u002F03.get-csrf-token",{"title":38,"path":168,"stem":169},"\u002Fdocs\u002Fauth-h3client\u002Fsecurity","docs\u002Fauth-h3client\u002F04.security",{"title":171,"path":172,"stem":173,"children":174,"page":53},"Guides","\u002Fdocs\u002Fauth-h3client\u002Fguides","docs\u002Fauth-h3client\u002F05.guides",[175,179,183,187],{"title":176,"path":177,"stem":178},"H3 and Nitro Setup","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fh3-nitro","docs\u002Fauth-h3client\u002F05.guides\u002F00.h3-nitro",{"title":180,"path":181,"stem":182},"HMAC Inter-service Auth","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fhmac","docs\u002Fauth-h3client\u002F05.guides\u002Fhmac",{"title":184,"path":185,"stem":186},"Image Upload","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fimage-upload","docs\u002Fauth-h3client\u002F05.guides\u002Fimage-upload",{"title":188,"path":189,"stem":190},"mTLS Configuration","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fmtls","docs\u002Fauth-h3client\u002F05.guides\u002Fmtls",{"title":192,"path":193,"stem":194},"Configuration","\u002Fdocs\u002Fauth-h3client\u002Fconfiguration","docs\u002Fauth-h3client\u002F06.configuration",{"title":196,"path":197,"stem":198,"children":199},"API Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi","docs\u002Fauth-h3client\u002F07.api\u002Findex",[200,201,205,209,213],{"title":196,"path":197,"stem":198},{"title":202,"path":203,"stem":204},"Routes Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcontrollers","docs\u002Fauth-h3client\u002F07.api\u002F00.controllers",{"title":206,"path":207,"stem":208},"Middleware Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fmiddleware","docs\u002Fauth-h3client\u002F07.api\u002F01.middleware",{"title":210,"path":211,"stem":212},"Client-side Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcomposables","docs\u002Fauth-h3client\u002F07.api\u002F02.composables",{"title":214,"path":215,"stem":216},"Utilities","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Futilities","docs\u002Fauth-h3client\u002F07.api\u002F03.utilities",[218],{"title":9,"path":66,"stem":67,"children":219,"page":53},[220,268,386,391,569,636],{"title":20,"path":22,"stem":70,"children":221},[222,223,227,238,246,253,254,260,261],{"title":20,"path":22,"stem":70},{"title":14,"path":74,"stem":75,"children":224},[225,226],{"title":14,"path":74,"stem":75},{"title":79,"path":80,"stem":81},{"title":83,"path":84,"stem":85,"children":228},[229,230,231,232,233,234,235,236,237],{"title":83,"path":84,"stem":85},{"title":89,"path":90,"stem":91},{"title":93,"path":94,"stem":95},{"title":97,"path":98,"stem":99},{"title":101,"path":102,"stem":103},{"title":105,"path":106,"stem":107},{"title":33,"path":109,"stem":110},{"title":112,"path":113,"stem":114},{"title":116,"path":117,"stem":118},{"title":120,"path":121,"stem":122,"children":239},[240,241,242,243,244,245],{"title":120,"path":121,"stem":122},{"title":126,"path":127,"stem":128},{"title":130,"path":131,"stem":132},{"title":134,"path":135,"stem":136},{"title":138,"path":139,"stem":140},{"title":142,"path":143,"stem":144},{"title":146,"path":147,"stem":148,"children":247},[248,249,250,251,252],{"title":146,"path":147,"stem":148},{"title":152,"path":153,"stem":154},{"title":156,"path":157,"stem":158},{"title":160,"path":161,"stem":162},{"title":164,"path":165,"stem":166},{"title":38,"path":168,"stem":169},{"title":171,"path":172,"stem":173,"children":255,"page":53},[256,257,258,259],{"title":176,"path":177,"stem":178},{"title":180,"path":181,"stem":182},{"title":184,"path":185,"stem":186},{"title":188,"path":189,"stem":190},{"title":192,"path":193,"stem":194},{"title":196,"path":197,"stem":198,"children":262},[263,264,265,266,267],{"title":196,"path":197,"stem":198},{"title":202,"path":203,"stem":204},{"title":206,"path":207,"stem":208},{"title":210,"path":211,"stem":212},{"title":214,"path":215,"stem":216},{"title":269,"path":35,"stem":270,"children":271},"Bot Detector","docs\u002Fbot-detection\u002Findex",[272,273,276,280,284,303,377,380,383],{"title":269,"path":35,"stem":270},{"title":14,"path":274,"stem":275},"\u002Fdocs\u002Fbot-detection\u002Fgetting-started","docs\u002Fbot-detection\u002F00.getting-started",{"title":277,"path":278,"stem":279},"CLI","\u002Fdocs\u002Fbot-detection\u002Fcli","docs\u002Fbot-detection\u002F01.cli",{"title":281,"path":282,"stem":283},"Data Sources","\u002Fdocs\u002Fbot-detection\u002Fdata-sources","docs\u002Fbot-detection\u002F02.data-sources",{"title":171,"path":285,"stem":286,"children":287,"page":53},"\u002Fdocs\u002Fbot-detection\u002Fguides","docs\u002Fbot-detection\u002F03.guides",[288,292,296,299],{"title":289,"path":290,"stem":291},"Custom Checkers","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom","docs\u002Fbot-detection\u002F03.guides\u002FCUSTOM",{"title":293,"path":294,"stem":295},"Scheduling Database Generation","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate","docs\u002Fbot-detection\u002F03.guides\u002FGENERATE",{"title":116,"path":297,"stem":298},"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Flogging","docs\u002Fbot-detection\u002F03.guides\u002FLOGGING",{"title":300,"path":301,"stem":302},"Score Modes and Reputation Healing","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fscore","docs\u002Fbot-detection\u002F03.guides\u002FSCORE",{"title":304,"path":305,"stem":306,"children":307},"Checkers","\u002Fdocs\u002Fbot-detection\u002Fcheckers","docs\u002Fbot-detection\u002F04.checkers\u002Findex",[308,309,313,317,321,325,329,333,337,341,345,349,353,357,361,365,369,373],{"title":304,"path":305,"stem":306},{"title":310,"path":311,"stem":312},"IP Validation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fip-validation","docs\u002Fbot-detection\u002F04.checkers\u002F01.ip-validation",{"title":314,"path":315,"stem":316},"Good \u002F Bad Bot Verification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgood-bots","docs\u002Fbot-detection\u002F04.checkers\u002F02.good-bots",{"title":318,"path":319,"stem":320},"Browser & Device Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbrowser-device","docs\u002Fbot-detection\u002F04.checkers\u002F03.browser-device",{"title":322,"path":323,"stem":324},"Locale Map","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Flocale-map","docs\u002Fbot-detection\u002F04.checkers\u002F04.locale-map",{"title":326,"path":327,"stem":328},"Known Threats","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-threats","docs\u002Fbot-detection\u002F04.checkers\u002F05.known-threats",{"title":330,"path":331,"stem":332},"ASN Classification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fasn-classification","docs\u002Fbot-detection\u002F04.checkers\u002F06.asn-classification",{"title":334,"path":335,"stem":336},"Tor Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftor-analysis","docs\u002Fbot-detection\u002F04.checkers\u002F07.tor-analysis",{"title":338,"path":339,"stem":340},"Timezone Consistency","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftimezone-consistency","docs\u002Fbot-detection\u002F04.checkers\u002F08.timezone-consistency",{"title":342,"path":343,"stem":344},"Honeypot","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fhoneypot","docs\u002Fbot-detection\u002F04.checkers\u002F09.honeypot",{"title":346,"path":347,"stem":348},"Known Bad IPs","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips","docs\u002Fbot-detection\u002F04.checkers\u002F10.known-bad-ips",{"title":350,"path":351,"stem":352},"Behavior Rate","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbehavior-rate","docs\u002Fbot-detection\u002F04.checkers\u002F11.behavior-rate",{"title":354,"path":355,"stem":356},"Proxy \u002F ISP \u002F Cookie","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fproxy-isp-cookies","docs\u002Fbot-detection\u002F04.checkers\u002F12.proxy-isp-cookies",{"title":358,"path":359,"stem":360},"Session Coherence","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fsession-coherence","docs\u002Fbot-detection\u002F04.checkers\u002F13.session-coherence",{"title":362,"path":363,"stem":364},"Velocity Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fvelocity-fingerprint","docs\u002Fbot-detection\u002F04.checkers\u002F14.velocity-fingerprint",{"title":366,"path":367,"stem":368},"UA & Header Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fua-header","docs\u002Fbot-detection\u002F04.checkers\u002F15.ua-header",{"title":370,"path":371,"stem":372},"Geolocation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgeolocation","docs\u002Fbot-detection\u002F04.checkers\u002F16.geolocation",{"title":374,"path":375,"stem":376},"Known Bad User-Agents","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ua","docs\u002Fbot-detection\u002F04.checkers\u002F17.known-bad-ua",{"title":38,"path":378,"stem":379},"\u002Fdocs\u002Fbot-detection\u002Fsecurity","docs\u002Fbot-detection\u002F04.security",{"title":196,"path":381,"stem":382},"\u002Fdocs\u002Fbot-detection\u002Fapi","docs\u002Fbot-detection\u002F05.api",{"title":192,"path":384,"stem":385},"\u002Fdocs\u002Fbot-detection\u002Fconfiguration","docs\u002Fbot-detection\u002F06.configuration",{"title":387,"path":11,"stem":388,"children":389},"Introduction","docs\u002Fgetting-started\u002Findex",[390],{"title":387,"path":11,"stem":388},{"title":27,"path":29,"stem":392,"children":393},"docs\u002Fiam\u002Findex",[394,395,398,533,536,552,555],{"title":27,"path":29,"stem":392},{"title":14,"path":396,"stem":397},"\u002Fdocs\u002Fiam\u002Fgetting-started","docs\u002Fiam\u002F00.getting-started",{"title":83,"path":399,"stem":400,"children":401},"\u002Fdocs\u002Fiam\u002Fessentials","docs\u002Fiam\u002F01.essentials\u002Findex",[402,403,407,411,415,419,423,427,431,435,439,443,446,450,454,458,462,465,469,473,476,480,483],{"title":83,"path":399,"stem":400},{"title":404,"path":405,"stem":406},"Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Ftokens","docs\u002Fiam\u002F01.essentials\u002F00.tokens",{"title":408,"path":409,"stem":410},"Access Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens","docs\u002Fiam\u002F01.essentials\u002F01.access-tokens",{"title":412,"path":413,"stem":414},"Refresh Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens","docs\u002Fiam\u002F01.essentials\u002F02.refresh-tokens",{"title":416,"path":417,"stem":418},"Anomaly Detection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies","docs\u002Fiam\u002F01.essentials\u002F03.anomalies",{"title":420,"path":421,"stem":422},"Signup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup","docs\u002Fiam\u002F01.essentials\u002F04.signup",{"title":424,"path":425,"stem":426},"Login","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin","docs\u002Fiam\u002F01.essentials\u002F05.login",{"title":428,"path":429,"stem":430},"Logout","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout","docs\u002Fiam\u002F01.essentials\u002F06.logout",{"title":432,"path":433,"stem":434},"OAuth","\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth","docs\u002Fiam\u002F01.essentials\u002F07.oauth",{"title":436,"path":437,"stem":438},"Magic Links","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links","docs\u002Fiam\u002F01.essentials\u002F08.magic-links",{"title":440,"path":441,"stem":442},"Emails","\u002Fdocs\u002Fiam\u002Fessentials\u002Femails","docs\u002Fiam\u002F01.essentials\u002F09.emails",{"title":120,"path":444,"stem":445},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa","docs\u002Fiam\u002F01.essentials\u002F10.mfa",{"title":447,"path":448,"stem":449},"Fingerprinting","\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting","docs\u002Fiam\u002F01.essentials\u002F11.fingerprinting",{"title":451,"path":452,"stem":453},"Backend for Frontend","\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff","docs\u002Fiam\u002F01.essentials\u002F12.bff",{"title":455,"path":456,"stem":457},"HMAC Authentication","\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac","docs\u002Fiam\u002F01.essentials\u002F13.hmac",{"title":459,"path":460,"stem":461},"XSS Protection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss","docs\u002Fiam\u002F01.essentials\u002F14.xss",{"title":116,"path":463,"stem":464},"\u002Fdocs\u002Fiam\u002Fessentials\u002Flogging","docs\u002Fiam\u002F01.essentials\u002F15.logging",{"title":466,"path":467,"stem":468},"Rate Limiting","\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F16.rate-limiting",{"title":470,"path":471,"stem":472},"Database","\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase","docs\u002Fiam\u002F01.essentials\u002F17.database",{"title":112,"path":474,"stem":475},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fcookies","docs\u002Fiam\u002F01.essentials\u002F18.cookies",{"title":477,"path":478,"stem":479},"Service Startup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice","docs\u002Fiam\u002F01.essentials\u002F19.service",{"title":130,"path":481,"stem":482},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset","docs\u002Fiam\u002F01.essentials\u002F20.password-reset",{"title":484,"path":485,"stem":486,"children":487},"API Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi","docs\u002Fiam\u002F01.essentials\u002F21.api\u002Findex",[488,489,493,497,527,530],{"title":484,"path":485,"stem":486},{"title":490,"path":491,"stem":492},"Creating Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fcreation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F00.creation",{"title":494,"path":495,"stem":496},"Verifying Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F01.verification",{"title":498,"path":499,"stem":500,"children":501},"Manage Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002Findex",[502,503,507,511,515,519,523],{"title":498,"path":499,"stem":500},{"title":504,"path":505,"stem":506},"Privileges","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fprivilege","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F00.privilege",{"title":508,"path":509,"stem":510},"Revocation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frevocation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F01.revocation",{"title":512,"path":513,"stem":514},"Rotation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frotation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F02.rotation",{"title":516,"path":517,"stem":518},"IP Restriction","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fip-updates","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F03.ip-updates",{"title":520,"path":521,"stem":522},"Metadata","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fmetadata","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F04.metadata",{"title":524,"path":525,"stem":526},"Token Listing","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Flist","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F05.list",{"title":466,"path":528,"stem":529},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F03.rate-limiting",{"title":38,"path":531,"stem":532},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fsecurity","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F04.security",{"title":38,"path":534,"stem":535},"\u002Fdocs\u002Fiam\u002Fsecurity","docs\u002Fiam\u002F02.security",{"title":171,"path":537,"stem":538,"children":539,"page":53},"\u002Fdocs\u002Fiam\u002Fguides","docs\u002Fiam\u002F03.guides",[540,544,548],{"title":541,"path":542,"stem":543},"Deployment","\u002Fdocs\u002Fiam\u002Fguides\u002Fdeployment","docs\u002Fiam\u002F03.guides\u002Fdeployment",{"title":545,"path":546,"stem":547},"Operation Scripts","\u002Fdocs\u002Fiam\u002Fguides\u002Foperation-scripts","docs\u002Fiam\u002F03.guides\u002Foperation-scripts",{"title":549,"path":550,"stem":551},"Role-Based Access Control","\u002Fdocs\u002Fiam\u002Fguides\u002Frbac","docs\u002Fiam\u002F03.guides\u002Frbac",{"title":192,"path":553,"stem":554},"\u002Fdocs\u002Fiam\u002Fconfiguration","docs\u002Fiam\u002F04.configuration",{"title":556,"path":557,"stem":558,"children":559,"page":53},"Api","\u002Fdocs\u002Fiam\u002Fapi","docs\u002Fiam\u002F05.API",[560,563,566],{"title":196,"path":561,"stem":562},"\u002Fdocs\u002Fiam\u002Fapi\u002Fapi","docs\u002Fiam\u002F05.API\u002F00.api",{"title":206,"path":564,"stem":565},"\u002Fdocs\u002Fiam\u002Fapi\u002Fmiddlewares","docs\u002Fiam\u002F05.API\u002F02.middlewares",{"title":202,"path":567,"stem":568},"\u002Fdocs\u002Fiam\u002Fapi\u002Froutes","docs\u002Fiam\u002F05.API\u002F03.routes",{"title":40,"path":42,"stem":570,"children":571},"docs\u002Fshield-base\u002Findex",[572,573,576,580,621,625,629,633],{"title":40,"path":42,"stem":570},{"title":14,"path":574,"stem":575},"\u002Fdocs\u002Fshield-base\u002Fgetting-started","docs\u002Fshield-base\u002F00.getting-started",{"title":577,"path":578,"stem":579},"CLI Reference","\u002Fdocs\u002Fshield-base\u002Fcli","docs\u002Fshield-base\u002F01.cli",{"title":281,"path":581,"stem":582,"children":583},"\u002Fdocs\u002Fshield-base\u002Fdata-sources","docs\u002Fshield-base\u002F02.data-sources\u002Findex",[584,585,589,593,597,601,605,609,613,617],{"title":281,"path":581,"stem":582},{"title":586,"path":587,"stem":588},"BGP \u002F ASN","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fbgp","docs\u002Fshield-base\u002F02.data-sources\u002Fbgp",{"title":590,"path":591,"stem":592},"City Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcity","docs\u002Fshield-base\u002F02.data-sources\u002Fcity",{"title":594,"path":595,"stem":596},"Country Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcountry","docs\u002Fshield-base\u002F02.data-sources\u002Fcountry",{"title":598,"path":599,"stem":600},"Verified Crawlers","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcrawlers","docs\u002Fshield-base\u002F02.data-sources\u002Fcrawlers",{"title":602,"path":603,"stem":604},"Disposable Emails","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Femail","docs\u002Fshield-base\u002F02.data-sources\u002Femail",{"title":606,"path":607,"stem":608},"FireHOL Threat Intelligence","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ffirehol","docs\u002Fshield-base\u002F02.data-sources\u002Ffirehol",{"title":610,"path":611,"stem":612},"Proxy Detection","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fproxy","docs\u002Fshield-base\u002F02.data-sources\u002Fproxy",{"title":614,"path":615,"stem":616},"Tor Nodes","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ftor","docs\u002Fshield-base\u002F02.data-sources\u002Ftor",{"title":618,"path":619,"stem":620},"Suspicious User-Agents","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fuseragent","docs\u002Fshield-base\u002F02.data-sources\u002Fuseragent",{"title":622,"path":623,"stem":624},"Programmatic Usage","\u002Fdocs\u002Fshield-base\u002Fusage","docs\u002Fshield-base\u002F03.usage",{"title":626,"path":627,"stem":628},"Custom Data Sources","\u002Fdocs\u002Fshield-base\u002Fcustom-data-sources","docs\u002Fshield-base\u002F04.custom-data-sources",{"title":630,"path":631,"stem":632},"TypeScript Types","\u002Fdocs\u002Fshield-base\u002Ftypes","docs\u002Fshield-base\u002F05.types",{"title":196,"path":634,"stem":635},"\u002Fdocs\u002Fshield-base\u002Fapi","docs\u002Fshield-base\u002F06.api",{"title":214,"path":48,"stem":637,"children":638},"docs\u002Futils\u002Findex",[639,640,657,690,787],{"title":214,"path":48,"stem":637},{"title":641,"path":642,"stem":643,"children":644,"page":53},"Eslint","\u002Fdocs\u002Futils\u002Feslint","docs\u002Futils\u002Feslint",[645,649,653],{"title":646,"path":647,"stem":648},"React Config","\u002Fdocs\u002Futils\u002Feslint\u002Freact","docs\u002Futils\u002Feslint\u002Freact",{"title":650,"path":651,"stem":652},"TypeScript Config","\u002Fdocs\u002Futils\u002Feslint\u002Ftypescript","docs\u002Futils\u002Feslint\u002Ftypescript",{"title":654,"path":655,"stem":656},"Vue Config","\u002Fdocs\u002Futils\u002Feslint\u002Fvue","docs\u002Futils\u002Feslint\u002Fvue",{"title":658,"path":659,"stem":660,"children":661,"page":53},"Server","\u002Fdocs\u002Futils\u002Fserver","docs\u002Futils\u002Fserver",[662,666,670,674,678,682,686],{"title":663,"path":664,"stem":665},"Encryption","\u002Fdocs\u002Futils\u002Fserver\u002Fencryption","docs\u002Futils\u002Fserver\u002Fencryption",{"title":667,"path":668,"stem":669},"Path Resolver","\u002Fdocs\u002Futils\u002Fserver\u002Fpathresolver","docs\u002Futils\u002Fserver\u002FpathResolver",{"title":671,"path":672,"stem":673},"File Replacements","\u002Fdocs\u002Futils\u002Fserver\u002Freplace","docs\u002Futils\u002Fserver\u002Freplace",{"title":675,"path":676,"stem":677},"run","\u002Fdocs\u002Futils\u002Fserver\u002Frun","docs\u002Futils\u002Fserver\u002Frun",{"title":679,"path":680,"stem":681},"scheduleTask","\u002Fdocs\u002Futils\u002Fserver\u002Fscheduletask","docs\u002Futils\u002Fserver\u002FscheduleTask",{"title":683,"path":684,"stem":685},"spawnRun","\u002Fdocs\u002Futils\u002Fserver\u002Fspawnrun","docs\u002Futils\u002Fserver\u002FspawnRun",{"title":687,"path":688,"stem":689},"uploadCsv","\u002Fdocs\u002Futils\u002Fserver\u002Fuploadcsv","docs\u002Futils\u002Fserver\u002FuploadCsv",{"title":691,"path":692,"stem":693,"children":694,"page":53},"Shared","\u002Fdocs\u002Futils\u002Fshared","docs\u002Futils\u002Fshared",[695,699,703,707,711,715,719,723,727,731,735,739,743,747,751,755,759,763,767,771,775,779,783],{"title":696,"path":697,"stem":698},"BatchQueue","\u002Fdocs\u002Futils\u002Fshared\u002Fbatchqueue","docs\u002Futils\u002Fshared\u002FbatchQueue",{"title":700,"path":701,"stem":702},"capitalize","\u002Fdocs\u002Futils\u002Fshared\u002Fcapitalize","docs\u002Futils\u002Fshared\u002Fcapitalize",{"title":704,"path":705,"stem":706},"chunkProcess","\u002Fdocs\u002Futils\u002Fshared\u002Fchunkprocess","docs\u002Futils\u002Fshared\u002FchunkProcess",{"title":708,"path":709,"stem":710},"cleanObject","\u002Fdocs\u002Futils\u002Fshared\u002Fcleanobject","docs\u002Futils\u002Fshared\u002FcleanObject",{"title":712,"path":713,"stem":714},"createConfigManager","\u002Fdocs\u002Futils\u002Fshared\u002Fconfigurationdefiner","docs\u002Futils\u002Fshared\u002FconfigurationDefiner",{"title":716,"path":717,"stem":718},"debounce","\u002Fdocs\u002Futils\u002Fshared\u002Fdebounce","docs\u002Futils\u002Fshared\u002Fdebounce",{"title":720,"path":721,"stem":722},"ensureArray","\u002Fdocs\u002Futils\u002Fshared\u002Fensurearray","docs\u002Futils\u002Fshared\u002FensureArray",{"title":724,"path":725,"stem":726},"fetchWithRetry","\u002Fdocs\u002Futils\u002Fshared\u002Ffetchwithretry","docs\u002Futils\u002Fshared\u002FfetchWithRetry",{"title":728,"path":729,"stem":730},"filterEmptyValues","\u002Fdocs\u002Futils\u002Fshared\u002Ffilteremptyvalues","docs\u002Futils\u002Fshared\u002FfilterEmptyValues",{"title":732,"path":733,"stem":734},"findStringsInObject","\u002Fdocs\u002Futils\u002Fshared\u002Ffindobjectvalues","docs\u002Futils\u002Fshared\u002FfindObjectValues",{"title":736,"path":737,"stem":738},"fisherYatesShuffle","\u002Fdocs\u002Futils\u002Fshared\u002Ffisheryatesshuffle","docs\u002Futils\u002Fshared\u002FfisherYatesShuffle",{"title":740,"path":741,"stem":742},"getRandomImage","\u002Fdocs\u002Futils\u002Fshared\u002Fgetrandomimage","docs\u002Futils\u002Fshared\u002FgetRandomImage",{"title":744,"path":745,"stem":746},"isObjectHasValues","\u002Fdocs\u002Futils\u002Fshared\u002Fisobjecthasvalues","docs\u002Futils\u002Fshared\u002FisObjectHasValues",{"title":748,"path":749,"stem":750},"isAsyncOrPromise","\u002Fdocs\u002Futils\u002Fshared\u002Fispromise","docs\u002Futils\u002Fshared\u002FisPromise",{"title":752,"path":753,"stem":754},"MiniCache","\u002Fdocs\u002Futils\u002Fshared\u002Fminicache","docs\u002Futils\u002Fshared\u002FminiCache",{"title":756,"path":757,"stem":758},"parseCookies","\u002Fdocs\u002Futils\u002Fshared\u002Fparserawcookies","docs\u002Futils\u002Fshared\u002FparseRawCookies",{"title":760,"path":761,"stem":762},"safeAction","\u002Fdocs\u002Futils\u002Fshared\u002Fpromiselocker","docs\u002Futils\u002Fshared\u002FpromiseLocker",{"title":764,"path":765,"stem":766},"Random","\u002Fdocs\u002Futils\u002Fshared\u002Frandom","docs\u002Futils\u002Fshared\u002Frandom",{"title":768,"path":769,"stem":770},"range","\u002Fdocs\u002Futils\u002Fshared\u002Frange","docs\u002Futils\u002Fshared\u002Frange",{"title":772,"path":773,"stem":774},"rateLimiters","\u002Fdocs\u002Futils\u002Fshared\u002Fratelimiters","docs\u002Futils\u002Fshared\u002FrateLimiters",{"title":776,"path":777,"stem":778},"safeObjectMerge","\u002Fdocs\u002Futils\u002Fshared\u002Fsafemerge","docs\u002Futils\u002Fshared\u002FsafeMerge",{"title":780,"path":781,"stem":782},"textTruncation","\u002Fdocs\u002Futils\u002Fshared\u002Ftexttruncation","docs\u002Futils\u002Fshared\u002FtextTruncation",{"title":784,"path":785,"stem":786},"validateZodSchema","\u002Fdocs\u002Futils\u002Fshared\u002Fvalidatezodschema","docs\u002Futils\u002Fshared\u002FvalidateZodSchema",{"title":788,"path":789,"stem":790,"children":791},"Utility Types","\u002Fdocs\u002Futils\u002Ftypes","docs\u002Futils\u002Ftypes\u002Findex",[792,793,797,801,805,809,813,817,821,825],{"title":788,"path":789,"stem":790},{"title":794,"path":795,"stem":796},"Brand","\u002Fdocs\u002Futils\u002Ftypes\u002Fbrand","docs\u002Futils\u002Ftypes\u002FBrand",{"title":798,"path":799,"stem":800},"DeepPartial","\u002Fdocs\u002Futils\u002Ftypes\u002Fdeeppartial","docs\u002Futils\u002Ftypes\u002FDeepPartial",{"title":802,"path":803,"stem":804},"Merge","\u002Fdocs\u002Futils\u002Ftypes\u002Fmerge","docs\u002Futils\u002Ftypes\u002FMerge",{"title":806,"path":807,"stem":808},"NonNullable","\u002Fdocs\u002Futils\u002Ftypes\u002Fnonnullable","docs\u002Futils\u002Ftypes\u002FNonNullable",{"title":810,"path":811,"stem":812},"Prettify","\u002Fdocs\u002Futils\u002Ftypes\u002Fprettify","docs\u002Futils\u002Ftypes\u002FPrettify",{"title":814,"path":815,"stem":816},"PromiseType","\u002Fdocs\u002Futils\u002Ftypes\u002Fpromisetype","docs\u002Futils\u002Ftypes\u002FPromiseType",{"title":818,"path":819,"stem":820},"RequireKeys","\u002Fdocs\u002Futils\u002Ftypes\u002Frequirekeys","docs\u002Futils\u002Ftypes\u002FRequireKeys",{"title":822,"path":823,"stem":824},"StandardResponse","\u002Fdocs\u002Futils\u002Ftypes\u002Fstandardresponse","docs\u002Futils\u002Ftypes\u002FStandardResponse",{"title":826,"path":827,"stem":828},"ValueOf","\u002Fdocs\u002Futils\u002Ftypes\u002Fvalueof","docs\u002Futils\u002Ftypes\u002FValueOf",{"id":4,"extension":5,"links":830,"meta":841,"stem":62,"__hash__":63},[831,839,840],{"nested":8,"label":9,"icon":10,"to":11,"children":832},[833,834,835,836,837,838],{"label":14,"icon":15,"to":11,"description":16,"github":17,"badge":18},{"label":20,"icon":21,"to":22,"description":23,"github":24,"badge":25},{"label":27,"icon":28,"to":29,"description":30,"github":31,"badge":25},{"label":33,"icon":34,"to":35,"description":36,"github":37,"badge":38},{"label":40,"icon":41,"to":42,"description":43,"github":44,"badge":38},{"label":46,"icon":47,"to":48,"description":49,"github":50,"badge":51},{"nested":53,"label":54,"icon":55,"to":56},{"nested":53,"label":58,"icon":59,"to":60},{},{"id":843,"title":38,"body":844,"description":2347,"extension":2348,"icon":2349,"meta":2350,"module":2351,"navigation":8,"path":168,"rawbody":2352,"seo":2353,"stem":169,"__hash__":2354},"docs\u002Fdocs\u002Fauth-h3client\u002F04.security.md",{"type":845,"value":846,"toc":2306},"minimark",[847,863,866,869,874,886,891,921,984,988,991,1097,1103,1105,1109,1118,1122,1138,1218,1231,1235,1262,1265,1313,1335,1337,1341,1345,1352,1362,1372,1387,1391,1401,1438,1447,1449,1453,1456,1460,1467,1524,1531,1545,1556,1560,1566,1572,1578,1580,1584,1590,1594,1602,1606,1617,1621,1637,1647,1657,1659,1663,1667,1681,1726,1730,1743,1747,1753,1867,1873,1985,1991,1993,1997,2001,2008,2014,2018,2038,2049,2053,2059,2061,2065,2068,2072,2083,2094,2098,2103,2117,2119,2123,2127,2224,2228,2233,2296,2302],[848,849,850,851,858,859,862],"p",{},"Auth H3 Client is built on the same ",[852,853,857],"a",{"href":854,"rel":855},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDefense_in_depth_(computing)",[856],"nofollow","Defense in Depth"," principle as the ",[852,860,861],{"href":534},"IAM service",". Every layer assumes the layers around it can fail and adds its own protection. A missing CSRF token does not reach your handler. An unrecognized IP does not reach the session check. A replayed service request does not pass the HMAC signature verification. No single control stands alone.",[848,864,865],{},"This page documents every security mechanism in the BFF layer. Where a control extends or delegates to the IAM service, you will find a link to the corresponding IAM documentation.",[867,868],"hr",{},[870,871,873],"h2",{"id":872},"request-lifecycle","Request lifecycle",[848,875,876,877,881,882,885],{},"Every request that enters the Auth H3 Client layer passes through a sequence of\nchecks before it reaches the final handler. When the Nuxt module runs with\n",[878,879,880],"code",{},"enableMiddleware: true",", its bundled global middleware applies three of these\nchecks to all non-skipped requests before route-level enforcement begins. When\n",[878,883,884],{},"enableMiddleware: false",", you are responsible for registering any equivalent\nbrowser middleware yourself.",[887,888,890],"h3",{"id":889},"global-middleware-chain","Global middleware chain",[848,892,893,894,897,898,901,902,905,906,909,910,913,914,905,917,920],{},"The global middleware in ",[878,895,896],{},"server\u002Fmiddleware\u002Fauth.ts"," skips requests that do not need checking: ",[878,899,900],{},"HEAD"," requests, ",[878,903,904],{},"\u002Fapi\u002Fhealth",", ",[878,907,908],{},"\u002F_nuxt"," static assets, ",[878,911,912],{},"\u002Fapi\u002F_mdc",", and requests arriving from loopback addresses (",[878,915,916],{},"127.0.0.1",[878,918,919],{},"::1","). All other requests pass through the following chain in order:",[922,923,925,932,939,945,967,973],"steps",{"level":924},"4",[926,927,929],"h4",{"id":928},"isipvalid",[878,930,931],{},"isIPValid",[848,933,934,935,938],{},"Extracts the client IP address from the request headers and validates it with ",[878,936,937],{},"net.isIP()",". Rejects any request with an invalid or missing IP address before any network call is made. This check has no latency cost.",[926,940,942],{"id":941},"botdetectormiddleware",[878,943,944],{},"botDetectorMiddleware",[848,946,947,948,951,952,955,956,959,960,963,964,966],{},"Forwards the visitor fingerprint to the IAM ",[878,949,950],{},"\u002Fcheck"," endpoint. Returns HTTP 403 when the visitor score exceeds the configured ban threshold. When ",[878,953,954],{},"enableFireWallBans"," is ",[878,957,958],{},"true",", calls ",[878,961,962],{},"banIp"," to block the IP at the firewall level. See ",[852,965,33],{"href":109}," for the full flow.",[926,968,970],{"id":969},"generatecsrfcookie",[878,971,972],{},"generateCsrfCookie",[848,974,975,976,979,980,983],{},"Mints a signed ",[878,977,978],{},"__Host-csrf"," cookie when one is not already present. The token is a 32-byte hex string signed with an expiring HMAC payload. See ",[852,981,97],{"href":982},"#csrf-protection"," below for how the cookie is constructed and verified.",[887,985,987],{"id":986},"route-level-enforcement","Route-level enforcement",[848,989,990],{},"Inside your own route handlers, the event handler wrappers enforce authentication and CSRF requirements:",[992,993,994,1007],"table",{},[995,996,997],"thead",{},[998,999,1000,1004],"tr",{},[1001,1002,1003],"th",{},"Wrapper",[1001,1005,1006],{},"Enforces",[1008,1009,1010,1021,1031,1041,1051,1067,1077,1087],"tbody",{},[998,1011,1012,1018],{},[1013,1014,1015],"td",{},[878,1016,1017],{},"defineAuthenticatedEventHandler",[1013,1019,1020],{},"HMAC signature, token rotation, session check",[998,1022,1023,1028],{},[1013,1024,1025],{},[878,1026,1027],{},"defineOptionalAuthenticationEvent",[1013,1029,1030],{},"Same as above, continues as guest on failure",[998,1032,1033,1038],{},[1013,1034,1035],{},[878,1036,1037],{},"defineVerifiedCsrfHandler",[1013,1039,1040],{},"CSRF cookie and header match",[998,1042,1043,1048],{},[1013,1044,1045],{},[878,1046,1047],{},"defineAuthenticatedEventPostHandlers",[1013,1049,1050],{},"Authentication + CSRF + POST method",[998,1052,1053,1058],{},[1013,1054,1055],{},[878,1056,1057],{},"defineAuthenticatePublicApi",[1013,1059,1060,1063,1064],{},[878,1061,1062],{},"X-API-KEY"," verification against IAM ",[878,1065,1066],{},"\u002Fapi\u002Fpublic\u002Fverify",[998,1068,1069,1074],{},[1013,1070,1071],{},[878,1072,1073],{},"defineApiManagementHandler",[1013,1075,1076],{},"Authentication + CSRF + POST method + 2 KB JSON body limit + server-side API token identity mapping",[998,1078,1079,1084],{},[1013,1080,1081],{},[878,1082,1083],{},"defineVerifiedMagicLinkGetHandler",[1013,1085,1086],{},"GET method, required cookies, magic link schema, IAM link verification",[998,1088,1089,1094],{},[1013,1090,1091],{},[878,1092,1093],{},"defineMfaCodeVerifierHandler",[1013,1095,1096],{},"POST method, CSRF, body limit, link params, 7-digit code, token rotation",[848,1098,1099,1100,1102],{},"See ",[852,1101,93],{"href":94}," for the full wrappers reference.",[867,1104],{},[870,1106,1108],{"id":1107},"csrf-protection","CSRF protection",[848,1110,1111,1112,1117],{},"The module implements the ",[852,1113,1116],{"href":1114,"rel":1115},"https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FCross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#alternative-using-a-double-submit-cookie-pattern",[856],"double-submit cookie"," pattern. The CSRF token is issued as a cookie and must also appear as a request header on every state-changing request.",[887,1119,1121],{"id":1120},"cookie-issuance","Cookie issuance",[848,1123,1124,1126,1127,1130,1131,1134,1135,1137],{},[878,1125,972],{}," runs automatically in global middleware. It generates a 32-byte random token with ",[878,1128,1129],{},"crypto.randomBytes(32).toString('hex')",", signs it into a ",[878,1132,1133],{},"base64(value).base64('csrf').expiry.hmac"," payload, and sets it as ",[878,1136,978],{},":",[992,1139,1140,1150],{},[995,1141,1142],{},[998,1143,1144,1147],{},[1001,1145,1146],{},"Attribute",[1001,1148,1149],{},"Value",[1008,1151,1152,1165,1177,1188,1201],{},[998,1153,1154,1159],{},[1013,1155,1156],{},[878,1157,1158],{},"HttpOnly",[1013,1160,1161,1164],{},[878,1162,1163],{},"false"," (the client must be able to read it to inject it as a header)",[998,1166,1167,1172],{},[1013,1168,1169],{},[878,1170,1171],{},"SameSite",[1013,1173,1174],{},[878,1175,1176],{},"Strict",[998,1178,1179,1184],{},[1013,1180,1181],{},[878,1182,1183],{},"Secure",[1013,1185,1186],{},[878,1187,958],{},[998,1189,1190,1195],{},[1013,1191,1192],{},[878,1193,1194],{},"MaxAge",[1013,1196,1197,1200],{},[878,1198,1199],{},"1800"," seconds (30 minutes)",[998,1202,1203,1208],{},[1013,1204,1205],{},[878,1206,1207],{},"Path",[1013,1209,1210,1213,1214,1217],{},[878,1211,1212],{},"\u002F"," (enforced by the ",[878,1215,1216],{},"__Host-"," prefix)",[848,1219,1220,1221,1223,1224,905,1227,1230],{},"The ",[878,1222,1216],{}," prefix tells the browser to enforce ",[878,1225,1226],{},"Secure: true",[878,1228,1229],{},"Path: \u002F",", and to strip the domain attribute. This prevents the cookie from being set by a subdomain and makes it impossible to replicate without HTTPS.",[887,1232,1234],{"id":1233},"verification","Verification",[848,1236,1237,1240,1241,1243,1244,1247,1248,1251,1252,1255,1256,1261],{},[878,1238,1239],{},"verifyCsrfCookie"," reads the ",[878,1242,978],{}," cookie, verifies the HMAC signature and expiry using ",[878,1245,1246],{},"verifySignedValue",", and then checks that the ",[878,1249,1250],{},"X-CSRF-Token"," request header equals the token stored inside the cookie payload. The comparison uses ",[878,1253,1254],{},"isSameBuffer",", a timing-safe HMAC comparison that prevents ",[852,1257,1260],{"href":1258,"rel":1259},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTiming_attack",[856],"timing side-channel attacks",".",[848,1263,1264],{},"Verification fails with the following HTTP 403 error codes:",[992,1266,1267,1277],{},[995,1268,1269],{},[998,1270,1271,1274],{},[1001,1272,1273],{},"Code",[1001,1275,1276],{},"Meaning",[1008,1278,1279,1291,1301],{},[998,1280,1281,1286],{},[1013,1282,1283],{},[878,1284,1285],{},"CSRF_MISSING",[1013,1287,1220,1288,1290],{},[878,1289,978],{}," cookie is absent",[998,1292,1293,1298],{},[1013,1294,1295],{},[878,1296,1297],{},"CSRF_INVALID",[1013,1299,1300],{},"The cookie signature or expiry check failed",[998,1302,1303,1308],{},[1013,1304,1305],{},[878,1306,1307],{},"TOKEN_INVALID",[1013,1309,1220,1310,1312],{},[878,1311,1250],{}," header does not match the cookie payload",[848,1314,1315,1316,1318,1319,1322,1323,1326,1327,1330,1331,1334],{},"On the client, ",[878,1317,160],{}," injects the CSRF token automatically from ",[878,1320,1321],{},"getCsrfToken()",", which reads the raw value from ",[878,1324,1325],{},"document.cookie",". On the server, the CSRF token is extracted from the forwarded request headers. See ",[852,1328,1329],{"href":98},"CSRF"," for the full flow and ",[878,1332,1333],{},"ApiContext"," usage.",[867,1336],{},[870,1338,1340],{"id":1339},"cookie-security","Cookie security",[887,1342,1344],{"id":1343},"signed-cookies","Signed cookies",[848,1346,1347,1348,1351],{},"Cookies that carry sensitive state are signed using ",[878,1349,1350],{},"createSignedValue",". The format is:",[1353,1354,1359],"pre",{"className":1355,"code":1357,"language":1358},[1356],"language-text","base64url(value) . base64url(keyword) . expiryTimestamp . hmacSha256Hex\n","text",[878,1360,1357],{"__ignoreMap":1361},"",[848,1363,1220,1364,1367,1368,1371],{},[878,1365,1366],{},"keyword"," parameter binds the signature to a specific purpose so that a valid CSRF cookie cannot be replayed as a different cookie type. The HMAC is computed with the ",[878,1369,1370],{},"cryptoCookiesSecret"," from the configuration.",[848,1373,1374,1376,1377,1379,1380,1383,1384,1386],{},[878,1375,1246],{}," parses this format, recomputes the HMAC, and uses ",[878,1378,1254],{}," for the comparison. A mismatched signature, expired timestamp, or malformed payload all return ",[878,1381,1382],{},"{ valid: false, payload: null }",". See ",[852,1385,112],{"href":113}," for the full inventory and attributes.",[887,1388,1390],{"id":1389},"cookie-prefixes","Cookie prefixes",[848,1392,1393,1394,1396,1397,1400],{},"All sensitive cookies use either the ",[878,1395,1216],{}," or ",[878,1398,1399],{},"__Secure-"," prefix:",[992,1402,1403,1413],{},[995,1404,1405],{},[998,1406,1407,1410],{},[1001,1408,1409],{},"Prefix",[1001,1411,1412],{},"Enforced attributes",[1008,1414,1415,1428],{},[998,1416,1417,1421],{},[1013,1418,1419],{},[878,1420,1216],{},[1013,1422,1423,905,1425,1427],{},[878,1424,1226],{},[878,1426,1229],{},", domain attribute stripped",[998,1429,1430,1434],{},[1013,1431,1432],{},[878,1433,1399],{},[1013,1435,1436],{},[878,1437,1226],{},[848,1439,1220,1440,1442,1443,1446],{},[878,1441,1216],{}," prefix, used for the CSRF cookie, provides the strongest protection: the browser will not send it on plain HTTP, will only send it on requests to the exact origin, and will refuse to set it from a subdomain. The access token cookie ",[878,1444,1445],{},"__Secure-a"," requires HTTPS but allows domain scoping for cross-subdomain token sharing.",[867,1448],{},[870,1450,1452],{"id":1451},"inter-service-authentication","Inter-service authentication",[848,1454,1455],{},"Traffic between the module and the IAM service runs on a private Docker network. This isolation is not sufficient alone. The module supports two independent authentication layers for every outbound request.",[887,1457,1459],{"id":1458},"hmac-request-signing","HMAC request signing",[848,1461,1462,1463,1466],{},"When ",[878,1464,1465],{},"enableHmac: true",", every request the module sends to the IAM service carries four signed headers:",[992,1468,1469,1479],{},[995,1470,1471],{},[998,1472,1473,1476],{},[1001,1474,1475],{},"Header",[1001,1477,1478],{},"Purpose",[1008,1480,1481,1491,1501,1511],{},[998,1482,1483,1488],{},[1013,1484,1485],{},[878,1486,1487],{},"X-Client-Id",[1013,1489,1490],{},"Identifies the gateway instance",[998,1492,1493,1498],{},[1013,1494,1495],{},[878,1496,1497],{},"X-Timestamp",[1013,1499,1500],{},"Millisecond timestamp for clock-skew detection",[998,1502,1503,1508],{},[1013,1504,1505],{},[878,1506,1507],{},"X-Request-Id",[1013,1509,1510],{},"A unique request ID for replay detection",[998,1512,1513,1518],{},[1013,1514,1515],{},[878,1516,1517],{},"X-Signature",[1013,1519,1520,1521],{},"HMAC-SHA256 of ",[878,1522,1523],{},"clientId:timestamp:method:path:requestId",[848,1525,1526,1527,1530],{},"The IAM service recomputes the signature with ",[878,1528,1529],{},"crypto.timingSafeEqual"," and rejects requests outside the configured clock-skew window. It also caches recent request IDs to reject replays. A container on the same Docker network without the shared secret cannot forge a valid request.",[848,1532,1533,1536,1537,1540,1541,1544],{},[878,1534,1535],{},"hmacSignatureMiddleware",", which runs inside every authenticated handler wrapper, generates these headers and stores them on ",[878,1538,1539],{},"event.context.authHeaders"," so that every ",[878,1542,1543],{},"serviceToService"," call to the IAM service carries a valid signature.",[848,1546,1547,1548,1551,1552,1555],{},"See the ",[852,1549,1550],{"href":181},"HMAC guide"," for configuration and the ",[852,1553,1554],{"href":456},"IAM HMAC documentation"," for the server-side verification flow.",[887,1557,1559],{"id":1558},"mutual-tls","Mutual TLS",[848,1561,1562,1565],{},[878,1563,1564],{},"getAuthAgent"," builds an Undici HTTP agent with optional client certificate and CA bundle. When configured, both the module and the IAM service present certificates and verify each other. This adds a transport-layer identity check on top of the application-layer HMAC signature.",[1567,1568,1569],"tip",{},[848,1570,1571],{},"HMAC and mTLS are independent. HMAC alone is sufficient for most private-network deployments. Add mTLS when your security policy or compliance requirements mandate encrypted, mutually authenticated internal traffic.",[848,1573,1547,1574,1577],{},[852,1575,1576],{"href":189},"mTLS guide"," for certificate generation and configuration.",[867,1579],{},[870,1581,1583],{"id":1582},"bot-detection-and-ip-validation","Bot detection and IP validation",[848,1585,1586,1587,1589],{},"The module integrates with the ",[852,1588,33],{"href":35}," service to screen incoming requests before any authentication logic runs.",[887,1591,1593],{"id":1592},"ip-validation","IP validation",[848,1595,1596,1598,1599,1601],{},[878,1597,931],{}," extracts the client IP from the request headers and validates it with ",[878,1600,937],{},". This check runs first, before any network call, and adds no latency.",[887,1603,1605],{"id":1604},"score-based-screening","Score-based screening",[848,1607,1608,1610,1611,1613,1614,1616],{},[878,1609,944],{}," forwards the visitor fingerprint to the IAM service ",[878,1612,950],{}," endpoint. The IAM service returns a score for the visitor based on the ",[852,1615,33],{"href":35}," pipeline. Visitors with a score at or above the configured ban threshold receive HTTP 403. The IAM service also updates the visitor record on each check.",[887,1618,1620],{"id":1619},"firewall-banning","Firewall banning",[848,1622,1462,1623,1626,1627,1629,1630,1632,1633,1636],{},[878,1624,1625],{},"enableFireWallBans: true",", the module calls ",[878,1628,962],{}," on any visitor that exceeds the ban threshold. ",[878,1631,962],{}," executes ",[878,1634,1635],{},"sudo ufw insert 1 deny from \u003Cip> to any"," to block the IP at the operating system firewall level, before the request even enters the application.",[1638,1639,1640],"warning",{},[848,1641,1642,1643,1646],{},"Firewall banning requires Linux with UFW installed and ",[878,1644,1645],{},"sudo"," privileges for the process user. Do not enable this option in environments where those conditions are not met.",[848,1648,1649,1650,1653,1654,1656],{},"In custom handlers, ",[878,1651,1652],{},"checkForBots(event)"," calls ",[878,1655,950],{}," directly so you can apply bot detection outside of the global middleware chain.",[867,1658],{},[870,1660,1662],{"id":1661},"input-validation","Input validation",[887,1664,1666],{"id":1665},"body-size-limits","Body size limits",[848,1668,1669,1672,1673,1676,1677,1680],{},[878,1670,1671],{},"limitBytes(maxBytes)"," reads the request body before any parsing and throws HTTP 403 with code ",[878,1674,1675],{},"INVALID_CONTENT_TYPE"," if the byte length exceeds the limit. Passing ",[878,1678,1679],{},"limitBytes(0)"," rejects any body entirely. This limit is applied before JSON parsing, so a large payload cannot consume memory during deserialization.",[992,1682,1683,1693],{},[995,1684,1685],{},[998,1686,1687,1690],{},[1001,1688,1689],{},"Route type",[1001,1691,1692],{},"Limit",[1008,1694,1695,1703,1711,1719],{},[998,1696,1697,1700],{},[1013,1698,1699],{},"Login, signup, MFA, password reset",[1013,1701,1702],{},"1 KB",[998,1704,1705,1708],{},[1013,1706,1707],{},"Email change body",[1013,1709,1710],{},"1 MB",[998,1712,1713,1716],{},[1013,1714,1715],{},"MFA code submission",[1013,1717,1718],{},"8 MB",[998,1720,1721,1723],{},[1013,1722,428],{},[1013,1724,1725],{},"0 (no body allowed)",[887,1727,1729],{"id":1728},"content-type-enforcement","Content-type enforcement",[848,1731,1732,1735,1736,1739,1740,1742],{},[878,1733,1734],{},"contentType(expected)"," validates the ",[878,1737,1738],{},"Content-Type"," request header before the body is parsed. Requests with an unexpected or missing content type are rejected with HTTP 403 and code ",[878,1741,1675],{},". This prevents multipart, URL-encoded, or XML payloads from bypassing JSON-level input validation.",[887,1744,1746],{"id":1745},"xss-sanitization-pipeline","XSS sanitization pipeline",[848,1748,1749,1752],{},[878,1750,1751],{},"sanitizeInputString"," runs a seven-stage pipeline on every user-supplied string before it reaches business logic:",[922,1754,1755,1759,1766,1770,1779,1783,1794,1798,1807,1811,1814,1818,1835,1842],{"level":924},[926,1756,1758],{"id":1757},"length-guard","Length guard",[848,1760,1761,1762,1765],{},"Rejects input exceeding ",[878,1763,1764],{},"htmlSanitizer.maxAllowedInputLength",". Oversized input is rejected before entering the loop.",[926,1767,1769],{"id":1768},"unicode-normalization","Unicode normalization",[848,1771,1772,1773,1778],{},"Normalizes to ",[852,1774,1777],{"href":1775,"rel":1776},"https:\u002F\u002Funicode.org\u002Freports\u002Ftr15\u002F",[856],"NFKC",", collapses zero-width characters, strips soft hyphens and bidirectional overrides, and transliterates fullwidth ASCII back to standard ASCII.",[926,1780,1782],{"id":1781},"uri-decode","URI decode",[848,1784,1785,1786,1789,1790,1793],{},"Applies ",[878,1787,1788],{},"decodeURIComponent"," once. Malformed percent-encoding (e.g. ",[878,1791,1792],{},"%ZZ",") causes immediate rejection.",[926,1795,1797],{"id":1796},"iterative-decoding-loop","Iterative decoding loop",[848,1799,1800,1801,1803,1804,1261],{},"Alternates between ",[878,1802,1788],{}," and HTML entity decoding until the output stabilizes or the iteration limit is reached. Catches layered payloads like ",[878,1805,1806],{},"%253Cscript%253E",[926,1808,1810],{"id":1809},"residual-cleanup","Residual cleanup",[848,1812,1813],{},"Strips zero-width characters that decoders may have reintroduced.",[926,1815,1817],{"id":1816},"pattern-detection","Pattern detection",[848,1819,1820,1821,905,1824,1827,1828,1831,1832,1261],{},"Tests for HTML tags, inline event handlers (",[878,1822,1823],{},"onclick=",[878,1825,1826],{},"onerror=","), and ",[878,1829,1830],{},"javascript:"," URIs. Any match sets ",[878,1833,1834],{},"htmlFound: true",[926,1836,1838,1841],{"id":1837},"sanitize-html-pass-and-entity-encoding",[878,1839,1840],{},"sanitize-html"," pass and entity encoding",[848,1843,1844,1845,1847,1848,905,1851,905,1854,905,1857,905,1860,1863,1864,1261],{},"Runs ",[878,1846,1840],{}," with zero allowed tags, then entity-encodes the output: ",[878,1849,1850],{},"&",[878,1852,1853],{},"\u003C",[878,1855,1856],{},">",[878,1858,1859],{},"\"",[878,1861,1862],{},"'",", backtick, and ",[878,1865,1866],{},"${",[848,1868,1869,1872],{},[878,1870,1871],{},"makeSafeString"," wraps this pipeline in a Zod transform so it applies automatically to any schema field:",[1353,1874,1878],{"className":1875,"code":1876,"language":1877,"meta":1361,"style":1361},"language-ts shiki shiki-themes light-plus light-plus dracula","const schema = z.object({\n  name: makeSafeString({ min: 1, max: 100 }),\n  bio: makeSafeString({ min: 0, max: 500 })\n})\n","ts",[878,1879,1880,1911,1948,1979],{"__ignoreMap":1361},[1881,1882,1885,1889,1893,1897,1901,1904,1908],"span",{"class":1883,"line":1884},"line",1,[1881,1886,1888],{"class":1887},"sl46w","const",[1881,1890,1892],{"class":1891},"s3JHE"," schema",[1881,1894,1896],{"class":1895},"saOXh"," =",[1881,1898,1900],{"class":1899},"sjsA6"," z",[1881,1902,1261],{"class":1903},"sDd4n",[1881,1905,1907],{"class":1906},"sHOzp","object",[1881,1909,1910],{"class":1903},"({\n",[1881,1912,1914,1917,1920,1923,1926,1929,1931,1935,1937,1940,1942,1945],{"class":1883,"line":1913},2,[1881,1915,1916],{"class":1899},"  name",[1881,1918,1137],{"class":1919},"s34zl",[1881,1921,1922],{"class":1906}," makeSafeString",[1881,1924,1925],{"class":1903},"({ ",[1881,1927,1928],{"class":1899},"min",[1881,1930,1137],{"class":1919},[1881,1932,1934],{"class":1933},"spgvN"," 1",[1881,1936,905],{"class":1903},[1881,1938,1939],{"class":1899},"max",[1881,1941,1137],{"class":1919},[1881,1943,1944],{"class":1933}," 100",[1881,1946,1947],{"class":1903}," }),\n",[1881,1949,1951,1954,1956,1958,1960,1962,1964,1967,1969,1971,1973,1976],{"class":1883,"line":1950},3,[1881,1952,1953],{"class":1899},"  bio",[1881,1955,1137],{"class":1919},[1881,1957,1922],{"class":1906},[1881,1959,1925],{"class":1903},[1881,1961,1928],{"class":1899},[1881,1963,1137],{"class":1919},[1881,1965,1966],{"class":1933}," 0",[1881,1968,905],{"class":1903},[1881,1970,1939],{"class":1899},[1881,1972,1137],{"class":1919},[1881,1974,1975],{"class":1933}," 500",[1881,1977,1978],{"class":1903}," })\n",[1881,1980,1982],{"class":1883,"line":1981},4,[1881,1983,1984],{"class":1903},"})\n",[848,1986,1547,1987,1990],{},[852,1988,1989],{"href":460},"IAM XSS Protection"," page for the full pipeline specification.",[867,1992],{},[870,1994,1996],{"id":1995},"token-management","Token management",[887,1998,2000],{"id":1999},"rotation-deduplication","Rotation deduplication",[848,2002,2003,2004,2007],{},"When two requests arrive simultaneously for the same session, both will detect that the access token is near expiry and attempt to call ",[878,2005,2006],{},"POST \u002Fauth\u002Fuser\u002Frefresh-session"," on the IAM service. Sending two concurrent rotation requests would cause the first-returned token to be immediately invalidated by the second rotation, ending the session.",[848,2009,2010,2013],{},[878,2011,2012],{},"lockAsyncAction"," deduplicates this by key. The first rotation call proceeds normally. Any additional calls with the same session key wait for the first result and reuse it. Rotation outcomes are cached for 5 seconds to handle requests that arrive after the first call completes.",[887,2015,2017],{"id":2016},"session-data-caching","Session data caching",[848,2019,2020,2023,2024,2027,2028,905,2031,2034,2035,2037],{},[878,2021,2022],{},"getCachedUserData"," caches the result of the IAM ",[878,2025,2026],{},"\u002Fsecret\u002Fdata"," response in the configured unstorage instance. The cache key is a SHA-256 hash of the ",[878,2029,2030],{},"canary_id",[878,2032,2033],{},"session",", and ",[878,2036,1445],{}," cookie values. A change in any token invalidates the cache entry automatically.",[848,2039,2040,2041,2044,2045,2048],{},"Cached results are served for the duration of ",[878,2042,2043],{},"successTtl"," (default 30 days) without a network call to the IAM service. Rate-limited responses are cached separately for ",[878,2046,2047],{},"rateLimitTtl"," to prevent hammering the IAM service during a rate limit window.",[887,2050,2052],{"id":2051},"access-token-metadata-cache","Access token metadata cache",[848,2054,2055,2058],{},[878,2056,2057],{},"getAccessTokenMetaData"," keeps a local LRU cache of access token expiry and rotation state. This means that checking whether a token is still valid does not require a round trip to the IAM service on every request. The cache is populated after each successful rotation.",[867,2060],{},[870,2062,2064],{"id":2063},"api-token-verification-and-management","API token verification and management",[848,2066,2067],{},"The API token surface adds two distinct security patterns: public API key\nverification for machine-to-machine traffic, and authenticated management\nroutes for token inventory and lifecycle operations.",[887,2069,2071],{"id":2070},"public-api-verification","Public API verification",[848,2073,2074,2076,2077,2079,2080,2082],{},[878,2075,1057],{}," reads ",[878,2078,1062],{}," from the incoming request and\nforwards it to IAM ",[878,2081,1066],{}," together with the privilege floor you\nchoose for that route. Your handler only runs after IAM verifies the key.",[848,2084,2085,2086,2089,2090,2093],{},"On success, the wrapper places the normalized verification result on\n",[878,2087,2088],{},"event.context.apiVerification",". On failure, it returns a structured JSON\nresponse and forwards ",[878,2091,2092],{},"Retry-After"," on rate limits. This keeps API key parsing,\nprivilege comparison, and abuse control out of your application handlers.",[887,2095,2097],{"id":2096},"authenticated-management-routes","Authenticated management routes",[848,2099,2100,2102],{},[878,2101,1073],{}," protects browser-facing token management routes\nbehind the full authenticated POST chain: HMAC signing to IAM, token rotation,\nsession verification, CSRF enforcement, method enforcement, and a 2 KB raw body\nlimit before JSON parsing.",[848,2104,2105,2106,2109,2110,2113,2114,2116],{},"For revoke, metadata, rotate, IP restriction updates, and privilege updates,\nthe wrapper first fetches the token list from IAM and maps the submitted\n",[878,2107,2108],{},"tokenId"," to ",[878,2111,2112],{},"public_identifier"," in the server layer. Clients only need to send\n",[878,2115,2108],{},", which keeps token identity details out of the public request body.",[867,2118],{},[870,2120,2122],{"id":2121},"threat-model","Threat model",[887,2124,2126],{"id":2125},"what-this-layer-defends-against","What this layer defends against",[992,2128,2129,2139],{},[995,2130,2131],{},[998,2132,2133,2136],{},[1001,2134,2135],{},"Threat",[1001,2137,2138],{},"Defense",[1008,2140,2141,2149,2165,2175,2183,2191,2201,2216],{},[998,2142,2143,2146],{},[1013,2144,2145],{},"CSRF attacks",[1013,2147,2148],{},"Double-submit cookie with HMAC-signed expiring token and timing-safe comparison",[998,2150,2151,2154],{},[1013,2152,2153],{},"Cookie theft via XSS",[1013,2155,2156,2157,2160,2161,2164],{},"CSRF cookie uses ",[878,2158,2159],{},"HttpOnly: false"," intentionally (client must read it); access and session cookies are ",[878,2162,2163],{},"httpOnly: true",", unreachable from JavaScript",[998,2166,2167,2170],{},[1013,2168,2169],{},"Subdomain cookie injection",[1013,2171,2172,2174],{},[878,2173,1216],{}," prefix prevents any subdomain from setting or reading the guarded cookies",[998,2176,2177,2180],{},[1013,2178,2179],{},"Unauthorized gateway requests",[1013,2181,2182],{},"HMAC signature with clock-skew and replay detection; optional mTLS",[998,2184,2185,2188],{},[1013,2186,2187],{},"Bot traffic and credential stuffing",[1013,2189,2190],{},"IP validation, bot score screening, optional UFW firewall banning",[998,2192,2193,2196],{},[1013,2194,2195],{},"Concurrent token rotation race",[1013,2197,2198,2200],{},[878,2199,2012],{}," deduplication prevents competing rotation calls for the same session",[998,2202,2203,2206],{},[1013,2204,2205],{},"Oversized or malformed payloads",[1013,2207,2208,2211,2212,2215],{},[878,2209,2210],{},"limitBytes"," applied before JSON parsing; ",[878,2213,2214],{},"contentType"," enforcement before body reads",[998,2217,2218,2221],{},[1013,2219,2220],{},"XSS injection in user input",[1013,2222,2223],{},"Seven-stage sanitization pipeline before any string reaches business logic",[887,2225,2227],{"id":2226},"what-this-layer-defers-to-the-iam-service","What this layer defers to the IAM service",[848,2229,2230,2231,1137],{},"The BFF layer does not handle these controls. They are enforced entirely within the ",[852,2232,861],{"href":534},[2234,2235,2236,2248,2262,2270,2278,2286],"ul",{},[2237,2238,2239,2243,2244,1261],"li",{},[2240,2241,2242],"strong",{},"Password hashing",": Argon2id with pepper. See ",[852,2245,2247],{"href":2246},"\u002Fdocs\u002Fiam\u002Fsecurity#credential-storage","IAM Security: Credential Storage",[2237,2249,2250,2253,2254,2257,2258,1261],{},[2240,2251,2252],{},"Refresh token single-use enforcement",": Atomic ",[878,2255,2256],{},"usage_count"," consumption. See ",[852,2259,2261],{"href":2260},"\u002Fdocs\u002Fiam\u002Fsecurity#token-lifecycle","IAM Security: Token Lifecycle",[2237,2263,2264,2267,2268,1261],{},[2240,2265,2266],{},"Anomaly detection",": Nine-check session fingerprint validation. See ",[852,2269,416],{"href":417},[2237,2271,2272,2275,2276,1261],{},[2240,2273,2274],{},"Adaptive MFA",": Triggered by anomaly detection, code hashed with SHA-256. See ",[852,2277,120],{"href":444},[2237,2279,2280,2283,2284,1261],{},[2240,2281,2282],{},"Rate limiting",": Per-IP and per-composite-key limits on all auth endpoints. See ",[852,2285,466],{"href":467},[2237,2287,2288,2291,2292,2295],{},[2240,2289,2290],{},"Session lifetime enforcement",": Hard ",[878,2293,2294],{},"MAX_SESSION_LIFE"," cap enforced during rotation",[2297,2298,2299],"note",{},[848,2300,2301],{},"A correctly deployed stack runs the IAM service on a private network, unreachable from the public internet. The BFF layer is the only entry point. Together, these two layers cover every control in a standard authentication threat model.",[2303,2304,2305],"style",{},"html pre.shiki code .sl46w, html code.shiki .sl46w{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#FF79C6}html pre.shiki code .s3JHE, html code.shiki .s3JHE{--shiki-light:#0070C1;--shiki-default:#0070C1;--shiki-dark:#F8F8F2}html pre.shiki code .saOXh, html code.shiki .saOXh{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#FF79C6}html pre.shiki code .sjsA6, html code.shiki .sjsA6{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#F8F8F2}html pre.shiki code .sDd4n, html code.shiki .sDd4n{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#F8F8F2}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}html pre.shiki code .s34zl, html code.shiki .s34zl{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#FF79C6}html pre.shiki code .spgvN, html code.shiki .spgvN{--shiki-light:#098658;--shiki-default:#098658;--shiki-dark:#BD93F9}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);}",{"title":1361,"searchDepth":1913,"depth":1913,"links":2307},[2308,2312,2316,2320,2324,2329,2334,2339,2343],{"id":872,"depth":1913,"text":873,"children":2309},[2310,2311],{"id":889,"depth":1950,"text":890},{"id":986,"depth":1950,"text":987},{"id":1107,"depth":1913,"text":1108,"children":2313},[2314,2315],{"id":1120,"depth":1950,"text":1121},{"id":1233,"depth":1950,"text":1234},{"id":1339,"depth":1913,"text":1340,"children":2317},[2318,2319],{"id":1343,"depth":1950,"text":1344},{"id":1389,"depth":1950,"text":1390},{"id":1451,"depth":1913,"text":1452,"children":2321},[2322,2323],{"id":1458,"depth":1950,"text":1459},{"id":1558,"depth":1950,"text":1559},{"id":1582,"depth":1913,"text":1583,"children":2325},[2326,2327,2328],{"id":1592,"depth":1950,"text":1593},{"id":1604,"depth":1950,"text":1605},{"id":1619,"depth":1950,"text":1620},{"id":1661,"depth":1913,"text":1662,"children":2330},[2331,2332,2333],{"id":1665,"depth":1950,"text":1666},{"id":1728,"depth":1950,"text":1729},{"id":1745,"depth":1950,"text":1746},{"id":1995,"depth":1913,"text":1996,"children":2335},[2336,2337,2338],{"id":1999,"depth":1950,"text":2000},{"id":2016,"depth":1950,"text":2017},{"id":2051,"depth":1950,"text":2052},{"id":2063,"depth":1913,"text":2064,"children":2340},[2341,2342],{"id":2070,"depth":1950,"text":2071},{"id":2096,"depth":1950,"text":2097},{"id":2121,"depth":1913,"text":2122,"children":2344},[2345,2346],{"id":2125,"depth":1950,"text":2126},{"id":2226,"depth":1950,"text":2227},"Defense-in-depth security architecture of the Auth H3 Client BFF layer, covering the request lifecycle, CSRF protection, signed cookies, inter-service authentication, bot detection, input validation, token management, and the threat model.","md","i-lucide-lock-keyhole",{},null,"---\ntitle: Security\ndescription: Defense-in-depth security architecture of the Auth H3 Client BFF layer, covering the request lifecycle, CSRF protection, signed cookies, inter-service authentication, bot detection, input validation, token management, and the threat model.\nicon: i-lucide-lock-keyhole\n---\n\nAuth H3 Client is built on the same [Defense in Depth](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDefense_in_depth_(computing)) principle as the [IAM service](\u002Fdocs\u002Fiam\u002Fsecurity). Every layer assumes the layers around it can fail and adds its own protection. A missing CSRF token does not reach your handler. An unrecognized IP does not reach the session check. A replayed service request does not pass the HMAC signature verification. No single control stands alone.\n\nThis page documents every security mechanism in the BFF layer. Where a control extends or delegates to the IAM service, you will find a link to the corresponding IAM documentation.\n\n---\n\n## Request lifecycle\n\nEvery request that enters the Auth H3 Client layer passes through a sequence of\nchecks before it reaches the final handler. When the Nuxt module runs with\n`enableMiddleware: true`, its bundled global middleware applies three of these\nchecks to all non-skipped requests before route-level enforcement begins. When\n`enableMiddleware: false`, you are responsible for registering any equivalent\nbrowser middleware yourself.\n\n### Global middleware chain\n\nThe global middleware in `server\u002Fmiddleware\u002Fauth.ts` skips requests that do not need checking: `HEAD` requests, `\u002Fapi\u002Fhealth`, `\u002F_nuxt` static assets, `\u002Fapi\u002F_mdc`, and requests arriving from loopback addresses (`127.0.0.1`, `::1`). All other requests pass through the following chain in order:\n\n::steps{level=\"4\"}\n\n#### `isIPValid`\n\nExtracts the client IP address from the request headers and validates it with `net.isIP()`. Rejects any request with an invalid or missing IP address before any network call is made. This check has no latency cost.\n\n#### `botDetectorMiddleware`\n\nForwards the visitor fingerprint to the IAM `\u002Fcheck` endpoint. Returns HTTP 403 when the visitor score exceeds the configured ban threshold. When `enableFireWallBans` is `true`, calls `banIp` to block the IP at the firewall level. See [Bot Detection](\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fbot-detection) for the full flow.\n\n#### `generateCsrfCookie`\n\nMints a signed `__Host-csrf` cookie when one is not already present. The token is a 32-byte hex string signed with an expiring HMAC payload. See [CSRF Protection](#csrf-protection) below for how the cookie is constructed and verified.\n\n::\n\n### Route-level enforcement\n\nInside your own route handlers, the event handler wrappers enforce authentication and CSRF requirements:\n\n| Wrapper | Enforces |\n|---|---|\n| `defineAuthenticatedEventHandler` | HMAC signature, token rotation, session check |\n| `defineOptionalAuthenticationEvent` | Same as above, continues as guest on failure |\n| `defineVerifiedCsrfHandler` | CSRF cookie and header match |\n| `defineAuthenticatedEventPostHandlers` | Authentication + CSRF + POST method |\n| `defineAuthenticatePublicApi` | `X-API-KEY` verification against IAM `\u002Fapi\u002Fpublic\u002Fverify` |\n| `defineApiManagementHandler` | Authentication + CSRF + POST method + 2 KB JSON body limit + server-side API token identity mapping |\n| `defineVerifiedMagicLinkGetHandler` | GET method, required cookies, magic link schema, IAM link verification |\n| `defineMfaCodeVerifierHandler` | POST method, CSRF, body limit, link params, 7-digit code, token rotation |\n\nSee [Route Protection](\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Froute-protection) for the full wrappers reference.\n\n---\n\n## CSRF protection\n\nThe module implements the [double-submit cookie](https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FCross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#alternative-using-a-double-submit-cookie-pattern) pattern. The CSRF token is issued as a cookie and must also appear as a request header on every state-changing request.\n\n### Cookie issuance\n\n`generateCsrfCookie` runs automatically in global middleware. It generates a 32-byte random token with `crypto.randomBytes(32).toString('hex')`, signs it into a `base64(value).base64('csrf').expiry.hmac` payload, and sets it as `__Host-csrf`:\n\n| Attribute | Value |\n|---|---|\n| `HttpOnly` | `false` (the client must be able to read it to inject it as a header) |\n| `SameSite` | `Strict` |\n| `Secure` | `true` |\n| `MaxAge` | `1800` seconds (30 minutes) |\n| `Path` | `\u002F` (enforced by the `__Host-` prefix) |\n\nThe `__Host-` prefix tells the browser to enforce `Secure: true`, `Path: \u002F`, and to strip the domain attribute. This prevents the cookie from being set by a subdomain and makes it impossible to replicate without HTTPS.\n\n### Verification\n\n`verifyCsrfCookie` reads the `__Host-csrf` cookie, verifies the HMAC signature and expiry using `verifySignedValue`, and then checks that the `X-CSRF-Token` request header equals the token stored inside the cookie payload. The comparison uses `isSameBuffer`, a timing-safe HMAC comparison that prevents [timing side-channel attacks](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTiming_attack).\n\nVerification fails with the following HTTP 403 error codes:\n\n| Code | Meaning |\n|---|---|\n| `CSRF_MISSING` | The `__Host-csrf` cookie is absent |\n| `CSRF_INVALID` | The cookie signature or expiry check failed |\n| `TOKEN_INVALID` | The `X-CSRF-Token` header does not match the cookie payload |\n\nOn the client, `executeRequest` injects the CSRF token automatically from `getCsrfToken()`, which reads the raw value from `document.cookie`. On the server, the CSRF token is extracted from the forwarded request headers. See [CSRF](\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcsrf) for the full flow and `ApiContext` usage.\n\n---\n\n## Cookie security\n\n### Signed cookies\n\nCookies that carry sensitive state are signed using `createSignedValue`. The format is:\n\n```\nbase64url(value) . base64url(keyword) . expiryTimestamp . hmacSha256Hex\n```\n\nThe `keyword` parameter binds the signature to a specific purpose so that a valid CSRF cookie cannot be replayed as a different cookie type. The HMAC is computed with the `cryptoCookiesSecret` from the configuration.\n\n`verifySignedValue` parses this format, recomputes the HMAC, and uses `isSameBuffer` for the comparison. A mismatched signature, expired timestamp, or malformed payload all return `{ valid: false, payload: null }`. See [Cookies](\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcookies) for the full inventory and attributes.\n\n### Cookie prefixes\n\nAll sensitive cookies use either the `__Host-` or `__Secure-` prefix:\n\n| Prefix | Enforced attributes |\n|---|---|\n| `__Host-` | `Secure: true`, `Path: \u002F`, domain attribute stripped |\n| `__Secure-` | `Secure: true` |\n\nThe `__Host-` prefix, used for the CSRF cookie, provides the strongest protection: the browser will not send it on plain HTTP, will only send it on requests to the exact origin, and will refuse to set it from a subdomain. The access token cookie `__Secure-a` requires HTTPS but allows domain scoping for cross-subdomain token sharing.\n\n---\n\n## Inter-service authentication\n\nTraffic between the module and the IAM service runs on a private Docker network. This isolation is not sufficient alone. The module supports two independent authentication layers for every outbound request.\n\n### HMAC request signing\n\nWhen `enableHmac: true`, every request the module sends to the IAM service carries four signed headers:\n\n| Header | Purpose |\n|---|---|\n| `X-Client-Id` | Identifies the gateway instance |\n| `X-Timestamp` | Millisecond timestamp for clock-skew detection |\n| `X-Request-Id` | A unique request ID for replay detection |\n| `X-Signature` | HMAC-SHA256 of `clientId:timestamp:method:path:requestId` |\n\nThe IAM service recomputes the signature with `crypto.timingSafeEqual` and rejects requests outside the configured clock-skew window. It also caches recent request IDs to reject replays. A container on the same Docker network without the shared secret cannot forge a valid request.\n\n`hmacSignatureMiddleware`, which runs inside every authenticated handler wrapper, generates these headers and stores them on `event.context.authHeaders` so that every `serviceToService` call to the IAM service carries a valid signature.\n\nSee the [HMAC guide](\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fhmac) for configuration and the [IAM HMAC documentation](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac) for the server-side verification flow.\n\n### Mutual TLS\n\n`getAuthAgent` builds an Undici HTTP agent with optional client certificate and CA bundle. When configured, both the module and the IAM service present certificates and verify each other. This adds a transport-layer identity check on top of the application-layer HMAC signature.\n\n::tip\nHMAC and mTLS are independent. HMAC alone is sufficient for most private-network deployments. Add mTLS when your security policy or compliance requirements mandate encrypted, mutually authenticated internal traffic.\n::\n\nSee the [mTLS guide](\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fmtls) for certificate generation and configuration.\n\n---\n\n## Bot detection and IP validation\n\nThe module integrates with the [Bot Detection](\u002Fdocs\u002Fbot-detection) service to screen incoming requests before any authentication logic runs.\n\n### IP validation\n\n`isIPValid` extracts the client IP from the request headers and validates it with `net.isIP()`. This check runs first, before any network call, and adds no latency.\n\n### Score-based screening\n\n`botDetectorMiddleware` forwards the visitor fingerprint to the IAM service `\u002Fcheck` endpoint. The IAM service returns a score for the visitor based on the [Bot Detection](\u002Fdocs\u002Fbot-detection) pipeline. Visitors with a score at or above the configured ban threshold receive HTTP 403. The IAM service also updates the visitor record on each check.\n\n### Firewall banning\n\nWhen `enableFireWallBans: true`, the module calls `banIp` on any visitor that exceeds the ban threshold. `banIp` executes `sudo ufw insert 1 deny from \u003Cip> to any` to block the IP at the operating system firewall level, before the request even enters the application.\n\n::warning\nFirewall banning requires Linux with UFW installed and `sudo` privileges for the process user. Do not enable this option in environments where those conditions are not met.\n::\n\nIn custom handlers, `checkForBots(event)` calls `\u002Fcheck` directly so you can apply bot detection outside of the global middleware chain.\n\n---\n\n## Input validation\n\n### Body size limits\n\n`limitBytes(maxBytes)` reads the request body before any parsing and throws HTTP 403 with code `INVALID_CONTENT_TYPE` if the byte length exceeds the limit. Passing `limitBytes(0)` rejects any body entirely. This limit is applied before JSON parsing, so a large payload cannot consume memory during deserialization.\n\n| Route type | Limit |\n|---|---|\n| Login, signup, MFA, password reset | 1 KB |\n| Email change body | 1 MB |\n| MFA code submission | 8 MB |\n| Logout | 0 (no body allowed) |\n\n### Content-type enforcement\n\n`contentType(expected)` validates the `Content-Type` request header before the body is parsed. Requests with an unexpected or missing content type are rejected with HTTP 403 and code `INVALID_CONTENT_TYPE`. This prevents multipart, URL-encoded, or XML payloads from bypassing JSON-level input validation.\n\n### XSS sanitization pipeline\n\n`sanitizeInputString` runs a seven-stage pipeline on every user-supplied string before it reaches business logic:\n\n::steps{level=\"4\"}\n\n#### Length guard\nRejects input exceeding `htmlSanitizer.maxAllowedInputLength`. Oversized input is rejected before entering the loop.\n\n#### Unicode normalization\nNormalizes to [NFKC](https:\u002F\u002Funicode.org\u002Freports\u002Ftr15\u002F), collapses zero-width characters, strips soft hyphens and bidirectional overrides, and transliterates fullwidth ASCII back to standard ASCII.\n\n#### URI decode\nApplies `decodeURIComponent` once. Malformed percent-encoding (e.g. `%ZZ`) causes immediate rejection.\n\n#### Iterative decoding loop\nAlternates between `decodeURIComponent` and HTML entity decoding until the output stabilizes or the iteration limit is reached. Catches layered payloads like `%253Cscript%253E`.\n\n#### Residual cleanup\nStrips zero-width characters that decoders may have reintroduced.\n\n#### Pattern detection\nTests for HTML tags, inline event handlers (`onclick=`, `onerror=`), and `javascript:` URIs. Any match sets `htmlFound: true`.\n\n#### `sanitize-html` pass and entity encoding\nRuns `sanitize-html` with zero allowed tags, then entity-encodes the output: `&`, `\u003C`, `>`, `\"`, `'`, backtick, and `${`.\n\n::\n\n`makeSafeString` wraps this pipeline in a Zod transform so it applies automatically to any schema field:\n\n```ts\nconst schema = z.object({\n  name: makeSafeString({ min: 1, max: 100 }),\n  bio: makeSafeString({ min: 0, max: 500 })\n})\n```\n\nSee the [IAM XSS Protection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss) page for the full pipeline specification.\n\n---\n\n## Token management\n\n### Rotation deduplication\n\nWhen two requests arrive simultaneously for the same session, both will detect that the access token is near expiry and attempt to call `POST \u002Fauth\u002Fuser\u002Frefresh-session` on the IAM service. Sending two concurrent rotation requests would cause the first-returned token to be immediately invalidated by the second rotation, ending the session.\n\n`lockAsyncAction` deduplicates this by key. The first rotation call proceeds normally. Any additional calls with the same session key wait for the first result and reuse it. Rotation outcomes are cached for 5 seconds to handle requests that arrive after the first call completes.\n\n### Session data caching\n\n`getCachedUserData` caches the result of the IAM `\u002Fsecret\u002Fdata` response in the configured unstorage instance. The cache key is a SHA-256 hash of the `canary_id`, `session`, and `__Secure-a` cookie values. A change in any token invalidates the cache entry automatically.\n\nCached results are served for the duration of `successTtl` (default 30 days) without a network call to the IAM service. Rate-limited responses are cached separately for `rateLimitTtl` to prevent hammering the IAM service during a rate limit window.\n\n### Access token metadata cache\n\n`getAccessTokenMetaData` keeps a local LRU cache of access token expiry and rotation state. This means that checking whether a token is still valid does not require a round trip to the IAM service on every request. The cache is populated after each successful rotation.\n\n---\n\n## API token verification and management\n\nThe API token surface adds two distinct security patterns: public API key\nverification for machine-to-machine traffic, and authenticated management\nroutes for token inventory and lifecycle operations.\n\n### Public API verification\n\n`defineAuthenticatePublicApi` reads `X-API-KEY` from the incoming request and\nforwards it to IAM `\u002Fapi\u002Fpublic\u002Fverify` together with the privilege floor you\nchoose for that route. Your handler only runs after IAM verifies the key.\n\nOn success, the wrapper places the normalized verification result on\n`event.context.apiVerification`. On failure, it returns a structured JSON\nresponse and forwards `Retry-After` on rate limits. This keeps API key parsing,\nprivilege comparison, and abuse control out of your application handlers.\n\n### Authenticated management routes\n\n`defineApiManagementHandler` protects browser-facing token management routes\nbehind the full authenticated POST chain: HMAC signing to IAM, token rotation,\nsession verification, CSRF enforcement, method enforcement, and a 2 KB raw body\nlimit before JSON parsing.\n\nFor revoke, metadata, rotate, IP restriction updates, and privilege updates,\nthe wrapper first fetches the token list from IAM and maps the submitted\n`tokenId` to `public_identifier` in the server layer. Clients only need to send\n`tokenId`, which keeps token identity details out of the public request body.\n\n---\n\n## Threat model\n\n### What this layer defends against\n\n| Threat | Defense |\n|---|---|\n| CSRF attacks | Double-submit cookie with HMAC-signed expiring token and timing-safe comparison |\n| Cookie theft via XSS | CSRF cookie uses `HttpOnly: false` intentionally (client must read it); access and session cookies are `httpOnly: true`, unreachable from JavaScript |\n| Subdomain cookie injection | `__Host-` prefix prevents any subdomain from setting or reading the guarded cookies |\n| Unauthorized gateway requests | HMAC signature with clock-skew and replay detection; optional mTLS |\n| Bot traffic and credential stuffing | IP validation, bot score screening, optional UFW firewall banning |\n| Concurrent token rotation race | `lockAsyncAction` deduplication prevents competing rotation calls for the same session |\n| Oversized or malformed payloads | `limitBytes` applied before JSON parsing; `contentType` enforcement before body reads |\n| XSS injection in user input | Seven-stage sanitization pipeline before any string reaches business logic |\n\n### What this layer defers to the IAM service\n\nThe BFF layer does not handle these controls. They are enforced entirely within the [IAM service](\u002Fdocs\u002Fiam\u002Fsecurity):\n\n- **Password hashing**: Argon2id with pepper. See [IAM Security: Credential Storage](\u002Fdocs\u002Fiam\u002Fsecurity#credential-storage).\n- **Refresh token single-use enforcement**: Atomic `usage_count` consumption. See [IAM Security: Token Lifecycle](\u002Fdocs\u002Fiam\u002Fsecurity#token-lifecycle).\n- **Anomaly detection**: Nine-check session fingerprint validation. See [Anomaly Detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies).\n- **Adaptive MFA**: Triggered by anomaly detection, code hashed with SHA-256. See [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa).\n- **Rate limiting**: Per-IP and per-composite-key limits on all auth endpoints. See [Rate Limiting](\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting).\n- **Session lifetime enforcement**: Hard `MAX_SESSION_LIFE` cap enforced during rotation\n\n::note\nA correctly deployed stack runs the IAM service on a private network, unreachable from the public internet. The BFF layer is the only entry point. Together, these two layers cover every control in a standard authentication threat model.\n::\n",{"title":38,"description":2347},"rPpDGp1x6ViVGc8aoMytdolLlTRqUcnB8GOL_PClZVc",[2356,2357],{"title":164,"path":165,"stem":166,"children":-1},{"title":176,"path":177,"stem":178,"children":-1},{"id":843,"title":38,"body":2359,"description":2347,"extension":2348,"icon":2349,"meta":3369,"module":2351,"navigation":8,"path":168,"rawbody":2352,"seo":3370,"stem":169,"__hash__":2354},{"type":845,"value":2360,"toc":3328},[2361,2368,2370,2372,2374,2380,2382,2398,2434,2436,2438,2518,2522,2524,2526,2531,2533,2543,2607,2615,2617,2632,2634,2674,2686,2688,2690,2692,2696,2701,2707,2717,2719,2725,2759,2765,2767,2769,2771,2773,2777,2823,2827,2835,2841,2843,2847,2851,2855,2857,2859,2863,2865,2871,2873,2881,2883,2893,2899,2905,2907,2909,2911,2919,2955,2957,2965,2967,2971,3038,3042,3118,3122,3124,3126,3128,3132,3136,3138,3150,3156,3158,3162,3164,3166,3168,3170,3178,3184,3186,3190,3198,3200,3202,3204,3276,3278,3282,3322,3326],[848,2362,850,2363,858,2366,862],{},[852,2364,857],{"href":854,"rel":2365},[856],[852,2367,861],{"href":534},[848,2369,865],{},[867,2371],{},[870,2373,873],{"id":872},[848,2375,876,2376,881,2378,885],{},[878,2377,880],{},[878,2379,884],{},[887,2381,890],{"id":889},[848,2383,893,2384,897,2386,901,2388,905,2390,909,2392,913,2394,905,2396,920],{},[878,2385,896],{},[878,2387,900],{},[878,2389,904],{},[878,2391,908],{},[878,2393,912],{},[878,2395,916],{},[878,2397,919],{},[922,2399,2400,2404,2408,2412,2424,2428],{"level":924},[926,2401,2402],{"id":928},[878,2403,931],{},[848,2405,934,2406,938],{},[878,2407,937],{},[926,2409,2410],{"id":941},[878,2411,944],{},[848,2413,947,2414,951,2416,955,2418,959,2420,963,2422,966],{},[878,2415,950],{},[878,2417,954],{},[878,2419,958],{},[878,2421,962],{},[852,2423,33],{"href":109},[926,2425,2426],{"id":969},[878,2427,972],{},[848,2429,975,2430,979,2432,983],{},[878,2431,978],{},[852,2433,97],{"href":982},[887,2435,987],{"id":986},[848,2437,990],{},[992,2439,2440,2448],{},[995,2441,2442],{},[998,2443,2444,2446],{},[1001,2445,1003],{},[1001,2447,1006],{},[1008,2449,2450,2458,2466,2474,2482,2494,2502,2510],{},[998,2451,2452,2456],{},[1013,2453,2454],{},[878,2455,1017],{},[1013,2457,1020],{},[998,2459,2460,2464],{},[1013,2461,2462],{},[878,2463,1027],{},[1013,2465,1030],{},[998,2467,2468,2472],{},[1013,2469,2470],{},[878,2471,1037],{},[1013,2473,1040],{},[998,2475,2476,2480],{},[1013,2477,2478],{},[878,2479,1047],{},[1013,2481,1050],{},[998,2483,2484,2488],{},[1013,2485,2486],{},[878,2487,1057],{},[1013,2489,2490,1063,2492],{},[878,2491,1062],{},[878,2493,1066],{},[998,2495,2496,2500],{},[1013,2497,2498],{},[878,2499,1073],{},[1013,2501,1076],{},[998,2503,2504,2508],{},[1013,2505,2506],{},[878,2507,1083],{},[1013,2509,1086],{},[998,2511,2512,2516],{},[1013,2513,2514],{},[878,2515,1093],{},[1013,2517,1096],{},[848,2519,1099,2520,1102],{},[852,2521,93],{"href":94},[867,2523],{},[870,2525,1108],{"id":1107},[848,2527,1111,2528,1117],{},[852,2529,1116],{"href":1114,"rel":2530},[856],[887,2532,1121],{"id":1120},[848,2534,2535,1126,2537,1130,2539,1134,2541,1137],{},[878,2536,972],{},[878,2538,1129],{},[878,2540,1133],{},[878,2542,978],{},[992,2544,2545,2553],{},[995,2546,2547],{},[998,2548,2549,2551],{},[1001,2550,1146],{},[1001,2552,1149],{},[1008,2554,2555,2565,2575,2585,2595],{},[998,2556,2557,2561],{},[1013,2558,2559],{},[878,2560,1158],{},[1013,2562,2563,1164],{},[878,2564,1163],{},[998,2566,2567,2571],{},[1013,2568,2569],{},[878,2570,1171],{},[1013,2572,2573],{},[878,2574,1176],{},[998,2576,2577,2581],{},[1013,2578,2579],{},[878,2580,1183],{},[1013,2582,2583],{},[878,2584,958],{},[998,2586,2587,2591],{},[1013,2588,2589],{},[878,2590,1194],{},[1013,2592,2593,1200],{},[878,2594,1199],{},[998,2596,2597,2601],{},[1013,2598,2599],{},[878,2600,1207],{},[1013,2602,2603,1213,2605,1217],{},[878,2604,1212],{},[878,2606,1216],{},[848,2608,1220,2609,1223,2611,905,2613,1230],{},[878,2610,1216],{},[878,2612,1226],{},[878,2614,1229],{},[887,2616,1234],{"id":1233},[848,2618,2619,1240,2621,1243,2623,1247,2625,1251,2627,1255,2629,1261],{},[878,2620,1239],{},[878,2622,978],{},[878,2624,1246],{},[878,2626,1250],{},[878,2628,1254],{},[852,2630,1260],{"href":1258,"rel":2631},[856],[848,2633,1264],{},[992,2635,2636,2644],{},[995,2637,2638],{},[998,2639,2640,2642],{},[1001,2641,1273],{},[1001,2643,1276],{},[1008,2645,2646,2656,2664],{},[998,2647,2648,2652],{},[1013,2649,2650],{},[878,2651,1285],{},[1013,2653,1220,2654,1290],{},[878,2655,978],{},[998,2657,2658,2662],{},[1013,2659,2660],{},[878,2661,1297],{},[1013,2663,1300],{},[998,2665,2666,2670],{},[1013,2667,2668],{},[878,2669,1307],{},[1013,2671,1220,2672,1312],{},[878,2673,1250],{},[848,2675,1315,2676,1318,2678,1322,2680,1326,2682,1330,2684,1334],{},[878,2677,160],{},[878,2679,1321],{},[878,2681,1325],{},[852,2683,1329],{"href":98},[878,2685,1333],{},[867,2687],{},[870,2689,1340],{"id":1339},[887,2691,1344],{"id":1343},[848,2693,1347,2694,1351],{},[878,2695,1350],{},[1353,2697,2699],{"className":2698,"code":1357,"language":1358},[1356],[878,2700,1357],{"__ignoreMap":1361},[848,2702,1220,2703,1367,2705,1371],{},[878,2704,1366],{},[878,2706,1370],{},[848,2708,2709,1376,2711,1379,2713,1383,2715,1386],{},[878,2710,1246],{},[878,2712,1254],{},[878,2714,1382],{},[852,2716,112],{"href":113},[887,2718,1390],{"id":1389},[848,2720,1393,2721,1396,2723,1400],{},[878,2722,1216],{},[878,2724,1399],{},[992,2726,2727,2735],{},[995,2728,2729],{},[998,2730,2731,2733],{},[1001,2732,1409],{},[1001,2734,1412],{},[1008,2736,2737,2749],{},[998,2738,2739,2743],{},[1013,2740,2741],{},[878,2742,1216],{},[1013,2744,2745,905,2747,1427],{},[878,2746,1226],{},[878,2748,1229],{},[998,2750,2751,2755],{},[1013,2752,2753],{},[878,2754,1399],{},[1013,2756,2757],{},[878,2758,1226],{},[848,2760,1220,2761,1442,2763,1446],{},[878,2762,1216],{},[878,2764,1445],{},[867,2766],{},[870,2768,1452],{"id":1451},[848,2770,1455],{},[887,2772,1459],{"id":1458},[848,2774,1462,2775,1466],{},[878,2776,1465],{},[992,2778,2779,2787],{},[995,2780,2781],{},[998,2782,2783,2785],{},[1001,2784,1475],{},[1001,2786,1478],{},[1008,2788,2789,2797,2805,2813],{},[998,2790,2791,2795],{},[1013,2792,2793],{},[878,2794,1487],{},[1013,2796,1490],{},[998,2798,2799,2803],{},[1013,2800,2801],{},[878,2802,1497],{},[1013,2804,1500],{},[998,2806,2807,2811],{},[1013,2808,2809],{},[878,2810,1507],{},[1013,2812,1510],{},[998,2814,2815,2819],{},[1013,2816,2817],{},[878,2818,1517],{},[1013,2820,1520,2821],{},[878,2822,1523],{},[848,2824,1526,2825,1530],{},[878,2826,1529],{},[848,2828,2829,1536,2831,1540,2833,1544],{},[878,2830,1535],{},[878,2832,1539],{},[878,2834,1543],{},[848,2836,1547,2837,1551,2839,1555],{},[852,2838,1550],{"href":181},[852,2840,1554],{"href":456},[887,2842,1559],{"id":1558},[848,2844,2845,1565],{},[878,2846,1564],{},[1567,2848,2849],{},[848,2850,1571],{},[848,2852,1547,2853,1577],{},[852,2854,1576],{"href":189},[867,2856],{},[870,2858,1583],{"id":1582},[848,2860,1586,2861,1589],{},[852,2862,33],{"href":35},[887,2864,1593],{"id":1592},[848,2866,2867,1598,2869,1601],{},[878,2868,931],{},[878,2870,937],{},[887,2872,1605],{"id":1604},[848,2874,2875,1610,2877,1613,2879,1616],{},[878,2876,944],{},[878,2878,950],{},[852,2880,33],{"href":35},[887,2882,1620],{"id":1619},[848,2884,1462,2885,1626,2887,1629,2889,1632,2891,1636],{},[878,2886,1625],{},[878,2888,962],{},[878,2890,962],{},[878,2892,1635],{},[1638,2894,2895],{},[848,2896,1642,2897,1646],{},[878,2898,1645],{},[848,2900,1649,2901,1653,2903,1656],{},[878,2902,1652],{},[878,2904,950],{},[867,2906],{},[870,2908,1662],{"id":1661},[887,2910,1666],{"id":1665},[848,2912,2913,1672,2915,1676,2917,1680],{},[878,2914,1671],{},[878,2916,1675],{},[878,2918,1679],{},[992,2920,2921,2929],{},[995,2922,2923],{},[998,2924,2925,2927],{},[1001,2926,1689],{},[1001,2928,1692],{},[1008,2930,2931,2937,2943,2949],{},[998,2932,2933,2935],{},[1013,2934,1699],{},[1013,2936,1702],{},[998,2938,2939,2941],{},[1013,2940,1707],{},[1013,2942,1710],{},[998,2944,2945,2947],{},[1013,2946,1715],{},[1013,2948,1718],{},[998,2950,2951,2953],{},[1013,2952,428],{},[1013,2954,1725],{},[887,2956,1729],{"id":1728},[848,2958,2959,1735,2961,1739,2963,1742],{},[878,2960,1734],{},[878,2962,1738],{},[878,2964,1675],{},[887,2966,1746],{"id":1745},[848,2968,2969,1752],{},[878,2970,1751],{},[922,2972,2973,2975,2979,2981,2986,2988,2994,2996,3002,3004,3006,3008,3018,3022],{"level":924},[926,2974,1758],{"id":1757},[848,2976,1761,2977,1765],{},[878,2978,1764],{},[926,2980,1769],{"id":1768},[848,2982,1772,2983,1778],{},[852,2984,1777],{"href":1775,"rel":2985},[856],[926,2987,1782],{"id":1781},[848,2989,1785,2990,1789,2992,1793],{},[878,2991,1788],{},[878,2993,1792],{},[926,2995,1797],{"id":1796},[848,2997,1800,2998,1803,3000,1261],{},[878,2999,1788],{},[878,3001,1806],{},[926,3003,1810],{"id":1809},[848,3005,1813],{},[926,3007,1817],{"id":1816},[848,3009,1820,3010,905,3012,1827,3014,1831,3016,1261],{},[878,3011,1823],{},[878,3013,1826],{},[878,3015,1830],{},[878,3017,1834],{},[926,3019,3020,1841],{"id":1837},[878,3021,1840],{},[848,3023,1844,3024,1847,3026,905,3028,905,3030,905,3032,905,3034,1863,3036,1261],{},[878,3025,1840],{},[878,3027,1850],{},[878,3029,1853],{},[878,3031,1856],{},[878,3033,1859],{},[878,3035,1862],{},[878,3037,1866],{},[848,3039,3040,1872],{},[878,3041,1871],{},[1353,3043,3044],{"className":1875,"code":1876,"language":1877,"meta":1361,"style":1361},[878,3045,3046,3062,3088,3114],{"__ignoreMap":1361},[1881,3047,3048,3050,3052,3054,3056,3058,3060],{"class":1883,"line":1884},[1881,3049,1888],{"class":1887},[1881,3051,1892],{"class":1891},[1881,3053,1896],{"class":1895},[1881,3055,1900],{"class":1899},[1881,3057,1261],{"class":1903},[1881,3059,1907],{"class":1906},[1881,3061,1910],{"class":1903},[1881,3063,3064,3066,3068,3070,3072,3074,3076,3078,3080,3082,3084,3086],{"class":1883,"line":1913},[1881,3065,1916],{"class":1899},[1881,3067,1137],{"class":1919},[1881,3069,1922],{"class":1906},[1881,3071,1925],{"class":1903},[1881,3073,1928],{"class":1899},[1881,3075,1137],{"class":1919},[1881,3077,1934],{"class":1933},[1881,3079,905],{"class":1903},[1881,3081,1939],{"class":1899},[1881,3083,1137],{"class":1919},[1881,3085,1944],{"class":1933},[1881,3087,1947],{"class":1903},[1881,3089,3090,3092,3094,3096,3098,3100,3102,3104,3106,3108,3110,3112],{"class":1883,"line":1950},[1881,3091,1953],{"class":1899},[1881,3093,1137],{"class":1919},[1881,3095,1922],{"class":1906},[1881,3097,1925],{"class":1903},[1881,3099,1928],{"class":1899},[1881,3101,1137],{"class":1919},[1881,3103,1966],{"class":1933},[1881,3105,905],{"class":1903},[1881,3107,1939],{"class":1899},[1881,3109,1137],{"class":1919},[1881,3111,1975],{"class":1933},[1881,3113,1978],{"class":1903},[1881,3115,3116],{"class":1883,"line":1981},[1881,3117,1984],{"class":1903},[848,3119,1547,3120,1990],{},[852,3121,1989],{"href":460},[867,3123],{},[870,3125,1996],{"id":1995},[887,3127,2000],{"id":1999},[848,3129,2003,3130,2007],{},[878,3131,2006],{},[848,3133,3134,2013],{},[878,3135,2012],{},[887,3137,2017],{"id":2016},[848,3139,3140,2023,3142,2027,3144,905,3146,2034,3148,2037],{},[878,3141,2022],{},[878,3143,2026],{},[878,3145,2030],{},[878,3147,2033],{},[878,3149,1445],{},[848,3151,2040,3152,2044,3154,2048],{},[878,3153,2043],{},[878,3155,2047],{},[887,3157,2052],{"id":2051},[848,3159,3160,2058],{},[878,3161,2057],{},[867,3163],{},[870,3165,2064],{"id":2063},[848,3167,2067],{},[887,3169,2071],{"id":2070},[848,3171,3172,2076,3174,2079,3176,2082],{},[878,3173,1057],{},[878,3175,1062],{},[878,3177,1066],{},[848,3179,2085,3180,2089,3182,2093],{},[878,3181,2088],{},[878,3183,2092],{},[887,3185,2097],{"id":2096},[848,3187,3188,2102],{},[878,3189,1073],{},[848,3191,2105,3192,2109,3194,2113,3196,2116],{},[878,3193,2108],{},[878,3195,2112],{},[878,3197,2108],{},[867,3199],{},[870,3201,2122],{"id":2121},[887,3203,2126],{"id":2125},[992,3205,3206,3214],{},[995,3207,3208],{},[998,3209,3210,3212],{},[1001,3211,2135],{},[1001,3213,2138],{},[1008,3215,3216,3222,3232,3240,3246,3252,3260,3270],{},[998,3217,3218,3220],{},[1013,3219,2145],{},[1013,3221,2148],{},[998,3223,3224,3226],{},[1013,3225,2153],{},[1013,3227,2156,3228,2160,3230,2164],{},[878,3229,2159],{},[878,3231,2163],{},[998,3233,3234,3236],{},[1013,3235,2169],{},[1013,3237,3238,2174],{},[878,3239,1216],{},[998,3241,3242,3244],{},[1013,3243,2179],{},[1013,3245,2182],{},[998,3247,3248,3250],{},[1013,3249,2187],{},[1013,3251,2190],{},[998,3253,3254,3256],{},[1013,3255,2195],{},[1013,3257,3258,2200],{},[878,3259,2012],{},[998,3261,3262,3264],{},[1013,3263,2205],{},[1013,3265,3266,2211,3268,2215],{},[878,3267,2210],{},[878,3269,2214],{},[998,3271,3272,3274],{},[1013,3273,2220],{},[1013,3275,2223],{},[887,3277,2227],{"id":2226},[848,3279,2230,3280,1137],{},[852,3281,861],{"href":534},[2234,3283,3284,3290,3298,3304,3310,3316],{},[2237,3285,3286,2243,3288,1261],{},[2240,3287,2242],{},[852,3289,2247],{"href":2246},[2237,3291,3292,2253,3294,2257,3296,1261],{},[2240,3293,2252],{},[878,3295,2256],{},[852,3297,2261],{"href":2260},[2237,3299,3300,2267,3302,1261],{},[2240,3301,2266],{},[852,3303,416],{"href":417},[2237,3305,3306,2275,3308,1261],{},[2240,3307,2274],{},[852,3309,120],{"href":444},[2237,3311,3312,2283,3314,1261],{},[2240,3313,2282],{},[852,3315,466],{"href":467},[2237,3317,3318,2291,3320,2295],{},[2240,3319,2290],{},[878,3321,2294],{},[2297,3323,3324],{},[848,3325,2301],{},[2303,3327,2305],{},{"title":1361,"searchDepth":1913,"depth":1913,"links":3329},[3330,3334,3338,3342,3346,3351,3356,3361,3365],{"id":872,"depth":1913,"text":873,"children":3331},[3332,3333],{"id":889,"depth":1950,"text":890},{"id":986,"depth":1950,"text":987},{"id":1107,"depth":1913,"text":1108,"children":3335},[3336,3337],{"id":1120,"depth":1950,"text":1121},{"id":1233,"depth":1950,"text":1234},{"id":1339,"depth":1913,"text":1340,"children":3339},[3340,3341],{"id":1343,"depth":1950,"text":1344},{"id":1389,"depth":1950,"text":1390},{"id":1451,"depth":1913,"text":1452,"children":3343},[3344,3345],{"id":1458,"depth":1950,"text":1459},{"id":1558,"depth":1950,"text":1559},{"id":1582,"depth":1913,"text":1583,"children":3347},[3348,3349,3350],{"id":1592,"depth":1950,"text":1593},{"id":1604,"depth":1950,"text":1605},{"id":1619,"depth":1950,"text":1620},{"id":1661,"depth":1913,"text":1662,"children":3352},[3353,3354,3355],{"id":1665,"depth":1950,"text":1666},{"id":1728,"depth":1950,"text":1729},{"id":1745,"depth":1950,"text":1746},{"id":1995,"depth":1913,"text":1996,"children":3357},[3358,3359,3360],{"id":1999,"depth":1950,"text":2000},{"id":2016,"depth":1950,"text":2017},{"id":2051,"depth":1950,"text":2052},{"id":2063,"depth":1913,"text":2064,"children":3362},[3363,3364],{"id":2070,"depth":1950,"text":2071},{"id":2096,"depth":1950,"text":2097},{"id":2121,"depth":1913,"text":2122,"children":3366},[3367,3368],{"id":2125,"depth":1950,"text":2126},{"id":2226,"depth":1950,"text":2227},{},{"title":38,"description":2347},1780436289888]