[{"data":1,"prerenderedAt":6923},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fiam":64,"navigation":257,"navLinks_footer":837,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff_page":850,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff_surround":4304,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff":4307},{"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":27,"path":29,"stem":70,"children":71},"docs\u002Fiam\u002Findex",[72,73,76,216,219,236,240],{"title":27,"path":29,"stem":70},{"title":14,"path":74,"stem":75},"\u002Fdocs\u002Fiam\u002Fgetting-started","docs\u002Fiam\u002F00.getting-started",{"title":77,"path":78,"stem":79,"children":80},"Essentials","\u002Fdocs\u002Fiam\u002Fessentials","docs\u002Fiam\u002F01.essentials\u002Findex",[81,82,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166],{"title":77,"path":78,"stem":79},{"title":83,"path":84,"stem":85},"Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Ftokens","docs\u002Fiam\u002F01.essentials\u002F00.tokens",{"title":87,"path":88,"stem":89},"Access Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens","docs\u002Fiam\u002F01.essentials\u002F01.access-tokens",{"title":91,"path":92,"stem":93},"Refresh Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens","docs\u002Fiam\u002F01.essentials\u002F02.refresh-tokens",{"title":95,"path":96,"stem":97},"Anomaly Detection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies","docs\u002Fiam\u002F01.essentials\u002F03.anomalies",{"title":99,"path":100,"stem":101},"Signup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup","docs\u002Fiam\u002F01.essentials\u002F04.signup",{"title":103,"path":104,"stem":105},"Login","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin","docs\u002Fiam\u002F01.essentials\u002F05.login",{"title":107,"path":108,"stem":109},"Logout","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout","docs\u002Fiam\u002F01.essentials\u002F06.logout",{"title":111,"path":112,"stem":113},"OAuth","\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth","docs\u002Fiam\u002F01.essentials\u002F07.oauth",{"title":115,"path":116,"stem":117},"Magic Links","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links","docs\u002Fiam\u002F01.essentials\u002F08.magic-links",{"title":119,"path":120,"stem":121},"Emails","\u002Fdocs\u002Fiam\u002Fessentials\u002Femails","docs\u002Fiam\u002F01.essentials\u002F09.emails",{"title":123,"path":124,"stem":125},"MFA","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa","docs\u002Fiam\u002F01.essentials\u002F10.mfa",{"title":127,"path":128,"stem":129},"Fingerprinting","\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting","docs\u002Fiam\u002F01.essentials\u002F11.fingerprinting",{"title":131,"path":132,"stem":133},"Backend for Frontend","\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff","docs\u002Fiam\u002F01.essentials\u002F12.bff",{"title":135,"path":136,"stem":137},"HMAC Authentication","\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac","docs\u002Fiam\u002F01.essentials\u002F13.hmac",{"title":139,"path":140,"stem":141},"XSS Protection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss","docs\u002Fiam\u002F01.essentials\u002F14.xss",{"title":143,"path":144,"stem":145},"Logging","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogging","docs\u002Fiam\u002F01.essentials\u002F15.logging",{"title":147,"path":148,"stem":149},"Rate Limiting","\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F16.rate-limiting",{"title":151,"path":152,"stem":153},"Database","\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase","docs\u002Fiam\u002F01.essentials\u002F17.database",{"title":155,"path":156,"stem":157},"Cookies","\u002Fdocs\u002Fiam\u002Fessentials\u002Fcookies","docs\u002Fiam\u002F01.essentials\u002F18.cookies",{"title":159,"path":160,"stem":161},"Service Startup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice","docs\u002Fiam\u002F01.essentials\u002F19.service",{"title":163,"path":164,"stem":165},"Password Reset","\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset","docs\u002Fiam\u002F01.essentials\u002F20.password-reset",{"title":167,"path":168,"stem":169,"children":170},"API Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi","docs\u002Fiam\u002F01.essentials\u002F21.api\u002Findex",[171,172,176,180,210,213],{"title":167,"path":168,"stem":169},{"title":173,"path":174,"stem":175},"Creating Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fcreation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F00.creation",{"title":177,"path":178,"stem":179},"Verifying Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F01.verification",{"title":181,"path":182,"stem":183,"children":184},"Manage Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002Findex",[185,186,190,194,198,202,206],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189},"Privileges","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fprivilege","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F00.privilege",{"title":191,"path":192,"stem":193},"Revocation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frevocation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F01.revocation",{"title":195,"path":196,"stem":197},"Rotation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frotation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F02.rotation",{"title":199,"path":200,"stem":201},"IP Restriction","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fip-updates","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F03.ip-updates",{"title":203,"path":204,"stem":205},"Metadata","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fmetadata","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F04.metadata",{"title":207,"path":208,"stem":209},"Token Listing","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Flist","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F05.list",{"title":147,"path":211,"stem":212},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F03.rate-limiting",{"title":38,"path":214,"stem":215},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fsecurity","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F04.security",{"title":38,"path":217,"stem":218},"\u002Fdocs\u002Fiam\u002Fsecurity","docs\u002Fiam\u002F02.security",{"title":220,"path":221,"stem":222,"children":223,"page":53},"Guides","\u002Fdocs\u002Fiam\u002Fguides","docs\u002Fiam\u002F03.guides",[224,228,232],{"title":225,"path":226,"stem":227},"Deployment","\u002Fdocs\u002Fiam\u002Fguides\u002Fdeployment","docs\u002Fiam\u002F03.guides\u002Fdeployment",{"title":229,"path":230,"stem":231},"Operation Scripts","\u002Fdocs\u002Fiam\u002Fguides\u002Foperation-scripts","docs\u002Fiam\u002F03.guides\u002Foperation-scripts",{"title":233,"path":234,"stem":235},"Role-Based Access Control","\u002Fdocs\u002Fiam\u002Fguides\u002Frbac","docs\u002Fiam\u002F03.guides\u002Frbac",{"title":237,"path":238,"stem":239},"Configuration","\u002Fdocs\u002Fiam\u002Fconfiguration","docs\u002Fiam\u002F04.configuration",{"title":241,"path":242,"stem":243,"children":244,"page":53},"Api","\u002Fdocs\u002Fiam\u002Fapi","docs\u002Fiam\u002F05.API",[245,249,253],{"title":246,"path":247,"stem":248},"API Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Fapi","docs\u002Fiam\u002F05.API\u002F00.api",{"title":250,"path":251,"stem":252},"Middleware Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Fmiddlewares","docs\u002Fiam\u002F05.API\u002F02.middlewares",{"title":254,"path":255,"stem":256},"Routes Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Froutes","docs\u002Fiam\u002F05.API\u002F03.routes",[258],{"title":9,"path":66,"stem":67,"children":259,"page":53},[260,398,516,521,577,644],{"title":20,"path":22,"stem":261,"children":262},"docs\u002Fauth-h3client\u002Findex",[263,264,273,307,331,353,356,376,379],{"title":20,"path":22,"stem":261},{"title":14,"path":265,"stem":266,"children":267},"\u002Fdocs\u002Fauth-h3client\u002Fgetting-started","docs\u002Fauth-h3client\u002F00.getting-started\u002Findex",[268,269],{"title":14,"path":265,"stem":266},{"title":270,"path":271,"stem":272},"Nuxt Module","\u002Fdocs\u002Fauth-h3client\u002Fgetting-started\u002Fnuxt","docs\u002Fauth-h3client\u002F00.getting-started\u002F00.nuxt",{"title":77,"path":274,"stem":275,"children":276},"\u002Fdocs\u002Fauth-h3client\u002Fessentials","docs\u002Fauth-h3client\u002F01.essentials\u002Findex",[277,278,282,286,290,294,298,301,304],{"title":77,"path":274,"stem":275},{"title":279,"path":280,"stem":281},"Session Management","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fsession","docs\u002Fauth-h3client\u002F01.essentials\u002F00.session",{"title":283,"path":284,"stem":285},"Route Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Froute-protection","docs\u002Fauth-h3client\u002F01.essentials\u002F01.route-protection",{"title":287,"path":288,"stem":289},"CSRF Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcsrf","docs\u002Fauth-h3client\u002F01.essentials\u002F02.csrf",{"title":291,"path":292,"stem":293},"Auth Flows","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fauth-flows","docs\u002Fauth-h3client\u002F01.essentials\u002F03.auth-flows",{"title":295,"path":296,"stem":297},"OAuth and OIDC","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Foauth","docs\u002Fauth-h3client\u002F01.essentials\u002F04.oauth",{"title":33,"path":299,"stem":300},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fbot-detection","docs\u002Fauth-h3client\u002F01.essentials\u002F05.bot-detection",{"title":155,"path":302,"stem":303},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcookies","docs\u002Fauth-h3client\u002F01.essentials\u002F06.cookies",{"title":143,"path":305,"stem":306},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Flogging","docs\u002Fauth-h3client\u002F01.essentials\u002F07.logging",{"title":123,"path":308,"stem":309,"children":310},"\u002Fdocs\u002Fauth-h3client\u002Fmfa","docs\u002Fauth-h3client\u002F02.mfa\u002Findex",[311,312,316,319,323,327],{"title":123,"path":308,"stem":309},{"title":313,"path":314,"stem":315},"Built-in MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fbuilt-in-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F01.built-in-flow",{"title":163,"path":317,"stem":318},"\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fpassword-reset","docs\u002Fauth-h3client\u002F02.mfa\u002F02.password-reset",{"title":320,"path":321,"stem":322},"Email Change","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Femail-change","docs\u002Fauth-h3client\u002F02.mfa\u002F03.email-change",{"title":324,"path":325,"stem":326},"Custom MFA Flow","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fcustom-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F04.custom-flow",{"title":328,"path":329,"stem":330},"Client-Side MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fclient-side","docs\u002Fauth-h3client\u002F02.mfa\u002F05.client-side",{"title":332,"path":333,"stem":334,"children":335},"Client-side","\u002Fdocs\u002Fauth-h3client\u002Fclient","docs\u002Fauth-h3client\u002F03.client\u002Findex",[336,337,341,345,349],{"title":332,"path":333,"stem":334},{"title":338,"path":339,"stem":340},"useAuthData","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-auth-data","docs\u002Fauth-h3client\u002F03.client\u002F00.use-auth-data",{"title":342,"path":343,"stem":344},"useMagicLink","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-magic-link","docs\u002Fauth-h3client\u002F03.client\u002F01.use-magic-link",{"title":346,"path":347,"stem":348},"executeRequest","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fexecute-request","docs\u002Fauth-h3client\u002F03.client\u002F02.execute-request",{"title":350,"path":351,"stem":352},"getCsrfToken","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fget-csrf-token","docs\u002Fauth-h3client\u002F03.client\u002F03.get-csrf-token",{"title":38,"path":354,"stem":355},"\u002Fdocs\u002Fauth-h3client\u002Fsecurity","docs\u002Fauth-h3client\u002F04.security",{"title":220,"path":357,"stem":358,"children":359,"page":53},"\u002Fdocs\u002Fauth-h3client\u002Fguides","docs\u002Fauth-h3client\u002F05.guides",[360,364,368,372],{"title":361,"path":362,"stem":363},"H3 and Nitro Setup","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fh3-nitro","docs\u002Fauth-h3client\u002F05.guides\u002F00.h3-nitro",{"title":365,"path":366,"stem":367},"HMAC Inter-service Auth","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fhmac","docs\u002Fauth-h3client\u002F05.guides\u002Fhmac",{"title":369,"path":370,"stem":371},"Image Upload","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fimage-upload","docs\u002Fauth-h3client\u002F05.guides\u002Fimage-upload",{"title":373,"path":374,"stem":375},"mTLS Configuration","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fmtls","docs\u002Fauth-h3client\u002F05.guides\u002Fmtls",{"title":237,"path":377,"stem":378},"\u002Fdocs\u002Fauth-h3client\u002Fconfiguration","docs\u002Fauth-h3client\u002F06.configuration",{"title":246,"path":380,"stem":381,"children":382},"\u002Fdocs\u002Fauth-h3client\u002Fapi","docs\u002Fauth-h3client\u002F07.api\u002Findex",[383,384,387,390,394],{"title":246,"path":380,"stem":381},{"title":254,"path":385,"stem":386},"\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcontrollers","docs\u002Fauth-h3client\u002F07.api\u002F00.controllers",{"title":250,"path":388,"stem":389},"\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fmiddleware","docs\u002Fauth-h3client\u002F07.api\u002F01.middleware",{"title":391,"path":392,"stem":393},"Client-side Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcomposables","docs\u002Fauth-h3client\u002F07.api\u002F02.composables",{"title":395,"path":396,"stem":397},"Utilities","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Futilities","docs\u002Fauth-h3client\u002F07.api\u002F03.utilities",{"title":399,"path":35,"stem":400,"children":401},"Bot Detector","docs\u002Fbot-detection\u002Findex",[402,403,406,410,414,433,507,510,513],{"title":399,"path":35,"stem":400},{"title":14,"path":404,"stem":405},"\u002Fdocs\u002Fbot-detection\u002Fgetting-started","docs\u002Fbot-detection\u002F00.getting-started",{"title":407,"path":408,"stem":409},"CLI","\u002Fdocs\u002Fbot-detection\u002Fcli","docs\u002Fbot-detection\u002F01.cli",{"title":411,"path":412,"stem":413},"Data Sources","\u002Fdocs\u002Fbot-detection\u002Fdata-sources","docs\u002Fbot-detection\u002F02.data-sources",{"title":220,"path":415,"stem":416,"children":417,"page":53},"\u002Fdocs\u002Fbot-detection\u002Fguides","docs\u002Fbot-detection\u002F03.guides",[418,422,426,429],{"title":419,"path":420,"stem":421},"Custom Checkers","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom","docs\u002Fbot-detection\u002F03.guides\u002FCUSTOM",{"title":423,"path":424,"stem":425},"Scheduling Database Generation","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate","docs\u002Fbot-detection\u002F03.guides\u002FGENERATE",{"title":143,"path":427,"stem":428},"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Flogging","docs\u002Fbot-detection\u002F03.guides\u002FLOGGING",{"title":430,"path":431,"stem":432},"Score Modes and Reputation Healing","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fscore","docs\u002Fbot-detection\u002F03.guides\u002FSCORE",{"title":434,"path":435,"stem":436,"children":437},"Checkers","\u002Fdocs\u002Fbot-detection\u002Fcheckers","docs\u002Fbot-detection\u002F04.checkers\u002Findex",[438,439,443,447,451,455,459,463,467,471,475,479,483,487,491,495,499,503],{"title":434,"path":435,"stem":436},{"title":440,"path":441,"stem":442},"IP Validation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fip-validation","docs\u002Fbot-detection\u002F04.checkers\u002F01.ip-validation",{"title":444,"path":445,"stem":446},"Good \u002F Bad Bot Verification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgood-bots","docs\u002Fbot-detection\u002F04.checkers\u002F02.good-bots",{"title":448,"path":449,"stem":450},"Browser & Device Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbrowser-device","docs\u002Fbot-detection\u002F04.checkers\u002F03.browser-device",{"title":452,"path":453,"stem":454},"Locale Map","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Flocale-map","docs\u002Fbot-detection\u002F04.checkers\u002F04.locale-map",{"title":456,"path":457,"stem":458},"Known Threats","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-threats","docs\u002Fbot-detection\u002F04.checkers\u002F05.known-threats",{"title":460,"path":461,"stem":462},"ASN Classification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fasn-classification","docs\u002Fbot-detection\u002F04.checkers\u002F06.asn-classification",{"title":464,"path":465,"stem":466},"Tor Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftor-analysis","docs\u002Fbot-detection\u002F04.checkers\u002F07.tor-analysis",{"title":468,"path":469,"stem":470},"Timezone Consistency","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftimezone-consistency","docs\u002Fbot-detection\u002F04.checkers\u002F08.timezone-consistency",{"title":472,"path":473,"stem":474},"Honeypot","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fhoneypot","docs\u002Fbot-detection\u002F04.checkers\u002F09.honeypot",{"title":476,"path":477,"stem":478},"Known Bad IPs","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips","docs\u002Fbot-detection\u002F04.checkers\u002F10.known-bad-ips",{"title":480,"path":481,"stem":482},"Behavior Rate","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbehavior-rate","docs\u002Fbot-detection\u002F04.checkers\u002F11.behavior-rate",{"title":484,"path":485,"stem":486},"Proxy \u002F ISP \u002F Cookie","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fproxy-isp-cookies","docs\u002Fbot-detection\u002F04.checkers\u002F12.proxy-isp-cookies",{"title":488,"path":489,"stem":490},"Session Coherence","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fsession-coherence","docs\u002Fbot-detection\u002F04.checkers\u002F13.session-coherence",{"title":492,"path":493,"stem":494},"Velocity Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fvelocity-fingerprint","docs\u002Fbot-detection\u002F04.checkers\u002F14.velocity-fingerprint",{"title":496,"path":497,"stem":498},"UA & Header Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fua-header","docs\u002Fbot-detection\u002F04.checkers\u002F15.ua-header",{"title":500,"path":501,"stem":502},"Geolocation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgeolocation","docs\u002Fbot-detection\u002F04.checkers\u002F16.geolocation",{"title":504,"path":505,"stem":506},"Known Bad User-Agents","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ua","docs\u002Fbot-detection\u002F04.checkers\u002F17.known-bad-ua",{"title":38,"path":508,"stem":509},"\u002Fdocs\u002Fbot-detection\u002Fsecurity","docs\u002Fbot-detection\u002F04.security",{"title":246,"path":511,"stem":512},"\u002Fdocs\u002Fbot-detection\u002Fapi","docs\u002Fbot-detection\u002F05.api",{"title":237,"path":514,"stem":515},"\u002Fdocs\u002Fbot-detection\u002Fconfiguration","docs\u002Fbot-detection\u002F06.configuration",{"title":517,"path":11,"stem":518,"children":519},"Introduction","docs\u002Fgetting-started\u002Findex",[520],{"title":517,"path":11,"stem":518},{"title":27,"path":29,"stem":70,"children":522},[523,524,525,565,566,571,572],{"title":27,"path":29,"stem":70},{"title":14,"path":74,"stem":75},{"title":77,"path":78,"stem":79,"children":526},[527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549],{"title":77,"path":78,"stem":79},{"title":83,"path":84,"stem":85},{"title":87,"path":88,"stem":89},{"title":91,"path":92,"stem":93},{"title":95,"path":96,"stem":97},{"title":99,"path":100,"stem":101},{"title":103,"path":104,"stem":105},{"title":107,"path":108,"stem":109},{"title":111,"path":112,"stem":113},{"title":115,"path":116,"stem":117},{"title":119,"path":120,"stem":121},{"title":123,"path":124,"stem":125},{"title":127,"path":128,"stem":129},{"title":131,"path":132,"stem":133},{"title":135,"path":136,"stem":137},{"title":139,"path":140,"stem":141},{"title":143,"path":144,"stem":145},{"title":147,"path":148,"stem":149},{"title":151,"path":152,"stem":153},{"title":155,"path":156,"stem":157},{"title":159,"path":160,"stem":161},{"title":163,"path":164,"stem":165},{"title":167,"path":168,"stem":169,"children":550},[551,552,553,554,563,564],{"title":167,"path":168,"stem":169},{"title":173,"path":174,"stem":175},{"title":177,"path":178,"stem":179},{"title":181,"path":182,"stem":183,"children":555},[556,557,558,559,560,561,562],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189},{"title":191,"path":192,"stem":193},{"title":195,"path":196,"stem":197},{"title":199,"path":200,"stem":201},{"title":203,"path":204,"stem":205},{"title":207,"path":208,"stem":209},{"title":147,"path":211,"stem":212},{"title":38,"path":214,"stem":215},{"title":38,"path":217,"stem":218},{"title":220,"path":221,"stem":222,"children":567,"page":53},[568,569,570],{"title":225,"path":226,"stem":227},{"title":229,"path":230,"stem":231},{"title":233,"path":234,"stem":235},{"title":237,"path":238,"stem":239},{"title":241,"path":242,"stem":243,"children":573,"page":53},[574,575,576],{"title":246,"path":247,"stem":248},{"title":250,"path":251,"stem":252},{"title":254,"path":255,"stem":256},{"title":40,"path":42,"stem":578,"children":579},"docs\u002Fshield-base\u002Findex",[580,581,584,588,629,633,637,641],{"title":40,"path":42,"stem":578},{"title":14,"path":582,"stem":583},"\u002Fdocs\u002Fshield-base\u002Fgetting-started","docs\u002Fshield-base\u002F00.getting-started",{"title":585,"path":586,"stem":587},"CLI Reference","\u002Fdocs\u002Fshield-base\u002Fcli","docs\u002Fshield-base\u002F01.cli",{"title":411,"path":589,"stem":590,"children":591},"\u002Fdocs\u002Fshield-base\u002Fdata-sources","docs\u002Fshield-base\u002F02.data-sources\u002Findex",[592,593,597,601,605,609,613,617,621,625],{"title":411,"path":589,"stem":590},{"title":594,"path":595,"stem":596},"BGP \u002F ASN","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fbgp","docs\u002Fshield-base\u002F02.data-sources\u002Fbgp",{"title":598,"path":599,"stem":600},"City Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcity","docs\u002Fshield-base\u002F02.data-sources\u002Fcity",{"title":602,"path":603,"stem":604},"Country Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcountry","docs\u002Fshield-base\u002F02.data-sources\u002Fcountry",{"title":606,"path":607,"stem":608},"Verified Crawlers","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcrawlers","docs\u002Fshield-base\u002F02.data-sources\u002Fcrawlers",{"title":610,"path":611,"stem":612},"Disposable Emails","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Femail","docs\u002Fshield-base\u002F02.data-sources\u002Femail",{"title":614,"path":615,"stem":616},"FireHOL Threat Intelligence","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ffirehol","docs\u002Fshield-base\u002F02.data-sources\u002Ffirehol",{"title":618,"path":619,"stem":620},"Proxy Detection","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fproxy","docs\u002Fshield-base\u002F02.data-sources\u002Fproxy",{"title":622,"path":623,"stem":624},"Tor Nodes","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ftor","docs\u002Fshield-base\u002F02.data-sources\u002Ftor",{"title":626,"path":627,"stem":628},"Suspicious User-Agents","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fuseragent","docs\u002Fshield-base\u002F02.data-sources\u002Fuseragent",{"title":630,"path":631,"stem":632},"Programmatic Usage","\u002Fdocs\u002Fshield-base\u002Fusage","docs\u002Fshield-base\u002F03.usage",{"title":634,"path":635,"stem":636},"Custom Data Sources","\u002Fdocs\u002Fshield-base\u002Fcustom-data-sources","docs\u002Fshield-base\u002F04.custom-data-sources",{"title":638,"path":639,"stem":640},"TypeScript Types","\u002Fdocs\u002Fshield-base\u002Ftypes","docs\u002Fshield-base\u002F05.types",{"title":246,"path":642,"stem":643},"\u002Fdocs\u002Fshield-base\u002Fapi","docs\u002Fshield-base\u002F06.api",{"title":395,"path":48,"stem":645,"children":646},"docs\u002Futils\u002Findex",[647,648,665,698,795],{"title":395,"path":48,"stem":645},{"title":649,"path":650,"stem":651,"children":652,"page":53},"Eslint","\u002Fdocs\u002Futils\u002Feslint","docs\u002Futils\u002Feslint",[653,657,661],{"title":654,"path":655,"stem":656},"React Config","\u002Fdocs\u002Futils\u002Feslint\u002Freact","docs\u002Futils\u002Feslint\u002Freact",{"title":658,"path":659,"stem":660},"TypeScript Config","\u002Fdocs\u002Futils\u002Feslint\u002Ftypescript","docs\u002Futils\u002Feslint\u002Ftypescript",{"title":662,"path":663,"stem":664},"Vue Config","\u002Fdocs\u002Futils\u002Feslint\u002Fvue","docs\u002Futils\u002Feslint\u002Fvue",{"title":666,"path":667,"stem":668,"children":669,"page":53},"Server","\u002Fdocs\u002Futils\u002Fserver","docs\u002Futils\u002Fserver",[670,674,678,682,686,690,694],{"title":671,"path":672,"stem":673},"Encryption","\u002Fdocs\u002Futils\u002Fserver\u002Fencryption","docs\u002Futils\u002Fserver\u002Fencryption",{"title":675,"path":676,"stem":677},"Path Resolver","\u002Fdocs\u002Futils\u002Fserver\u002Fpathresolver","docs\u002Futils\u002Fserver\u002FpathResolver",{"title":679,"path":680,"stem":681},"File Replacements","\u002Fdocs\u002Futils\u002Fserver\u002Freplace","docs\u002Futils\u002Fserver\u002Freplace",{"title":683,"path":684,"stem":685},"run","\u002Fdocs\u002Futils\u002Fserver\u002Frun","docs\u002Futils\u002Fserver\u002Frun",{"title":687,"path":688,"stem":689},"scheduleTask","\u002Fdocs\u002Futils\u002Fserver\u002Fscheduletask","docs\u002Futils\u002Fserver\u002FscheduleTask",{"title":691,"path":692,"stem":693},"spawnRun","\u002Fdocs\u002Futils\u002Fserver\u002Fspawnrun","docs\u002Futils\u002Fserver\u002FspawnRun",{"title":695,"path":696,"stem":697},"uploadCsv","\u002Fdocs\u002Futils\u002Fserver\u002Fuploadcsv","docs\u002Futils\u002Fserver\u002FuploadCsv",{"title":699,"path":700,"stem":701,"children":702,"page":53},"Shared","\u002Fdocs\u002Futils\u002Fshared","docs\u002Futils\u002Fshared",[703,707,711,715,719,723,727,731,735,739,743,747,751,755,759,763,767,771,775,779,783,787,791],{"title":704,"path":705,"stem":706},"BatchQueue","\u002Fdocs\u002Futils\u002Fshared\u002Fbatchqueue","docs\u002Futils\u002Fshared\u002FbatchQueue",{"title":708,"path":709,"stem":710},"capitalize","\u002Fdocs\u002Futils\u002Fshared\u002Fcapitalize","docs\u002Futils\u002Fshared\u002Fcapitalize",{"title":712,"path":713,"stem":714},"chunkProcess","\u002Fdocs\u002Futils\u002Fshared\u002Fchunkprocess","docs\u002Futils\u002Fshared\u002FchunkProcess",{"title":716,"path":717,"stem":718},"cleanObject","\u002Fdocs\u002Futils\u002Fshared\u002Fcleanobject","docs\u002Futils\u002Fshared\u002FcleanObject",{"title":720,"path":721,"stem":722},"createConfigManager","\u002Fdocs\u002Futils\u002Fshared\u002Fconfigurationdefiner","docs\u002Futils\u002Fshared\u002FconfigurationDefiner",{"title":724,"path":725,"stem":726},"debounce","\u002Fdocs\u002Futils\u002Fshared\u002Fdebounce","docs\u002Futils\u002Fshared\u002Fdebounce",{"title":728,"path":729,"stem":730},"ensureArray","\u002Fdocs\u002Futils\u002Fshared\u002Fensurearray","docs\u002Futils\u002Fshared\u002FensureArray",{"title":732,"path":733,"stem":734},"fetchWithRetry","\u002Fdocs\u002Futils\u002Fshared\u002Ffetchwithretry","docs\u002Futils\u002Fshared\u002FfetchWithRetry",{"title":736,"path":737,"stem":738},"filterEmptyValues","\u002Fdocs\u002Futils\u002Fshared\u002Ffilteremptyvalues","docs\u002Futils\u002Fshared\u002FfilterEmptyValues",{"title":740,"path":741,"stem":742},"findStringsInObject","\u002Fdocs\u002Futils\u002Fshared\u002Ffindobjectvalues","docs\u002Futils\u002Fshared\u002FfindObjectValues",{"title":744,"path":745,"stem":746},"fisherYatesShuffle","\u002Fdocs\u002Futils\u002Fshared\u002Ffisheryatesshuffle","docs\u002Futils\u002Fshared\u002FfisherYatesShuffle",{"title":748,"path":749,"stem":750},"getRandomImage","\u002Fdocs\u002Futils\u002Fshared\u002Fgetrandomimage","docs\u002Futils\u002Fshared\u002FgetRandomImage",{"title":752,"path":753,"stem":754},"isObjectHasValues","\u002Fdocs\u002Futils\u002Fshared\u002Fisobjecthasvalues","docs\u002Futils\u002Fshared\u002FisObjectHasValues",{"title":756,"path":757,"stem":758},"isAsyncOrPromise","\u002Fdocs\u002Futils\u002Fshared\u002Fispromise","docs\u002Futils\u002Fshared\u002FisPromise",{"title":760,"path":761,"stem":762},"MiniCache","\u002Fdocs\u002Futils\u002Fshared\u002Fminicache","docs\u002Futils\u002Fshared\u002FminiCache",{"title":764,"path":765,"stem":766},"parseCookies","\u002Fdocs\u002Futils\u002Fshared\u002Fparserawcookies","docs\u002Futils\u002Fshared\u002FparseRawCookies",{"title":768,"path":769,"stem":770},"safeAction","\u002Fdocs\u002Futils\u002Fshared\u002Fpromiselocker","docs\u002Futils\u002Fshared\u002FpromiseLocker",{"title":772,"path":773,"stem":774},"Random","\u002Fdocs\u002Futils\u002Fshared\u002Frandom","docs\u002Futils\u002Fshared\u002Frandom",{"title":776,"path":777,"stem":778},"range","\u002Fdocs\u002Futils\u002Fshared\u002Frange","docs\u002Futils\u002Fshared\u002Frange",{"title":780,"path":781,"stem":782},"rateLimiters","\u002Fdocs\u002Futils\u002Fshared\u002Fratelimiters","docs\u002Futils\u002Fshared\u002FrateLimiters",{"title":784,"path":785,"stem":786},"safeObjectMerge","\u002Fdocs\u002Futils\u002Fshared\u002Fsafemerge","docs\u002Futils\u002Fshared\u002FsafeMerge",{"title":788,"path":789,"stem":790},"textTruncation","\u002Fdocs\u002Futils\u002Fshared\u002Ftexttruncation","docs\u002Futils\u002Fshared\u002FtextTruncation",{"title":792,"path":793,"stem":794},"validateZodSchema","\u002Fdocs\u002Futils\u002Fshared\u002Fvalidatezodschema","docs\u002Futils\u002Fshared\u002FvalidateZodSchema",{"title":796,"path":797,"stem":798,"children":799},"Utility Types","\u002Fdocs\u002Futils\u002Ftypes","docs\u002Futils\u002Ftypes\u002Findex",[800,801,805,809,813,817,821,825,829,833],{"title":796,"path":797,"stem":798},{"title":802,"path":803,"stem":804},"Brand","\u002Fdocs\u002Futils\u002Ftypes\u002Fbrand","docs\u002Futils\u002Ftypes\u002FBrand",{"title":806,"path":807,"stem":808},"DeepPartial","\u002Fdocs\u002Futils\u002Ftypes\u002Fdeeppartial","docs\u002Futils\u002Ftypes\u002FDeepPartial",{"title":810,"path":811,"stem":812},"Merge","\u002Fdocs\u002Futils\u002Ftypes\u002Fmerge","docs\u002Futils\u002Ftypes\u002FMerge",{"title":814,"path":815,"stem":816},"NonNullable","\u002Fdocs\u002Futils\u002Ftypes\u002Fnonnullable","docs\u002Futils\u002Ftypes\u002FNonNullable",{"title":818,"path":819,"stem":820},"Prettify","\u002Fdocs\u002Futils\u002Ftypes\u002Fprettify","docs\u002Futils\u002Ftypes\u002FPrettify",{"title":822,"path":823,"stem":824},"PromiseType","\u002Fdocs\u002Futils\u002Ftypes\u002Fpromisetype","docs\u002Futils\u002Ftypes\u002FPromiseType",{"title":826,"path":827,"stem":828},"RequireKeys","\u002Fdocs\u002Futils\u002Ftypes\u002Frequirekeys","docs\u002Futils\u002Ftypes\u002FRequireKeys",{"title":830,"path":831,"stem":832},"StandardResponse","\u002Fdocs\u002Futils\u002Ftypes\u002Fstandardresponse","docs\u002Futils\u002Ftypes\u002FStandardResponse",{"title":834,"path":835,"stem":836},"ValueOf","\u002Fdocs\u002Futils\u002Ftypes\u002Fvalueof","docs\u002Futils\u002Ftypes\u002FValueOf",{"id":4,"extension":5,"links":838,"meta":849,"stem":62,"__hash__":63},[839,847,848],{"nested":8,"label":9,"icon":10,"to":11,"children":840},[841,842,843,844,845,846],{"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":851,"title":131,"body":852,"description":4296,"extension":4297,"icon":4298,"meta":4299,"module":4300,"navigation":8,"path":132,"rawbody":4301,"seo":4302,"stem":133,"__hash__":4303},"docs\u002Fdocs\u002Fiam\u002F01.essentials\u002F12.bff.md",{"type":853,"value":854,"toc":4250},"minimark",[855,871,874,896,915,937,943,946,951,954,1032,1034,1038,1048,1201,1205,1324,1326,1329,1338,1343,1362,1367,1377,1382,1395,1400,1425,1430,1433,1615,1625,1726,1732,1738,1825,1828,1830,1836,1844,1848,1998,2052,2056,2146,2158,2162,2180,2182,2188,2193,2196,2522,2589,2593,2601,2609,2621,2626,2629,2709,2713,2716,2786,2795,2797,2803,2806,2810,2832,2846,2849,2896,2927,2930,2932,2936,2943,2998,3076,3079,3121,3124,3126,3130,3148,3161,3167,3169,3173,3185,3315,3332,3345,3347,3351,3354,3579,3590,3592,3596,3599,3603,3644,3648,3675,3679,3727,3729,3733,3744,3822,3825,3830,3832,3836,3839,3939,3946,3948,3952,3959,4115,4117,4121,4246],[856,857,858,859,865,866,870],"p",{},"The IAM service supports the ",[860,861,131],"a",{"href":862,"rel":863},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fazure\u002Farchitecture\u002Fpatterns\u002Fbackends-for-frontends",[864],"nofollow"," or \"BFF\" pattern natively. Instead of exposing JWT tokens to the browser, a server-side proxy sits between the client and the IAM service. The proxy holds tokens in ",[867,868,869],"code",{},"httpOnly"," cookies, rotates them transparently, and relays authorization decisions back to the frontend. The browser never sees a raw token.",[856,872,873],{},"The service provides three endpoints dedicated to BFF communication:",[875,876,877,884,890],"ul",{},[878,879,880,883],"li",{},[867,881,882],{},"GET \u002Fsecret\u002Fdata"," answers \"is this user authorized?\" and returns the user's roles.",[878,885,886,889],{},[867,887,888],{},"GET \u002Fsecret\u002Faccesstoken\u002Fmetadata"," answers \"how long until this token expires?\" and computes a proactive rotation hint.",[878,891,892,895],{},[867,893,894],{},"GET \u002Foperational\u002Fconfig"," shares the cookie domain and access token TTL so the proxy can set cookies correctly.",[856,897,898,899,902,903,906,907,910,911,914],{},"Both ",[867,900,901],{},"\u002Fsecret"," routes run behind the full middleware chain: access token\nextraction, refresh token validation, device fingerprinting,\n",[867,904,905],{},"checkForActiveMfa",", and anomaly-aware JWT verification. The metadata route\nadds a strict cookie-only guard. The ",[867,908,909],{},"\u002Foperational\u002Fconfig"," endpoint is gated by\nIP address instead, and the service can also apply\n",[860,912,913],{"href":136},"HMAC"," globally.",[856,916,917,920,921,926,927,932,933,936],{},[860,918,919],{"href":22},"auth-h3client"," is the companion Nuxt module that implements the proxy side of the pattern. It consumes the three IAM endpoints above and handles login, signup, logout, OAuth, MFA relay, CSRF, token rotation, and user data caching automatically. it can also be used as ",[860,922,925],{"href":923,"rel":924},"https:\u002F\u002Fh3.dev\u002F",[864],"h3"," \u002F ",[860,928,931],{"href":929,"rel":930},"https:\u002F\u002Fnitro.build\u002F",[864],"nitro"," client directly. See the ",[860,934,935],{"href":22},"auth-h3client documentation"," for its full API reference, configuration, and composables.",[938,939,940],"tip",{},[856,941,942],{},"You can use any http client for this pattern",[944,945],"hr",{},[947,948,950],"h2",{"id":949},"the-pattern","The pattern",[856,952,953],{},"In a traditional SPA, the browser stores access and refresh tokens in localStorage, sessionStorage, or cookies and sends them directly to resource APIs. This exposes tokens to XSS attacks, browser extensions, and third-party scripts. The BFF pattern eliminates this exposure.",[955,956,958,963,976,980,999,1003,1014,1018],"steps",{"level":957},"4",[959,960,962],"h4",{"id":961},"browser-sends-a-request-to-the-bff","Browser sends a request to the BFF",[856,964,965,966,968,969,968,972,975],{},"The browser makes a request to the application server. The only credentials the browser carries are ",[867,967,869],{},", ",[867,970,971],{},"Secure",[867,973,974],{},"SameSite=Strict"," cookies that it cannot read or modify via JavaScript.",[959,977,979],{"id":978},"bff-extracts-cookies-and-calls-the-iam-service","BFF extracts cookies and calls the IAM service",[856,981,982,983,986,987,990,991,994,995,998],{},"The BFF reads the ",[867,984,985],{},"session"," (refresh token), ",[867,988,989],{},"__Secure-a"," (access token), and ",[867,992,993],{},"canary_id"," (session binding) cookies from the incoming request. It forwards them to the IAM service as part of a server-to-server call, adding ",[860,996,997],{"href":136},"HMAC signature headers"," when configured.",[959,1000,1002],{"id":1001},"iam-validates-and-responds","IAM validates and responds",[856,1004,1005,1006,1009,1010,1013],{},"The IAM service runs the full middleware chain on the forwarded request: access token verification, refresh token verification, ",[860,1007,1008],{"href":128},"device fingerprinting",", and ",[860,1011,1012],{"href":96},"anomaly detection",". It responds with the authorization decision, user roles, and, for the metadata route, token TTL information.",[959,1015,1017],{"id":1016},"bff-relays-the-decision","BFF relays the decision",[856,1019,1020,1021,1024,1025,1028,1029,1031],{},"The BFF interprets the IAM response. If the user is authorized, the BFF proceeds with the application logic. If tokens need rotation, the BFF calls ",[867,1022,1023],{},"POST \u002Fauth\u002Fuser\u002Frefresh-session"," and forwards the new ",[867,1026,1027],{},"Set-Cookie"," headers to the browser. If ",[860,1030,123],{"href":124}," is required, the BFF relays the challenge to the browser.",[944,1033],{},[947,1035,1037],{"id":1036},"iam-service-routes","IAM service routes",[856,1039,1040,1041,1043,1044,1047],{},"The IAM service exposes two BFF-specific routes under the ",[867,1042,901],{}," prefix and one configuration endpoint under ",[867,1045,1046],{},"\u002Foperational",". These routes are mounted after the authentication, token rotation, and magic link route groups in the Express middleware chain.",[1049,1050,1055],"pre",{"className":1051,"code":1052,"language":1053,"meta":1054,"style":1054},"language-ts shiki shiki-themes light-plus light-plus dracula","\u002F\u002F service.ts (simplified mounting order)\napp.use(authenticationRoutes)     \u002F\u002F \u002Fsignup, \u002Flogin, \u002Fauth\u002FOAuth\u002F*\napp.use(tokenRotationRoutes)      \u002F\u002F \u002Fauth\u002Fuser\u002Frefresh-session, \u002Fauth\u002Flogout\napp.use(magicLinks)               \u002F\u002F \u002Fauth\u002Fverify-mfa\u002F*, \u002Fauth\u002Freset-password\u002F*\napp.use(allowBff)                 \u002F\u002F \u002Fsecret\u002Fdata, \u002Fsecret\u002Faccesstoken\u002Fmetadata\napp.use(apiProtectedRoutes())     \u002F\u002F \u002Fapi\u002Fmanage\u002F*\napp.use('\u002Foperational\u002Fconfig', sendOperationalConfig)\n","ts","",[867,1056,1057,1066,1093,1113,1133,1153,1173],{"__ignoreMap":1054},[1058,1059,1062],"span",{"class":1060,"line":1061},"line",1,[1058,1063,1065],{"class":1064},"sghk6","\u002F\u002F service.ts (simplified mounting order)\n",[1058,1067,1069,1073,1077,1081,1084,1087,1090],{"class":1060,"line":1068},2,[1058,1070,1072],{"class":1071},"sjsA6","app",[1058,1074,1076],{"class":1075},"sDd4n",".",[1058,1078,1080],{"class":1079},"sHOzp","use",[1058,1082,1083],{"class":1075},"(",[1058,1085,1086],{"class":1071},"authenticationRoutes",[1058,1088,1089],{"class":1075},")     ",[1058,1091,1092],{"class":1064},"\u002F\u002F \u002Fsignup, \u002Flogin, \u002Fauth\u002FOAuth\u002F*\n",[1058,1094,1096,1098,1100,1102,1104,1107,1110],{"class":1060,"line":1095},3,[1058,1097,1072],{"class":1071},[1058,1099,1076],{"class":1075},[1058,1101,1080],{"class":1079},[1058,1103,1083],{"class":1075},[1058,1105,1106],{"class":1071},"tokenRotationRoutes",[1058,1108,1109],{"class":1075},")      ",[1058,1111,1112],{"class":1064},"\u002F\u002F \u002Fauth\u002Fuser\u002Frefresh-session, \u002Fauth\u002Flogout\n",[1058,1114,1116,1118,1120,1122,1124,1127,1130],{"class":1060,"line":1115},4,[1058,1117,1072],{"class":1071},[1058,1119,1076],{"class":1075},[1058,1121,1080],{"class":1079},[1058,1123,1083],{"class":1075},[1058,1125,1126],{"class":1071},"magicLinks",[1058,1128,1129],{"class":1075},")               ",[1058,1131,1132],{"class":1064},"\u002F\u002F \u002Fauth\u002Fverify-mfa\u002F*, \u002Fauth\u002Freset-password\u002F*\n",[1058,1134,1136,1138,1140,1142,1144,1147,1150],{"class":1060,"line":1135},5,[1058,1137,1072],{"class":1071},[1058,1139,1076],{"class":1075},[1058,1141,1080],{"class":1079},[1058,1143,1083],{"class":1075},[1058,1145,1146],{"class":1071},"allowBff",[1058,1148,1149],{"class":1075},")                 ",[1058,1151,1152],{"class":1064},"\u002F\u002F \u002Fsecret\u002Fdata, \u002Fsecret\u002Faccesstoken\u002Fmetadata\n",[1058,1154,1156,1158,1160,1162,1164,1167,1170],{"class":1060,"line":1155},6,[1058,1157,1072],{"class":1071},[1058,1159,1076],{"class":1075},[1058,1161,1080],{"class":1079},[1058,1163,1083],{"class":1075},[1058,1165,1166],{"class":1079},"apiProtectedRoutes",[1058,1168,1169],{"class":1075},"())     ",[1058,1171,1172],{"class":1064},"\u002F\u002F \u002Fapi\u002Fmanage\u002F*\n",[1058,1174,1176,1178,1180,1182,1184,1188,1191,1193,1195,1198],{"class":1060,"line":1175},7,[1058,1177,1072],{"class":1071},[1058,1179,1076],{"class":1075},[1058,1181,1080],{"class":1079},[1058,1183,1083],{"class":1075},[1058,1185,1187],{"class":1186},"sFkSl","'",[1058,1189,909],{"class":1190},"sFB1V",[1058,1192,1187],{"class":1186},[1058,1194,968],{"class":1075},[1058,1196,1197],{"class":1071},"sendOperationalConfig",[1058,1199,1200],{"class":1075},")\n",[925,1202,1204],{"id":1203},"route-table","Route table",[1206,1207,1208,1230],"table",{},[1209,1210,1211],"thead",{},[1212,1213,1214,1218,1221,1224,1227],"tr",{},[1215,1216,1217],"th",{},"Method",[1215,1219,1220],{},"Path",[1215,1222,1223],{},"Middleware chain",[1215,1225,1226],{},"Controller",[1215,1228,1229],{},"Purpose",[1231,1232,1233,1270,1304],"tbody",{},[1212,1234,1235,1241,1246,1262,1267],{},[1236,1237,1238],"td",{},[867,1239,1240],{},"GET",[1236,1242,1243],{},[867,1244,1245],{},"\u002Fsecret\u002Fdata",[1236,1247,1248,968,1251,968,1254,968,1257,968,1259],{},[867,1249,1250],{},"requireAccessToken",[867,1252,1253],{},"requireRefreshToken",[867,1255,1256],{},"getFingerPrint",[867,1258,905],{},[867,1260,1261],{},"protectRoute",[1236,1263,1264],{},[867,1265,1266],{},"allowBffAccess",[1236,1268,1269],{},"Authorization check",[1212,1271,1272,1276,1281,1296,1301],{},[1236,1273,1274],{},[867,1275,1240],{},[1236,1277,1278],{},[867,1279,1280],{},"\u002Fsecret\u002Faccesstoken\u002Fmetadata",[1236,1282,1283,968,1285,968,1287,968,1289,968,1291,968,1293],{},[867,1284,1250],{},[867,1286,1253],{},[867,1288,1256],{},[867,1290,905],{},[867,1292,1261],{},[867,1294,1295],{},"acceptCookieOnly",[1236,1297,1298],{},[867,1299,1300],{},"getAccessTokenPayload",[1236,1302,1303],{},"Token TTL and rotation hint",[1212,1305,1306,1310,1314,1317,1321],{},[1236,1307,1308],{},[867,1309,1240],{},[1236,1311,1312],{},[867,1313,909],{},[1236,1315,1316],{},"IP restriction (no middleware chain)",[1236,1318,1319],{},[867,1320,1197],{},[1236,1322,1323],{},"Cookie domain and token TTL",[944,1325],{},[947,1327,1223],{"id":1328},"middleware-chain",[856,1330,1331,1332,1334,1335,1337],{},"Every request to ",[867,1333,1245],{}," and ",[867,1336,1280],{}," passes\nthrough five shared middleware functions before reaching the controller. The\nmetadata route adds a sixth guard. Each middleware either populates request\ncontext for downstream handlers or terminates the request with an error.",[925,1339,1341],{"id":1340},"requireaccesstoken",[867,1342,1250],{},[856,1344,1345,1346,1349,1350,1353,1354,1357,1358,1361],{},"Extracts the JWT access token from the ",[867,1347,1348],{},"Authorization"," header. The header must use the ",[867,1351,1352],{},"Bearer"," scheme. If the header is missing, empty, or uses a different scheme, the middleware responds with HTTP 401 and ",[867,1355,1356],{},"{ ok: false, error: \"Missing Bearer token\" }",". On success, it stores the raw token string on ",[867,1359,1360],{},"req.token"," for the next middleware to verify.",[925,1363,1365],{"id":1364},"requirerefreshtoken",[867,1366,1253],{},[856,1368,1369,1370,1372,1373,1376],{},"Reads the ",[867,1371,985],{}," cookie from the request. If the cookie is absent, the middleware responds with HTTP 401 and ",[867,1374,1375],{},"{ error: \"Refresh token missing\" }",". It does not validate the token's contents or hash; that responsibility belongs to rotation endpoints. Its purpose here is to confirm that the caller holds a session cookie, which proves the request comes from a context where the user has logged in.",[925,1378,1380],{"id":1379},"getfingerprint",[867,1381,1256],{},[856,1383,1384,1385,1388,1389,1391,1392,1394],{},"Extracts device and network metadata from the request headers and attaches a\nfingerprint to the request context. This includes the raw ",[867,1386,1387],{},"User-Agent",", the\nresolved IP address, geolocation data, and parsed browser and device details.\nThe fingerprint is consumed by ",[867,1390,1261],{}," for anomaly scoring. See\n",[860,1393,127],{"href":128}," for the full list of\nsignals collected.",[925,1396,1398],{"id":1397},"checkforactivemfa",[867,1399,905],{},[856,1401,1402,1403,1405,1406,1409,1410,1413,1414,1417,1418,1421,1422,1424],{},"Hashes the ",[867,1404,985],{}," cookie and checks ",[867,1407,1408],{},"anomaliesCache()"," for an unresolved MFA\nstate. If the current session already has a resolvable MFA challenge, the\nmiddleware returns ",[867,1411,1412],{},"202 { mfa: true }",". If the cached anomaly is not resolvable,\nit returns ",[867,1415,1416],{},"401 { error: 'Re-login is required', ... }",". Otherwise it calls\n",[867,1419,1420],{},"next()"," and lets ",[867,1423,1261],{}," continue.",[925,1426,1428],{"id":1427},"protectroute",[867,1429,1261],{},[856,1431,1432],{},"The core security layer. It performs two operations:",[1434,1435,1436,1450],"ol",{},[878,1437,1438,1442,1443,1446,1447,1449],{},[1439,1440,1441],"strong",{},"JWT verification."," Calls ",[867,1444,1445],{},"verifyAccessToken"," with the token from ",[867,1448,1360],{},". The verification uses a cache-backed lookup: the SHA-256 hash of the token is checked against stored access token records. If the token is revoked, expired, or not found, the middleware returns HTTP 401.",[878,1451,1452,1442,1455,1458,1459,1461,1462,1606,1609,1610,1614],{},[1439,1453,1454],{},"Anomaly detection.",[867,1456,1457],{},"strangeThings()"," with the verified token data and the fingerprint from ",[867,1460,1256],{},". This function runs a series of checks:",[1206,1463,1464,1477],{},[1209,1465,1466],{},[1212,1467,1468,1471,1474],{},[1215,1469,1470],{},"Check",[1215,1472,1473],{},"Trigger",[1215,1475,1476],{},"Result",[1231,1478,1479,1490,1511,1527,1542,1555,1566,1576,1586,1596],{},[1212,1480,1481,1484,1487],{},[1236,1482,1483],{},"Token not found",[1236,1485,1486],{},"SHA-256 hash does not match any stored record",[1236,1488,1489],{},"HTTP 401, invalid token",[1212,1491,1492,1495,1509],{},[1236,1493,1494],{},"Token revoked",[1236,1496,1497,1498,1501,1502,1505,1506],{},"Token's ",[867,1499,1500],{},"valid"," flag is ",[867,1503,1504],{},"false"," or ",[867,1507,1508],{},"usage_count > 1",[1236,1510,1489],{},[1212,1512,1513,1516,1521],{},[1236,1514,1515],{},"Canary mismatch",[1236,1517,1518,1520],{},[867,1519,993],{}," cookie does not match the session record",[1236,1522,1523,1524],{},"HTTP 401 + ",[860,1525,1526],{"href":124},"MFA challenge",[1212,1528,1529,1532,1539],{},[1236,1530,1531],{},"Idle session",[1236,1533,1534,1535,1538],{},"User's ",[867,1536,1537],{},"last_seen"," exceeds 24 hours",[1236,1540,1541],{},"HTTP 401 + MFA challenge",[1212,1543,1544,1547,1553],{},[1236,1545,1546],{},"Session overflow",[1236,1548,1549,1550],{},"Active sessions exceed ",[867,1551,1552],{},"maxAllowedSessionsPerUser",[1236,1554,1541],{},[1212,1556,1557,1560,1563],{},[1236,1558,1559],{},"Token spam",[1236,1561,1562],{},"3 or more tokens issued within 10 minutes",[1236,1564,1565],{},"Revoke all tokens, HTTP 401",[1212,1567,1568,1571,1574],{},[1236,1569,1570],{},"IP range mismatch",[1236,1572,1573],{},"Client IP falls outside the known range for this session",[1236,1575,1541],{},[1212,1577,1578,1581,1584],{},[1236,1579,1580],{},"High suspicion score",[1236,1582,1583],{},"Suspicion score reaches 25% of the ban threshold",[1236,1585,1541],{},[1212,1587,1588,1591,1594],{},[1236,1589,1590],{},"Proxy\u002Fhosting detected",[1236,1592,1593],{},"IP identified as proxy or hosting and user is not allowlisted",[1236,1595,1541],{},[1212,1597,1598,1601,1604],{},[1236,1599,1600],{},"Device fingerprint mismatch",[1236,1602,1603],{},"Browser, OS, or location differs from the session baseline",[1236,1605,1541],{},[1607,1608],"br",{},"When an anomaly triggers an MFA challenge, the IAM service responds with HTTP 202 and sends a magic link email. The BFF is expected to relay this 202 to the browser so the user can verify their identity. Anomalies that trigger within the ",[860,1611,1613],{"href":1612},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#bypass-window","MFA bypass window"," are skipped.",[856,1616,1617,1618,1620,1621,1624],{},"On success, ",[867,1619,1261],{}," populates ",[867,1622,1623],{},"req.user"," with:",[1206,1626,1627,1640],{},[1209,1628,1629],{},[1212,1630,1631,1634,1637],{},[1215,1632,1633],{},"Field",[1215,1635,1636],{},"Type",[1215,1638,1639],{},"Description",[1231,1641,1642,1661,1677,1694,1711],{},[1212,1643,1644,1649,1654],{},[1236,1645,1646],{},[867,1647,1648],{},"userId",[1236,1650,1651],{},[867,1652,1653],{},"string",[1236,1655,1656,1657,1660],{},"The ",[867,1658,1659],{},"sub"," claim from the JWT",[1212,1662,1663,1668,1672],{},[1236,1664,1665],{},[867,1666,1667],{},"visitor_id",[1236,1669,1670],{},[867,1671,1653],{},[1236,1673,1656,1674,1660],{},[867,1675,1676],{},"visitor",[1212,1678,1679,1684,1688],{},[1236,1680,1681],{},[867,1682,1683],{},"accessTokenId",[1236,1685,1686],{},[867,1687,1653],{},[1236,1689,1656,1690,1693],{},[867,1691,1692],{},"jti"," claim (JWT ID)",[1212,1695,1696,1701,1706],{},[1236,1697,1698],{},[867,1699,1700],{},"roles",[1236,1702,1703],{},[867,1704,1705],{},"string[]",[1236,1707,1656,1708,1710],{},[867,1709,1700],{}," claim from the JWT (may be undefined)",[1212,1712,1713,1718,1723],{},[1236,1714,1715],{},[867,1716,1717],{},"payload",[1236,1719,1720],{},[867,1721,1722],{},"JwtPayload",[1236,1724,1725],{},"The full decoded JWT payload",[925,1727,1729,1731],{"id":1728},"acceptcookieonly-metadata-route-only",[867,1730,1295],{}," (metadata route only)",[856,1733,1734,1735,1737],{},"The metadata route adds an extra guard after ",[867,1736,1261],{},". This middleware enforces a strict contract: the request must carry cookies and nothing else. It rejects the request under any of these conditions:",[1206,1739,1740,1753],{},[1209,1741,1742],{},[1212,1743,1744,1747,1750],{},[1215,1745,1746],{},"Condition",[1215,1748,1749],{},"Status",[1215,1751,1752],{},"Response",[1231,1754,1755,1771,1794,1808],{},[1212,1756,1757,1762,1767],{},[1236,1758,1759,1761],{},[867,1760,985],{}," cookie is missing",[1236,1763,1764],{},[867,1765,1766],{},"401",[1236,1768,1769],{},[867,1770,1375],{},[1212,1772,1773,1784,1789],{},[1236,1774,1775,1776,1779,1780,1783],{},"Request has a body (parsed JSON, ",[867,1777,1778],{},"content-length > 0",", or ",[867,1781,1782],{},"transfer-encoding: chunked",")",[1236,1785,1786],{},[867,1787,1788],{},"400",[1236,1790,1791],{},[867,1792,1793],{},"{ error: \"Request body not allowed\" }",[1212,1795,1796,1799,1803],{},[1236,1797,1798],{},"Query string is present",[1236,1800,1801],{},[867,1802,1788],{},[1236,1804,1805],{},[867,1806,1807],{},"{ error: \"Query string not allowed\" }",[1212,1809,1810,1816,1820],{},[1236,1811,1812,1815],{},[867,1813,1814],{},"Content-Type"," header is set",[1236,1817,1818],{},[867,1819,1788],{},[1236,1821,1822],{},[867,1823,1824],{},"{ error: \"Content-Type not allowed\" }",[856,1826,1827],{},"This prevents injection vectors on an endpoint whose only input should be the cookies attached by the browser. No body, query parameters, or content-type declarations are legitimate for this route.",[944,1829],{},[947,1831,1833,1834],{"id":1832},"authorization-check-get-secretdata","Authorization check: ",[867,1835,882],{},[856,1837,1656,1838,1840,1841,1843],{},[867,1839,1266],{}," controller validates that the upstream middleware successfully populated ",[867,1842,1623],{},", then confirms that the user and their visitor record exist in the database. If everything checks out, it returns a JSON payload with the authorization decision.",[925,1845,1847],{"id":1846},"response-shape","Response shape",[1049,1849,1852],{"className":1850,"code":1851,"language":5,"meta":1054,"style":1054},"language-json shiki shiki-themes light-plus light-plus dracula","{\n  \"userId\": 42,\n  \"authorized\": true,\n  \"ipAddress\": \"203.0.113.10\",\n  \"userAgent\": \"Mozilla\u002F5.0 ...\",\n  \"date\": \"2025-01-15T10:30:00.000Z\",\n  \"roles\": [\"admin\", \"editor\"]\n}\n",[867,1853,1854,1859,1882,1899,1920,1940,1960,1992],{"__ignoreMap":1054},[1058,1855,1856],{"class":1060,"line":1061},[1058,1857,1858],{"class":1075},"{\n",[1058,1860,1861,1865,1868,1871,1875,1879],{"class":1060,"line":1068},[1058,1862,1864],{"class":1863},"saJyd","  \"",[1058,1866,1648],{"class":1867},"s_W10",[1058,1869,1870],{"class":1863},"\"",[1058,1872,1874],{"class":1873},"saOXh",":",[1058,1876,1878],{"class":1877},"spgvN"," 42",[1058,1880,1881],{"class":1075},",\n",[1058,1883,1884,1886,1889,1891,1893,1897],{"class":1060,"line":1095},[1058,1885,1864],{"class":1863},[1058,1887,1888],{"class":1867},"authorized",[1058,1890,1870],{"class":1863},[1058,1892,1874],{"class":1873},[1058,1894,1896],{"class":1895},"sjR7W"," true",[1058,1898,1881],{"class":1075},[1058,1900,1901,1903,1906,1908,1910,1913,1916,1918],{"class":1060,"line":1115},[1058,1902,1864],{"class":1863},[1058,1904,1905],{"class":1867},"ipAddress",[1058,1907,1870],{"class":1863},[1058,1909,1874],{"class":1873},[1058,1911,1912],{"class":1186}," \"",[1058,1914,1915],{"class":1190},"203.0.113.10",[1058,1917,1870],{"class":1186},[1058,1919,1881],{"class":1075},[1058,1921,1922,1924,1927,1929,1931,1933,1936,1938],{"class":1060,"line":1135},[1058,1923,1864],{"class":1863},[1058,1925,1926],{"class":1867},"userAgent",[1058,1928,1870],{"class":1863},[1058,1930,1874],{"class":1873},[1058,1932,1912],{"class":1186},[1058,1934,1935],{"class":1190},"Mozilla\u002F5.0 ...",[1058,1937,1870],{"class":1186},[1058,1939,1881],{"class":1075},[1058,1941,1942,1944,1947,1949,1951,1953,1956,1958],{"class":1060,"line":1155},[1058,1943,1864],{"class":1863},[1058,1945,1946],{"class":1867},"date",[1058,1948,1870],{"class":1863},[1058,1950,1874],{"class":1873},[1058,1952,1912],{"class":1186},[1058,1954,1955],{"class":1190},"2025-01-15T10:30:00.000Z",[1058,1957,1870],{"class":1186},[1058,1959,1881],{"class":1075},[1058,1961,1962,1964,1966,1968,1970,1973,1975,1978,1980,1982,1984,1987,1989],{"class":1060,"line":1175},[1058,1963,1864],{"class":1863},[1058,1965,1700],{"class":1867},[1058,1967,1870],{"class":1863},[1058,1969,1874],{"class":1873},[1058,1971,1972],{"class":1075}," [",[1058,1974,1870],{"class":1186},[1058,1976,1977],{"class":1190},"admin",[1058,1979,1870],{"class":1186},[1058,1981,968],{"class":1075},[1058,1983,1870],{"class":1186},[1058,1985,1986],{"class":1190},"editor",[1058,1988,1870],{"class":1186},[1058,1990,1991],{"class":1075},"]\n",[1058,1993,1995],{"class":1060,"line":1994},8,[1058,1996,1997],{"class":1075},"}\n",[1999,2000,2001,2011,2021,2030,2037,2042],"field-group",{},[2002,2003,2005],"field",{"name":1648,"type":2004},"number",[856,2006,2007,2008,2010],{},"The authenticated user's database ID, extracted from the JWT ",[867,2009,1659],{}," claim.",[2002,2012,2014],{"name":1888,"type":2013},"boolean",[856,2015,2016,2017,2020],{},"Always ",[867,2018,2019],{},"true"," in a successful response. The BFF proxy uses this flag to decide whether to proceed with the application request.",[2002,2022,2023],{"name":1905,"type":1653},[856,2024,2025,2026,2029],{},"The client IP address as seen by the IAM service. When the BFF forwards ",[867,2027,2028],{},"X-Forwarded-For",", this reflects the original browser IP.",[2002,2031,2032],{"name":1926,"type":1653},[856,2033,1656,2034,2036],{},[867,2035,1387],{}," header value from the request.",[2002,2038,2039],{"name":1946,"type":1653},[856,2040,2041],{},"ISO 8601 timestamp of when the response was generated.",[2002,2043,2045],{"name":1700,"type":2044},"string[] | string",[856,2046,2047,2048,2051],{},"The roles embedded in the access token. Returns the array of role strings when roles are present, or ",[867,2049,2050],{},"\"No roles added with this token.\""," when the token carries no roles.",[925,2053,2055],{"id":2054},"error-responses","Error responses",[1206,2057,2058,2070],{},[1209,2059,2060],{},[1212,2061,2062,2064,2067],{},[1215,2063,1749],{},[1215,2065,2066],{},"Body",[1215,2068,2069],{},"Cause",[1231,2071,2072,2087,2102,2118,2133],{},[1212,2073,2074,2079,2084],{},[1236,2075,2076],{},[867,2077,2078],{},"200",[1236,2080,2081],{},[867,2082,2083],{},"{ authorized: true, ... }",[1236,2085,2086],{},"User and visitor exist, middleware chain passed",[1212,2088,2089,2094,2099],{},[1236,2090,2091],{},[867,2092,2093],{},"202",[1236,2095,2096,2097,1783],{},"MFA challenge (from ",[867,2098,1261],{},[1236,2100,2101],{},"Anomaly detected, magic link sent",[1212,2103,2104,2108,2113],{},[1236,2105,2106],{},[867,2107,1766],{},[1236,2109,2110],{},[867,2111,2112],{},"{ authorized: false, reason: \"Not authenticated\" }",[1236,2114,2115,2117],{},[867,2116,1623],{}," was not populated by upstream middleware",[1212,2119,2120,2125,2130],{},[1236,2121,2122],{},[867,2123,2124],{},"404",[1236,2126,2127],{},[867,2128,2129],{},"{ authorized: false, reason: \"Not found\" }",[1236,2131,2132],{},"User or visitor record does not exist in the database",[1212,2134,2135,2140,2143],{},[1236,2136,2137],{},[867,2138,2139],{},"500",[1236,2141,2142],{},"Logged internally",[1236,2144,2145],{},"Database query failure. The controller catches the error and logs it without exposing details",[2147,2148,2149],"note",{},[856,2150,2151,2152,2154,2155,2157],{},"The controller trusts that ",[867,2153,1261],{}," has already verified the JWT, run anomaly detection, and populated ",[867,2156,1623],{},". It does not re-validate the token. The database lookups serve as a final consistency check: the user and visitor must still exist at query time.",[925,2159,2161],{"id":2160},"observability","Observability",[856,2163,2164,2165,2167,2168,2171,2172,2175,2176,2179],{},"Every request to this endpoint produces a structured log entry with the client IP, request ID, user agent, original URL, and ",[867,2166,993],{}," cookie value. Successful authorizations log at ",[867,2169,2170],{},"info"," level. Failed attempts log at ",[867,2173,2174],{},"warn"," level with the specific reason. Database errors log at ",[867,2177,2178],{},"error"," level without exposing the error details in the response.",[944,2181],{},[947,2183,2185,2186],{"id":2184},"token-metadata-get-secretaccesstokenmetadata","Token metadata: ",[867,2187,888],{},[856,2189,1656,2190,2192],{},[867,2191,1300],{}," controller returns the decoded JWT payload along with computed TTL information. The BFF proxy uses this data to decide whether to proactively rotate the access token before it expires.",[925,2194,1847],{"id":2195},"response-shape-1",[1049,2197,2199],{"className":1850,"code":2198,"language":5,"meta":1054,"style":1054},"{\n  \"authorized\": true,\n  \"ipAddress\": \"203.0.113.10\",\n  \"userAgent\": \"Mozilla\u002F5.0 ...\",\n  \"date\": \"2025-01-15T10:30:00.000Z\",\n  \"roles\": [\"admin\"],\n  \"payload\": {\n    \"sub\": \"42\",\n    \"visitor\": \"v_abc123\",\n    \"jti\": \"tok_xyz789\",\n    \"roles\": [\"admin\"],\n    \"iat\": 1736934600,\n    \"exp\": 1736935500,\n    \"aud\": \"example.com\",\n    \"iss\": \"example.com\"\n  },\n  \"msUntilExp\": 540000,\n  \"refreshThreshold\": 225000,\n  \"shouldRotate\": false\n}\n",[867,2200,2201,2205,2219,2237,2255,2273,2294,2307,2327,2347,2367,2388,2405,2422,2443,2462,2468,2485,2502,2517],{"__ignoreMap":1054},[1058,2202,2203],{"class":1060,"line":1061},[1058,2204,1858],{"class":1075},[1058,2206,2207,2209,2211,2213,2215,2217],{"class":1060,"line":1068},[1058,2208,1864],{"class":1863},[1058,2210,1888],{"class":1867},[1058,2212,1870],{"class":1863},[1058,2214,1874],{"class":1873},[1058,2216,1896],{"class":1895},[1058,2218,1881],{"class":1075},[1058,2220,2221,2223,2225,2227,2229,2231,2233,2235],{"class":1060,"line":1095},[1058,2222,1864],{"class":1863},[1058,2224,1905],{"class":1867},[1058,2226,1870],{"class":1863},[1058,2228,1874],{"class":1873},[1058,2230,1912],{"class":1186},[1058,2232,1915],{"class":1190},[1058,2234,1870],{"class":1186},[1058,2236,1881],{"class":1075},[1058,2238,2239,2241,2243,2245,2247,2249,2251,2253],{"class":1060,"line":1115},[1058,2240,1864],{"class":1863},[1058,2242,1926],{"class":1867},[1058,2244,1870],{"class":1863},[1058,2246,1874],{"class":1873},[1058,2248,1912],{"class":1186},[1058,2250,1935],{"class":1190},[1058,2252,1870],{"class":1186},[1058,2254,1881],{"class":1075},[1058,2256,2257,2259,2261,2263,2265,2267,2269,2271],{"class":1060,"line":1135},[1058,2258,1864],{"class":1863},[1058,2260,1946],{"class":1867},[1058,2262,1870],{"class":1863},[1058,2264,1874],{"class":1873},[1058,2266,1912],{"class":1186},[1058,2268,1955],{"class":1190},[1058,2270,1870],{"class":1186},[1058,2272,1881],{"class":1075},[1058,2274,2275,2277,2279,2281,2283,2285,2287,2289,2291],{"class":1060,"line":1155},[1058,2276,1864],{"class":1863},[1058,2278,1700],{"class":1867},[1058,2280,1870],{"class":1863},[1058,2282,1874],{"class":1873},[1058,2284,1972],{"class":1075},[1058,2286,1870],{"class":1186},[1058,2288,1977],{"class":1190},[1058,2290,1870],{"class":1186},[1058,2292,2293],{"class":1075},"],\n",[1058,2295,2296,2298,2300,2302,2304],{"class":1060,"line":1175},[1058,2297,1864],{"class":1863},[1058,2299,1717],{"class":1867},[1058,2301,1870],{"class":1863},[1058,2303,1874],{"class":1873},[1058,2305,2306],{"class":1075}," {\n",[1058,2308,2309,2312,2314,2316,2318,2320,2323,2325],{"class":1060,"line":1994},[1058,2310,2311],{"class":1863},"    \"",[1058,2313,1659],{"class":1867},[1058,2315,1870],{"class":1863},[1058,2317,1874],{"class":1873},[1058,2319,1912],{"class":1186},[1058,2321,2322],{"class":1190},"42",[1058,2324,1870],{"class":1186},[1058,2326,1881],{"class":1075},[1058,2328,2330,2332,2334,2336,2338,2340,2343,2345],{"class":1060,"line":2329},9,[1058,2331,2311],{"class":1863},[1058,2333,1676],{"class":1867},[1058,2335,1870],{"class":1863},[1058,2337,1874],{"class":1873},[1058,2339,1912],{"class":1186},[1058,2341,2342],{"class":1190},"v_abc123",[1058,2344,1870],{"class":1186},[1058,2346,1881],{"class":1075},[1058,2348,2350,2352,2354,2356,2358,2360,2363,2365],{"class":1060,"line":2349},10,[1058,2351,2311],{"class":1863},[1058,2353,1692],{"class":1867},[1058,2355,1870],{"class":1863},[1058,2357,1874],{"class":1873},[1058,2359,1912],{"class":1186},[1058,2361,2362],{"class":1190},"tok_xyz789",[1058,2364,1870],{"class":1186},[1058,2366,1881],{"class":1075},[1058,2368,2370,2372,2374,2376,2378,2380,2382,2384,2386],{"class":1060,"line":2369},11,[1058,2371,2311],{"class":1863},[1058,2373,1700],{"class":1867},[1058,2375,1870],{"class":1863},[1058,2377,1874],{"class":1873},[1058,2379,1972],{"class":1075},[1058,2381,1870],{"class":1186},[1058,2383,1977],{"class":1190},[1058,2385,1870],{"class":1186},[1058,2387,2293],{"class":1075},[1058,2389,2391,2393,2396,2398,2400,2403],{"class":1060,"line":2390},12,[1058,2392,2311],{"class":1863},[1058,2394,2395],{"class":1867},"iat",[1058,2397,1870],{"class":1863},[1058,2399,1874],{"class":1873},[1058,2401,2402],{"class":1877}," 1736934600",[1058,2404,1881],{"class":1075},[1058,2406,2408,2410,2413,2415,2417,2420],{"class":1060,"line":2407},13,[1058,2409,2311],{"class":1863},[1058,2411,2412],{"class":1867},"exp",[1058,2414,1870],{"class":1863},[1058,2416,1874],{"class":1873},[1058,2418,2419],{"class":1877}," 1736935500",[1058,2421,1881],{"class":1075},[1058,2423,2425,2427,2430,2432,2434,2436,2439,2441],{"class":1060,"line":2424},14,[1058,2426,2311],{"class":1863},[1058,2428,2429],{"class":1867},"aud",[1058,2431,1870],{"class":1863},[1058,2433,1874],{"class":1873},[1058,2435,1912],{"class":1186},[1058,2437,2438],{"class":1190},"example.com",[1058,2440,1870],{"class":1186},[1058,2442,1881],{"class":1075},[1058,2444,2446,2448,2451,2453,2455,2457,2459],{"class":1060,"line":2445},15,[1058,2447,2311],{"class":1863},[1058,2449,2450],{"class":1867},"iss",[1058,2452,1870],{"class":1863},[1058,2454,1874],{"class":1873},[1058,2456,1912],{"class":1186},[1058,2458,2438],{"class":1190},[1058,2460,2461],{"class":1186},"\"\n",[1058,2463,2465],{"class":1060,"line":2464},16,[1058,2466,2467],{"class":1075},"  },\n",[1058,2469,2471,2473,2476,2478,2480,2483],{"class":1060,"line":2470},17,[1058,2472,1864],{"class":1863},[1058,2474,2475],{"class":1867},"msUntilExp",[1058,2477,1870],{"class":1863},[1058,2479,1874],{"class":1873},[1058,2481,2482],{"class":1877}," 540000",[1058,2484,1881],{"class":1075},[1058,2486,2488,2490,2493,2495,2497,2500],{"class":1060,"line":2487},18,[1058,2489,1864],{"class":1863},[1058,2491,2492],{"class":1867},"refreshThreshold",[1058,2494,1870],{"class":1863},[1058,2496,1874],{"class":1873},[1058,2498,2499],{"class":1877}," 225000",[1058,2501,1881],{"class":1075},[1058,2503,2505,2507,2510,2512,2514],{"class":1060,"line":2504},19,[1058,2506,1864],{"class":1863},[1058,2508,2509],{"class":1867},"shouldRotate",[1058,2511,1870],{"class":1863},[1058,2513,1874],{"class":1873},[1058,2515,2516],{"class":1895}," false\n",[1058,2518,2520],{"class":1060,"line":2519},20,[1058,2521,1997],{"class":1075},[1999,2523,2524,2531,2558,2571,2576],{},[2002,2525,2526],{"name":1888,"type":2013},[856,2527,2016,2528,2530],{},[867,2529,2019],{}," in a successful response.",[2002,2532,2533],{"name":1717,"type":1722},[856,2534,2535,2536,2538,2539,2541,2542,2544,2545,2547,2548,968,2550,968,2552,968,2554,1009,2556,1076],{},"The full decoded JWT payload as attached by ",[867,2537,1261],{},". Contains ",[867,2540,1659],{}," (user ID), ",[867,2543,1676],{}," (visitor ID), ",[867,2546,1692],{}," (token ID), ",[867,2549,1700],{},[867,2551,2395],{},[867,2553,2412],{},[867,2555,2429],{},[867,2557,2450],{},[2002,2559,2560],{"name":2475,"type":2004},[856,2561,2562,2563,2566,2567,2570],{},"Milliseconds remaining until the access token expires. Computed as ",[867,2564,2565],{},"Math.max(0, expMs - Date.now())",". Returns ",[867,2568,2569],{},"0"," when the token has already expired.",[2002,2572,2573],{"name":2492,"type":2004},[856,2574,2575],{},"The threshold in milliseconds below which the BFF should trigger a rotation. Computed as 25% of the configured access token TTL.",[2002,2577,2578],{"name":2509,"type":2013},[856,2579,2580,2582,2583,2586,2587,1076],{},[867,2581,2019],{}," when ",[867,2584,2585],{},"msUntilExp \u003C= refreshThreshold",". The BFF uses this flag to trigger proactive rotation by calling ",[867,2588,1023],{},[925,2590,2592],{"id":2591},"ttl-computation","TTL computation",[856,2594,2595,2596,1334,2598,2600],{},"The controller computes timing values from the JWT's ",[867,2597,2395],{},[867,2599,2412],{}," claims combined with the configured access token TTL:",[1049,2602,2607],{"className":2603,"code":2605,"language":2606},[2604],"language-text","TTL_MS     = config.jwt.access_tokens.expiresInMs ?? 900000   (default: 15 minutes)\nTHRESHOLD  = TTL_MS * 0.25                                    (25% of TTL)\niatMs      = payload.iat * 1000                               (or Date.now() if missing)\nexpMs      = payload.exp * 1000                               (or iatMs + TTL_MS if missing)\nmsUntilExp = max(0, expMs - now)\nshouldRotate = msUntilExp \u003C= THRESHOLD\n","text",[867,2608,2605],{"__ignoreMap":1054},[856,2610,2611,2612,2614,2615,2617,2618,2620],{},"When a 15-minute access token has less than 3 minutes and 45 seconds remaining, ",[867,2613,2509],{}," becomes ",[867,2616,2019],{},". The BFF can then call ",[867,2619,1023],{}," to obtain fresh tokens before the current ones expire.",[938,2622,2623],{},[856,2624,2625],{},"The 25% threshold is not configurable. It provides a balance between minimizing unnecessary rotations and ensuring the token is refreshed well before expiry. A 15-minute TTL triggers rotation in the last 3:45. A 5-minute TTL triggers rotation in the last 1:15.",[925,2627,2055],{"id":2628},"error-responses-1",[1206,2630,2631,2641],{},[1209,2632,2633],{},[1212,2634,2635,2637,2639],{},[1215,2636,1749],{},[1215,2638,2066],{},[1215,2640,2069],{},[1231,2642,2643,2657,2669,2683,2695],{},[1212,2644,2645,2649,2654],{},[1236,2646,2647],{},[867,2648,2078],{},[1236,2650,2651],{},[867,2652,2653],{},"{ authorized: true, payload, msUntilExp, ... }",[1236,2655,2656],{},"Token valid, metadata computed",[1212,2658,2659,2663,2667],{},[1236,2660,2661],{},[867,2662,2093],{},[1236,2664,2096,2665,1783],{},[867,2666,1261],{},[1236,2668,2101],{},[1212,2670,2671,2675,2679],{},[1236,2672,2673],{},[867,2674,1766],{},[1236,2676,2677],{},[867,2678,2112],{},[1236,2680,2681,2117],{},[867,2682,1623],{},[1212,2684,2685,2689,2693],{},[1236,2686,2687],{},[867,2688,2124],{},[1236,2690,2691],{},[867,2692,2129],{},[1236,2694,2132],{},[1212,2696,2697,2701,2706],{},[1236,2698,2699],{},[867,2700,2139],{},[1236,2702,2703],{},[867,2704,2705],{},"{ authorized: false, reason: \"Server error\" }",[1236,2707,2708],{},"Database query failure",[925,2710,2712],{"id":2711},"how-the-bff-uses-this-endpoint","How the BFF uses this endpoint",[856,2714,2715],{},"A typical BFF proxy calls this endpoint on every authenticated request to determine whether the access token is still usable or needs rotation:",[955,2717,2718,2722,2736,2743,2752,2756,2771,2775],{"level":957},[959,2719,2721],{"id":2720},"call-the-metadata-endpoint","Call the metadata endpoint",[856,2723,2724,2725,2727,2728,2730,2731,1334,2733,2735],{},"The BFF sends ",[867,2726,888],{}," with the access token as a ",[867,2729,1352],{}," header and the ",[867,2732,985],{},[867,2734,993],{}," cookies.",[959,2737,2739,2740,2742],{"id":2738},"read-the-shouldrotate-flag","Read the ",[867,2741,2509],{}," flag",[856,2744,2745,2746,2748,2749,2751],{},"If ",[867,2747,2509],{}," is ",[867,2750,1504],{},", the access token is still fresh. The BFF proceeds with the application request using the current token.",[959,2753,2755],{"id":2754},"trigger-rotation-when-needed","Trigger rotation when needed",[856,2757,2745,2758,2748,2760,2762,2763,2765,2766,1334,2768,2770],{},[867,2759,2509],{},[867,2761,2019],{},", the BFF calls ",[867,2764,1023],{}," on the IAM service, forwarding the ",[867,2767,985],{},[867,2769,993],{}," cookies. The IAM service validates the refresh token, generates new tokens, and returns them.",[959,2772,2774],{"id":2773},"forward-new-cookies","Forward new cookies",[856,2776,2777,2778,968,2780,1009,2782,2785],{},"After rotation, the BFF sets the new ",[867,2779,989],{},[867,2781,985],{},[867,2783,2784],{},"a-iat"," cookies on the browser response. The browser transparently picks up the refreshed tokens on the next request.",[2147,2787,2788],{},[856,2789,2790,2791,2794],{},"The BFF can cache the metadata response to avoid calling this endpoint on every single request. A safe cache TTL is ",[867,2792,2793],{},"msUntilExp - refreshThreshold - 5000"," (5-second safety margin for network latency). This ensures the cache entry expires just before the rotation window opens.",[944,2796],{},[947,2798,2800,2801],{"id":2799},"operational-configuration-get-operationalconfig","Operational configuration: ",[867,2802,894],{},[856,2804,2805],{},"The IAM service exposes a non-authenticated endpoint that returns runtime settings the BFF needs for cookie management. This endpoint does not use the middleware chain. Instead, it is gated by IP address: only requests from the configured trusted client IP receive a response.",[925,2807,2809],{"id":2808},"ip-validation","IP validation",[856,2811,2812,2813,2816,2817,2820,2821,2824,2825,2828,2829,1076],{},"The controller reads the raw socket address via ",[867,2814,2815],{},"req.socket.remoteAddress",", strips the IPv4-mapped IPv6 prefix (",[867,2818,2819],{},"::ffff:",") if present, and compares the result against ",[867,2822,2823],{},"config.service.clientIp",". If that field is not set, it falls back to ",[867,2826,2827],{},"config.service.proxy.ipToTrust",". Any mismatch returns HTTP 403 with ",[867,2830,2831],{},"{ error: \"Forbidden\" }",[2147,2833,2834],{},[856,2835,2836,2837,2839,2840,1505,2843,2845],{},"The comparison uses ",[867,2838,2815],{}," (the physical TCP connection address), not ",[867,2841,2842],{},"req.ip",[867,2844,2028],{},". This means the IP restriction cannot be bypassed by spoofing forwarded headers. The BFF must connect directly from the trusted IP.",[925,2847,1847],{"id":2848},"response-shape-2",[1049,2850,2852],{"className":1850,"code":2851,"language":5,"meta":1054,"style":1054},"{\n  \"domain\": \".example.com\",\n  \"accessTokenTTL\": 900000\n}\n",[867,2853,2854,2858,2878,2892],{"__ignoreMap":1054},[1058,2855,2856],{"class":1060,"line":1061},[1058,2857,1858],{"class":1075},[1058,2859,2860,2862,2865,2867,2869,2871,2874,2876],{"class":1060,"line":1068},[1058,2861,1864],{"class":1863},[1058,2863,2864],{"class":1867},"domain",[1058,2866,1870],{"class":1863},[1058,2868,1874],{"class":1873},[1058,2870,1912],{"class":1186},[1058,2872,2873],{"class":1190},".example.com",[1058,2875,1870],{"class":1186},[1058,2877,1881],{"class":1075},[1058,2879,2880,2882,2885,2887,2889],{"class":1060,"line":1095},[1058,2881,1864],{"class":1863},[1058,2883,2884],{"class":1867},"accessTokenTTL",[1058,2886,1870],{"class":1863},[1058,2888,1874],{"class":1873},[1058,2890,2891],{"class":1877}," 900000\n",[1058,2893,2894],{"class":1060,"line":1115},[1058,2895,1997],{"class":1075},[1999,2897,2898,2911],{},[2002,2899,2900],{"name":2864,"type":1653},[856,2901,2902,2903,2906,2907,2910],{},"The cookie domain from ",[867,2904,2905],{},"config.jwt.refresh_tokens.domain",". The BFF uses this value as the ",[867,2908,2909],{},"Domain"," attribute when setting the refresh token cookie on the browser.",[2002,2912,2913],{"name":2884,"type":2004},[856,2914,2915,2916,2919,2920,2923,2924,2926],{},"The access token lifetime in milliseconds from ",[867,2917,2918],{},"config.jwt.access_tokens.expiresInMs"," (defaults to 900000, which is 15 minutes). The BFF uses this as the ",[867,2921,2922],{},"maxAge"," for the ",[867,2925,989],{}," cookie.",[856,2928,2929],{},"The BFF typically calls this endpoint once at startup and caches the result. Since these values change only when the IAM service configuration changes, a 24-hour cache TTL is reasonable.",[944,2931],{},[947,2933,2935],{"id":2934},"exports-for-library-consumers","Exports for library consumers",[856,2937,2938,2939,2942],{},"The BFF routes and controllers are exported individually from the ",[867,2940,2941],{},"@riavzon\u002Fauth"," package. This allows library consumers to mount them in their own Express applications, compose them with additional middleware, or use the controllers standalone.",[1049,2944,2946],{"className":1051,"code":2945,"language":1053,"meta":1054,"style":1054},"import {\n  bffAccessRoute,\n  allowBffAccess,\n  getAccessTokenPayload,\n  sendOperationalConfig\n} from '@riavzon\u002Fauth'\n",[867,2947,2948,2956,2963,2970,2977,2982],{"__ignoreMap":1054},[1058,2949,2950,2954],{"class":1060,"line":1061},[1058,2951,2953],{"class":2952},"sZ328","import",[1058,2955,2306],{"class":1075},[1058,2957,2958,2961],{"class":1060,"line":1068},[1058,2959,2960],{"class":1071},"  bffAccessRoute",[1058,2962,1881],{"class":1075},[1058,2964,2965,2968],{"class":1060,"line":1095},[1058,2966,2967],{"class":1071},"  allowBffAccess",[1058,2969,1881],{"class":1075},[1058,2971,2972,2975],{"class":1060,"line":1115},[1058,2973,2974],{"class":1071},"  getAccessTokenPayload",[1058,2976,1881],{"class":1075},[1058,2978,2979],{"class":1060,"line":1135},[1058,2980,2981],{"class":1071},"  sendOperationalConfig\n",[1058,2983,2984,2987,2990,2993,2995],{"class":1060,"line":1155},[1058,2985,2986],{"class":1075},"} ",[1058,2988,2989],{"class":2952},"from",[1058,2991,2992],{"class":1186}," '",[1058,2994,2941],{"class":1190},[1058,2996,2997],{"class":1186},"'\n",[1206,2999,3000,3011],{},[1209,3001,3002],{},[1212,3003,3004,3007,3009],{},[1215,3005,3006],{},"Export",[1215,3008,1636],{},[1215,3010,1639],{},[1231,3012,3013,3033,3047,3063],{},[1212,3014,3015,3020,3025],{},[1236,3016,3017],{},[867,3018,3019],{},"bffAccessRoute",[1236,3021,3022],{},[867,3023,3024],{},"Router",[1236,3026,3027,3028,1334,3030,3032],{},"The full router with both ",[867,3029,1245],{},[867,3031,1280],{}," routes, including all middleware",[1212,3034,3035,3039,3044],{},[1236,3036,3037],{},[867,3038,1266],{},[1236,3040,3041],{},[867,3042,3043],{},"RequestHandler",[1236,3045,3046],{},"The standalone authorization controller. Can be mounted with custom middleware chains",[1212,3048,3049,3053,3057],{},[1236,3050,3051],{},[867,3052,1300],{},[1236,3054,3055],{},[867,3056,3043],{},[1236,3058,3059,3060,3062],{},"The standalone metadata controller. Expects ",[867,3061,1623],{}," to be populated by upstream middleware",[1212,3064,3065,3069,3073],{},[1236,3066,3067],{},[867,3068,1197],{},[1236,3070,3071],{},[867,3072,3043],{},[1236,3074,3075],{},"The operational config controller. Handles its own IP validation internally",[856,3077,3078],{},"The supporting middleware functions are also exported for building custom pipelines:",[1049,3080,3082],{"className":1051,"code":3081,"language":1053,"meta":1054,"style":1054},"import {\n  requireAccessToken,\n  requireRefreshToken,\n  protectRoute\n} from '@riavzon\u002Fauth'\n",[867,3083,3084,3090,3097,3104,3109],{"__ignoreMap":1054},[1058,3085,3086,3088],{"class":1060,"line":1061},[1058,3087,2953],{"class":2952},[1058,3089,2306],{"class":1075},[1058,3091,3092,3095],{"class":1060,"line":1068},[1058,3093,3094],{"class":1071},"  requireAccessToken",[1058,3096,1881],{"class":1075},[1058,3098,3099,3102],{"class":1060,"line":1095},[1058,3100,3101],{"class":1071},"  requireRefreshToken",[1058,3103,1881],{"class":1075},[1058,3105,3106],{"class":1060,"line":1115},[1058,3107,3108],{"class":1071},"  protectRoute\n",[1058,3110,3111,3113,3115,3117,3119],{"class":1060,"line":1135},[1058,3112,2986],{"class":1075},[1058,3114,2989],{"class":2952},[1058,3116,2992],{"class":1186},[1058,3118,2941],{"class":1190},[1058,3120,2997],{"class":1186},[856,3122,3123],{},"When using the IAM service in standalone mode, all routes are automatically mounted. When using the library, you can mount them yourself with your own middleware composition. For example, you could add rate limiting or logging middleware before the controller without modifying the IAM source.",[944,3125],{},[947,3127,3129],{"id":3128},"hmac-integration","HMAC integration",[856,3131,3132,3133,3136,3137,3140,3141,968,3143,1009,3145,3147],{},"When the IAM service has HMAC authentication enabled (",[867,3134,3135],{},"service.Hmac","), the ",[867,3138,3139],{},"hmacAuth"," middleware runs before all route groups, including the BFF routes. Every request to ",[867,3142,1245],{},[867,3144,1280],{},[867,3146,909],{}," must carry valid HMAC headers in addition to the JWT and refresh token cookies.",[856,3149,3150,3151,1334,3154,3157,3158,3160],{},"The BFF proxy must sign each outbound request with the same ",[867,3152,3153],{},"clientId",[867,3155,3156],{},"sharedSecret"," configured on the IAM service. See ",[860,3159,135],{"href":136}," for the signature format, clock-skew tolerance, and replay protection details.",[3162,3163,3164],"warning",{},[856,3165,3166],{},"If HMAC is enabled on the IAM service but the BFF does not sign its requests, all BFF calls will receive HTTP 401 with a specific HMAC failure reason (missing headers, unknown client, stale timestamp, or signature mismatch).",[944,3168],{},[947,3170,3172],{"id":3171},"cookie-lifecycle","Cookie lifecycle",[856,3174,3175,3176,968,3179,968,3182,3184],{},"The BFF pattern relies on cookies that flow between the browser, the BFF proxy, and the IAM service. The IAM service sets refresh token cookies directly on authentication responses (",[867,3177,3178],{},"POST \u002Flogin",[867,3180,3181],{},"POST \u002Fsignup",[867,3183,1023],{},"). The BFF proxy is responsible for setting the access token cookie on the browser and forwarding refresh token cookies in both directions.",[1206,3186,3187,3215],{},[1209,3188,3189],{},[1212,3190,3191,3194,3198,3202,3207,3210,3213],{},[1215,3192,3193],{},"Cookie",[1215,3195,3196],{},[867,3197,869],{},[1215,3199,3200],{},[867,3201,971],{},[1215,3203,3204],{},[867,3205,3206],{},"SameSite",[1215,3208,3209],{},"TTL",[1215,3211,3212],{},"Set by",[1215,3214,1229],{},[1231,3216,3217,3242,3265,3291],{},[1212,3218,3219,3223,3226,3228,3233,3236,3239],{},[1236,3220,3221],{},[867,3222,989],{},[1236,3224,3225],{},"Yes",[1236,3227,3225],{},[1236,3229,3230],{},[867,3231,3232],{},"Strict",[1236,3234,3235],{},"Access token TTL",[1236,3237,3238],{},"BFF proxy",[1236,3240,3241],{},"Carries the JWT access token",[1212,3243,3244,3248,3250,3252,3256,3259,3262],{},[1236,3245,3246],{},[867,3247,985],{},[1236,3249,3225],{},[1236,3251,3225],{},[1236,3253,3254],{},[867,3255,3232],{},[1236,3257,3258],{},"Refresh token TTL",[1236,3260,3261],{},"IAM service",[1236,3263,3264],{},"Carries the refresh token",[1212,3266,3267,3271,3273,3275,3280,3283,3285],{},[1236,3268,3269],{},[867,3270,993],{},[1236,3272,3225],{},[1236,3274,3225],{},[1236,3276,3277],{},[867,3278,3279],{},"Lax",[1236,3281,3282],{},"90 days",[1236,3284,3261],{},[1236,3286,3287,3288],{},"Session binding for ",[860,3289,1012],{"href":3290},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies#canary-id-lifecycle",[1212,3292,3293,3297,3299,3301,3305,3307,3309],{},[1236,3294,3295],{},[867,3296,2784],{},[1236,3298,3225],{},[1236,3300,3225],{},[1236,3302,3303],{},[867,3304,3232],{},[1236,3306,3235],{},[1236,3308,3238],{},[1236,3310,3311,3312,3314],{},"Stores the access token's ",[867,3313,2395],{}," (issued-at) timestamp",[856,3316,3317,3318,1334,3320,3322,3323,3325,3326,3328,3329,3331],{},"The IAM service validates the ",[867,3319,985],{},[867,3321,993],{}," cookies on every BFF request. The access token arrives as a ",[867,3324,1352],{}," header, not from a cookie, because the BFF extracts it from ",[867,3327,989],{}," and places it in the ",[867,3330,1348],{}," header before calling the IAM.",[938,3333,3334],{},[856,3335,1656,3336,3338,3339,3341,3342,3344],{},[867,3337,989],{}," prefix enforces that the cookie is ",[867,3340,971],{}," (HTTPS only). The ",[867,3343,2784],{}," cookie lets the BFF compare the current token's issued-at time against a cached value to detect when the access token has changed (for example, after another tab triggers a rotation).",[944,3346],{},[947,3348,3350],{"id":3349},"what-the-bff-proxy-expects-from-the-iam-service","What the BFF proxy expects from the IAM service",[856,3352,3353],{},"The BFF proxy needs specific response patterns from the IAM service to manage the session lifecycle. This table summarizes every IAM endpoint the BFF calls and what it expects in return.",[1206,3355,3356,3374],{},[1209,3357,3358],{},[1212,3359,3360,3363,3365,3368,3371],{},[1215,3361,3362],{},"IAM endpoint",[1215,3364,1217],{},[1215,3366,3367],{},"When the BFF calls it",[1215,3369,3370],{},"Expected success",[1215,3372,3373],{},"Expected errors",[1231,3375,3376,3408,3436,3462,3495,3522,3553],{},[1212,3377,3378,3382,3386,3389,3397],{},[1236,3379,3380],{},[867,3381,1245],{},[1236,3383,3384],{},[867,3385,1240],{},[1236,3387,3388],{},"On every authenticated request to check authorization",[1236,3390,3391,3393,3394],{},[867,3392,2078],{}," with ",[867,3395,3396],{},"{ authorized, userId, roles, ... }",[1236,3398,3399,3401,3402,3404,3405,3407],{},[867,3400,2093],{}," MFA, ",[867,3403,1766],{}," unauthorized, ",[867,3406,2124],{}," not found",[1212,3409,3410,3414,3418,3421,3428],{},[1236,3411,3412],{},[867,3413,1280],{},[1236,3415,3416],{},[867,3417,1240],{},[1236,3419,3420],{},"On every authenticated request to check token freshness",[1236,3422,3423,3393,3425],{},[867,3424,2078],{},[867,3426,3427],{},"{ shouldRotate, msUntilExp, ... }",[1236,3429,3430,3401,3432,3404,3434,3407],{},[867,3431,2093],{},[867,3433,1766],{},[867,3435,2124],{},[1212,3437,3438,3442,3446,3449,3456],{},[1236,3439,3440],{},[867,3441,909],{},[1236,3443,3444],{},[867,3445,1240],{},[1236,3447,3448],{},"Once at startup",[1236,3450,3451,3393,3453],{},[867,3452,2078],{},[867,3454,3455],{},"{ domain, accessTokenTTL }",[1236,3457,3458,3461],{},[867,3459,3460],{},"403"," IP not trusted",[1212,3463,3464,3469,3474,3477,3486],{},[1236,3465,3466],{},[867,3467,3468],{},"\u002Flogin",[1236,3470,3471],{},[867,3472,3473],{},"POST",[1236,3475,3476],{},"When the user logs in",[1236,3478,3479,3482,3483,3485],{},[867,3480,3481],{},"201"," with access token in body + ",[867,3484,1027],{}," for refresh token",[1236,3487,3488,3490,3491,3494],{},[867,3489,1766],{}," invalid credentials, ",[867,3492,3493],{},"429"," rate limited",[1212,3496,3497,3502,3506,3509,3515],{},[1236,3498,3499],{},[867,3500,3501],{},"\u002Fsignup",[1236,3503,3504],{},[867,3505,3473],{},[1236,3507,3508],{},"When the user signs up",[1236,3510,3511,3482,3513,3485],{},[867,3512,3481],{},[867,3514,1027],{},[1236,3516,3517,3519,3520,3494],{},[867,3518,1788],{}," validation error, ",[867,3521,3493],{},[1212,3523,3524,3529,3533,3536,3544],{},[1236,3525,3526],{},[867,3527,3528],{},"\u002Fauth\u002Fuser\u002Frefresh-session",[1236,3530,3531],{},[867,3532,3473],{},[1236,3534,3535],{},"When tokens need rotation",[1236,3537,3538,3540,3541,3543],{},[867,3539,3481],{}," with new access token + ",[867,3542,1027],{}," for new refresh token",[1236,3545,3546,3401,3548,3550,3551,3494],{},[867,3547,2093],{},[867,3549,1766],{}," invalid refresh token, ",[867,3552,3493],{},[1212,3554,3555,3560,3564,3567,3574],{},[1236,3556,3557],{},[867,3558,3559],{},"\u002Fauth\u002Flogout",[1236,3561,3562],{},[867,3563,3473],{},[1236,3565,3566],{},"When the user logs out",[1236,3568,3569,3393,3571,3573],{},[867,3570,2078],{},[867,3572,1027],{}," clearing the refresh token",[1236,3575,3576,3578],{},[867,3577,1766],{}," unauthorized",[2147,3580,3581],{},[856,3582,3583,3584,3586,3587,3589],{},"When the IAM service returns ",[867,3585,2093],{}," on any endpoint, it means anomaly detection triggered an MFA challenge and a magic link email has been sent. The BFF must relay this response to the browser so the frontend can prompt the user to check their email. See ",[860,3588,123],{"href":124}," for the verification flow.",[944,3591],{},[947,3593,3595],{"id":3594},"configuration-reference-iam-side","Configuration reference (IAM side)",[856,3597,3598],{},"The following IAM service configuration fields directly affect BFF behavior. These are set in the IAM service's configuration file, not in the BFF proxy.",[925,3600,3602],{"id":3601},"trusted-proxy","Trusted proxy",[1999,3604,3605,3617,3629],{},[2002,3606,3608],{"name":3607,"type":1653},"service.clientIp",[856,3609,3610,3611,3613,3614,3616],{},"The IP address of the BFF server. Used by ",[867,3612,894],{}," to restrict access. Must match the BFF's outbound IP as seen by ",[867,3615,2815],{}," on the IAM service.",[2002,3618,3620],{"name":3619,"type":1653},"service.proxy.ipToTrust",[856,3621,3622,3623,3625,3626,3628],{},"Fallback IP when ",[867,3624,3607],{}," is not set. Also used for proxy trust in ",[867,3627,2028],{}," processing.",[2002,3630,3632],{"name":3631,"type":2013},"service.proxy.trust",[856,3633,3634,3635,3637,3638,1334,3640,3643],{},"When ",[867,3636,2019],{},", the IAM service trusts ",[867,3639,2028],{},[867,3641,3642],{},"X-Real-IP"," headers. This must be enabled when the BFF forwards the original client IP so that rate limiting and anomaly detection target the real browser, not the proxy.",[925,3645,3647],{"id":3646},"hmac-verification","HMAC verification",[1999,3649,3650,3658,3668],{},[2002,3651,3653],{"name":3652,"type":1653},"service.Hmac.sharedSecret",[856,3654,3655,3656,1076],{},"The shared secret for HMAC-SHA256 verification. Must be identical to the BFF proxy's signing secret. See ",[860,3657,135],{"href":136},[2002,3659,3661],{"name":3660,"type":1653},"service.Hmac.clientId",[856,3662,3663,3664,3667],{},"The expected client identifier in the ",[867,3665,3666],{},"X-Client-Id"," header. Must match the BFF proxy's configured client ID.",[2002,3669,3672],{"name":3670,"type":2004,"default":3671},"service.Hmac.maxClockSkew","300000",[856,3673,3674],{},"Maximum allowed clock difference in milliseconds between the BFF and the IAM service. Requests with timestamps outside this window are rejected. Default is 5 minutes.",[925,3676,3678],{"id":3677},"token-settings","Token settings",[1999,3680,3681,3699,3710,3719],{},[2002,3682,3685],{"name":3683,"type":2004,"default":3684},"jwt.access_tokens.expiresInMs","900000",[856,3686,3687,3688,2614,3690,3692,3693,3695,3696,3698],{},"Access token lifetime in milliseconds. Controls how long until ",[867,3689,2509],{},[867,3691,2019],{}," on the metadata endpoint. Also returned by ",[867,3694,894],{}," as ",[867,3697,2884],{},". Default is 15 minutes.",[2002,3700,3702],{"name":3701,"type":1653},"jwt.refresh_tokens.domain",[856,3703,3704,3705,3695,3707,3709],{},"The cookie domain for refresh token cookies. Returned by ",[867,3706,894],{},[867,3708,2864],{},". The BFF uses this when setting cookies on the browser.",[2002,3711,3713],{"name":3712,"type":2004},"jwt.refresh_tokens.maxAllowedSessionsPerUser",[856,3714,3715,3716,3718],{},"Maximum concurrent sessions per user. When this limit is exceeded, the ",[867,3717,1261],{}," anomaly detection triggers an MFA challenge on BFF routes.",[2002,3720,3722],{"name":3721,"type":2004},"jwt.refresh_tokens.byPassAnomaliesFor",[856,3723,3724,3725,1076],{},"Time in milliseconds after a successful MFA verification during which anomaly checks are skipped. See ",[860,3726,1613],{"href":1612},[944,3728],{},[947,3730,3732],{"id":3731},"using-auth-h3client-as-the-bff-proxy","Using auth-h3client as the BFF proxy",[856,3734,3735,3737,3738,3743],{},[860,3736,919],{"href":22}," is the companion Nuxt module that implements the BFF proxy side of the pattern for ",[860,3739,3742],{"href":3740,"rel":3741},"https:\u002F\u002Fh3.unjs.io\u002F",[864],"H3","-based frameworks. When installed, it consumes every IAM endpoint listed above and provides:",[875,3745,3746,3756,3764,3770,3776,3786,3792,3798,3804,3810,3816],{},[878,3747,3748,3751,3752,1334,3754,1076],{},[1439,3749,3750],{},"Automatic token rotation"," using ",[867,3753,888],{},[867,3755,1023],{},[878,3757,3758,3761,3762,1076],{},[1439,3759,3760],{},"Cookie management"," using domain and TTL values from ",[867,3763,894],{},[878,3765,3766,3769],{},[1439,3767,3768],{},"HMAC signing"," on every outbound request to the IAM service.",[878,3771,3772,3775],{},[1439,3773,3774],{},"mTLS support"," with optional client certificate authentication between the BFF and the IAM service.",[878,3777,3778,3781,3782,3785],{},[1439,3779,3780],{},"OAuth 2.0 and OpenID Connect client"," with pre-configured providers (Google, GitHub, X\u002FTwitter, LinkedIn) and support for custom providers. Includes OIDC auto-discovery, PKCE, JWKS-based ID token verification, ",[867,3783,3784],{},"at_hash"," validation, per-provider scopes, custom email extraction callbacks, and configurable redirect URLs.",[878,3787,3788,3791],{},[1439,3789,3790],{},"Magic link handling"," for password reset, email change, and MFA verification flows with configurable redirect paths and a bounce page system.",[878,3793,3794,3797],{},[1439,3795,3796],{},"Client composables"," for browser-side auth state.",[878,3799,3800,3803],{},[1439,3801,3802],{},"Global middleware"," for IP validation, bot detection, CSRF cookie generation, and visitor fingerprint validation via signed canary cookies.",[878,3805,3806,3809],{},[1439,3807,3808],{},"Server-side route handler"," that wraps token rotation, authorization, and MFA relay into a single function.",[878,3811,3812,3815],{},[1439,3813,3814],{},"Input sanitization"," with configurable HTML sanitizer iterations, filename sanitization, and image file type\u002Fsize validation.",[878,3817,3818,3821],{},[1439,3819,3820],{},"Structured logging"," via Pino with automatic secret redaction, configurable log levels, and per-request ID tracking.",[856,3823,3824],{},"The module auto-imports both client-side and server-side utilities, registers global middleware, and mounts authentication routes and OAuth routes with built-in CSRF enforcement.",[856,3826,3827,3828,1076],{},"For installation, configuration, composables API, and handler reference, see the ",[860,3829,935],{"href":22},[944,3831],{},[947,3833,3835],{"id":3834},"rate-limiting-on-bff-routes","Rate limiting on BFF routes",[856,3837,3838],{},"The IAM service enforces rate limits on the middleware that protects BFF routes. These limits target the access token and refresh token validation layers, not the BFF controllers directly.",[1206,3840,3841,3860],{},[1209,3842,3843],{},[1212,3844,3845,3848,3851,3854,3857],{},[1215,3846,3847],{},"Limiter",[1215,3849,3850],{},"Points",[1215,3852,3853],{},"Window",[1215,3855,3856],{},"Block duration",[1215,3858,3859],{},"Scope",[1231,3861,3862,3879,3895,3908,3922],{},[1212,3863,3864,3867,3870,3873,3876],{},[1236,3865,3866],{},"Access token brute force",[1236,3868,3869],{},"2",[1236,3871,3872],{},"1 second",[1236,3874,3875],{},"30 minutes",[1236,3877,3878],{},"Per IP",[1212,3880,3881,3884,3887,3890,3893],{},[1236,3882,3883],{},"Access token slow",[1236,3885,3886],{},"3",[1236,3888,3889],{},"10 minutes",[1236,3891,3892],{},"1 hour",[1236,3894,3878],{},[1212,3896,3897,3900,3902,3904,3906],{},[1236,3898,3899],{},"Refresh token brute force",[1236,3901,3869],{},[1236,3903,3872],{},[1236,3905,3875],{},[1236,3907,3878],{},[1212,3909,3910,3913,3915,3918,3920],{},[1236,3911,3912],{},"Refresh token slow",[1236,3914,957],{},[1236,3916,3917],{},"12 hours",[1236,3919,3917],{},[1236,3921,3878],{},[1212,3923,3924,3927,3930,3933,3936],{},[1236,3925,3926],{},"Blacklist (JTI revocation)",[1236,3928,3929],{},"20",[1236,3931,3932],{},"24 hours",[1236,3934,3935],{},"72 hours",[1236,3937,3938],{},"Per token ID",[856,3940,3941,3942,3945],{},"When a rate limit is hit, the IAM service responds with HTTP 429 and a ",[867,3943,3944],{},"Retry-After"," header. The BFF proxy should forward this header and status code to the browser.",[944,3947],{},[947,3949,3951],{"id":3950},"security-layers","Security layers",[856,3953,3954,3955,1505,3957,1874],{},"Requests to BFF routes pass through the full IAM security stack. This is the complete order of operations for a request reaching ",[867,3956,882],{},[867,3958,888],{},[955,3960,3961,3964,3974,3978,4001,4005,4015,4019,4040,4044,4052,4056,4064,4067,4072,4076,4084,4088,4093,4097,4102,4105],{"level":957},[959,3962,2809],{"id":3963},"ip-validation-1",[856,3965,3966,3967,3970,3971,3973],{},"The global ",[867,3968,3969],{},"validateIp"," middleware verifies that ",[867,3972,2842],{}," is a valid IP address. Malformed or missing IPs receive HTTP 403.",[959,3975,3977],{"id":3976},"security-headers","Security headers",[856,3979,1656,3980,3983,3984,968,3987,968,3990,968,3993,3996,3997,4000],{},[867,3981,3982],{},"helmet"," middleware sets ",[867,3985,3986],{},"Content-Security-Policy",[867,3988,3989],{},"X-Frame-Options: DENY",[867,3991,3992],{},"Cross-Origin-Embedder-Policy",[867,3994,3995],{},"Referrer-Policy: origin",", and cache directives (",[867,3998,3999],{},"no-cache, private",").",[959,4002,4004],{"id":4003},"hmac-authentication-optional","HMAC authentication (optional)",[856,4006,3634,4007,4009,4010,4012,4013,1076],{},[867,4008,3135],{}," is configured, ",[867,4011,3139],{}," validates the four HMAC headers. This runs before body parsing and cookie parsing. See ",[860,4014,135],{"href":136},[959,4016,4018],{"id":4017},"http-logging","HTTP logging",[856,4020,1656,4021,4024,4025,4028,4029,4032,4033,4036,4037,4039],{},[867,4022,4023],{},"pino-http"," middleware generates a request ID (from ",[867,4026,4027],{},"X-Request-ID"," or a UUID), sets the ",[867,4030,4031],{},"X-Request-Id"," response header, and logs the request with redacted ",[867,4034,4035],{},"authorization"," headers and ",[867,4038,985],{}," cookie values.",[959,4041,4043],{"id":4042},"access-token-extraction","Access token extraction",[856,4045,4046,4048,4049,4051],{},[867,4047,1250],{}," extracts the Bearer token from the ",[867,4050,1348],{}," header.",[959,4053,4055],{"id":4054},"refresh-token-check","Refresh token check",[856,4057,4058,4060,4061,4063],{},[867,4059,1253],{}," verifies the ",[867,4062,985],{}," cookie is present.",[959,4065,127],{"id":4066},"fingerprinting",[856,4068,4069,4071],{},[867,4070,1256],{}," collects device, browser, geolocation, and network metadata\nfrom the request headers and resolved IP address.",[959,4073,4075],{"id":4074},"active-mfa-short-circuit","Active MFA short-circuit",[856,4077,4078,4080,4081,4083],{},[867,4079,905],{}," stops requests that already have an unresolved MFA state\ncached for the session before ",[867,4082,1261],{}," runs again.",[959,4085,4087],{"id":4086},"jwt-verification-and-anomaly-detection","JWT verification and anomaly detection",[856,4089,4090,4092],{},[867,4091,1261],{}," verifies the JWT and runs the full anomaly detection suite. This is where MFA challenges originate.",[959,4094,4096],{"id":4095},"cookie-only-guard-metadata-route","Cookie-only guard (metadata route)",[856,4098,4099,4101],{},[867,4100,1295],{}," enforces a strict no-body, no-query, no-content-type contract on the metadata endpoint.",[959,4103,1226],{"id":4104},"controller",[856,4106,1656,4107,1505,4109,4111,4112,4114],{},[867,4108,1266],{},[867,4110,1300],{}," controller runs with ",[867,4113,1623],{}," fully populated.",[944,4116],{},[947,4118,4120],{"id":4119},"summary","Summary",[1206,4122,4123,4133],{},[1209,4124,4125],{},[1212,4126,4127,4130],{},[1215,4128,4129],{},"System",[1215,4131,4132],{},"Integration point",[1231,4134,4135,4155,4175,4198,4212,4225,4237],{},[1212,4136,4137,4142],{},[1236,4138,4139],{},[860,4140,4141],{"href":88},"Access tokens",[1236,4143,4144,4145,4147,4148,4151,4152,4154],{},"The metadata endpoint returns ",[867,4146,2509],{}," based on ",[860,4149,4150],{"href":88},"access token"," TTL and the 25% threshold. The BFF stores access tokens as ",[867,4153,869],{}," cookies and rotates them proactively",[1212,4156,4157,4162],{},[1236,4158,4159],{},[860,4160,4161],{"href":92},"Refresh tokens",[1236,4163,4164,4165,4167,4168,4170,4171],{},"Both BFF routes require a valid ",[867,4166,985],{}," cookie. The BFF forwards it to ",[867,4169,1023],{}," for ",[860,4172,4174],{"href":4173},"\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens#consuming-a-token","rotation",[1212,4176,4177,4182],{},[1236,4178,4179],{},[860,4180,4181],{"href":96},"Anomaly detection",[1236,4183,898,4184,1334,4186,4188,4189,4191,4192,4194,4195,4197],{},[867,4185,1245],{},[867,4187,1280],{}," run through ",[867,4190,1261],{},", which calls ",[867,4193,1457],{}," for the full ",[860,4196,1012],{"href":96}," suite",[1212,4199,4200,4204],{},[1236,4201,4202],{},[860,4203,127],{"href":128},[1236,4205,1656,4206,4208,4209,4211],{},[867,4207,1256],{}," middleware runs on both BFF routes and builds the server-side fingerprint from the request ",[867,4210,1387],{},", resolved IP, and geolocation data",[1212,4213,4214,4219],{},[1236,4215,4216],{},[860,4217,4218],{"href":136},"HMAC authentication",[1236,4220,4221,4222,4224],{},"When configured, ",[860,4223,913],{"href":136}," validation runs before all BFF routes, authenticating the proxy as a trusted service",[1212,4226,4227,4231],{},[1236,4228,4229],{},[860,4230,123],{"href":124},[1236,4232,4233,4234,4236],{},"Anomaly detection on BFF routes can return HTTP 202, triggering an ",[860,4235,123],{"href":124}," challenge that the BFF relays to the browser",[1212,4238,4239,4243],{},[1236,4240,4241],{},[860,4242,919],{"href":22},[1236,4244,4245],{},"The companion Nuxt module that implements the full BFF proxy, consuming every IAM endpoint documented above",[4247,4248,4249],"style",{},"html pre.shiki code .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}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 .sFkSl, html code.shiki .sFkSl{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#E9F284}html pre.shiki code .sFB1V, html code.shiki .sFB1V{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#F1FA8C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .saJyd, html code.shiki .saJyd{--shiki-light:#0451A5;--shiki-default:#0451A5;--shiki-dark:#8BE9FE}html pre.shiki code .s_W10, html code.shiki .s_W10{--shiki-light:#0451A5;--shiki-default:#0451A5;--shiki-dark:#8BE9FD}html pre.shiki code .saOXh, html code.shiki .saOXh{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#FF79C6}html pre.shiki code .spgvN, html code.shiki .spgvN{--shiki-light:#098658;--shiki-default:#098658;--shiki-dark:#BD93F9}html pre.shiki code .sjR7W, html code.shiki .sjR7W{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#BD93F9}html pre.shiki code .sZ328, html code.shiki .sZ328{--shiki-light:#AF00DB;--shiki-default:#AF00DB;--shiki-dark:#FF79C6}",{"title":1054,"searchDepth":1068,"depth":1068,"links":4251},[4252,4253,4256,4265,4271,4278,4283,4284,4285,4286,4287,4292,4293,4294,4295],{"id":949,"depth":1068,"text":950},{"id":1036,"depth":1068,"text":1037,"children":4254},[4255],{"id":1203,"depth":1095,"text":1204},{"id":1328,"depth":1068,"text":1223,"children":4257},[4258,4259,4260,4261,4262,4263],{"id":1340,"depth":1095,"text":1250},{"id":1364,"depth":1095,"text":1253},{"id":1379,"depth":1095,"text":1256},{"id":1397,"depth":1095,"text":905},{"id":1427,"depth":1095,"text":1261},{"id":1728,"depth":1095,"text":4264},"acceptCookieOnly (metadata route only)",{"id":1832,"depth":1068,"text":4266,"children":4267},"Authorization check: GET \u002Fsecret\u002Fdata",[4268,4269,4270],{"id":1846,"depth":1095,"text":1847},{"id":2054,"depth":1095,"text":2055},{"id":2160,"depth":1095,"text":2161},{"id":2184,"depth":1068,"text":4272,"children":4273},"Token metadata: GET \u002Fsecret\u002Faccesstoken\u002Fmetadata",[4274,4275,4276,4277],{"id":2195,"depth":1095,"text":1847},{"id":2591,"depth":1095,"text":2592},{"id":2628,"depth":1095,"text":2055},{"id":2711,"depth":1095,"text":2712},{"id":2799,"depth":1068,"text":4279,"children":4280},"Operational configuration: GET \u002Foperational\u002Fconfig",[4281,4282],{"id":2808,"depth":1095,"text":2809},{"id":2848,"depth":1095,"text":1847},{"id":2934,"depth":1068,"text":2935},{"id":3128,"depth":1068,"text":3129},{"id":3171,"depth":1068,"text":3172},{"id":3349,"depth":1068,"text":3350},{"id":3594,"depth":1068,"text":3595,"children":4288},[4289,4290,4291],{"id":3601,"depth":1095,"text":3602},{"id":3646,"depth":1095,"text":3647},{"id":3677,"depth":1095,"text":3678},{"id":3731,"depth":1068,"text":3732},{"id":3834,"depth":1068,"text":3835},{"id":3950,"depth":1068,"text":3951},{"id":4119,"depth":1068,"text":4120},"How the IAM service exposes protected endpoints for BFF proxies, including authorization checks, token metadata with rotation hints, operational configuration, and the full middleware security chain that gates every BFF call.","md","i-lucide-layers",{},null,"---\ntitle: Backend for Frontend\ndescription: How the IAM service exposes protected endpoints for BFF proxies, including authorization checks, token metadata with rotation hints, operational configuration, and the full middleware security chain that gates every BFF call.\nicon: i-lucide-layers\n---\n\nThe IAM service supports the [Backend for Frontend](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fazure\u002Farchitecture\u002Fpatterns\u002Fbackends-for-frontends) or \"BFF\" pattern natively. Instead of exposing JWT tokens to the browser, a server-side proxy sits between the client and the IAM service. The proxy holds tokens in `httpOnly` cookies, rotates them transparently, and relays authorization decisions back to the frontend. The browser never sees a raw token.\n\nThe service provides three endpoints dedicated to BFF communication:\n\n- `GET \u002Fsecret\u002Fdata` answers \"is this user authorized?\" and returns the user's roles.\n- `GET \u002Fsecret\u002Faccesstoken\u002Fmetadata` answers \"how long until this token expires?\" and computes a proactive rotation hint.\n- `GET \u002Foperational\u002Fconfig` shares the cookie domain and access token TTL so the proxy can set cookies correctly.\n\nBoth `\u002Fsecret` routes run behind the full middleware chain: access token\nextraction, refresh token validation, device fingerprinting,\n`checkForActiveMfa`, and anomaly-aware JWT verification. The metadata route\nadds a strict cookie-only guard. The `\u002Foperational\u002Fconfig` endpoint is gated by\nIP address instead, and the service can also apply\n[HMAC](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac) globally.\n\n[auth-h3client](\u002Fdocs\u002Fauth-h3client) is the companion Nuxt module that implements the proxy side of the pattern. It consumes the three IAM endpoints above and handles login, signup, logout, OAuth, MFA relay, CSRF, token rotation, and user data caching automatically. it can also be used as [h3](https:\u002F\u002Fh3.dev\u002F) \u002F [nitro](https:\u002F\u002Fnitro.build\u002F) client directly. See the [auth-h3client documentation](\u002Fdocs\u002Fauth-h3client) for its full API reference, configuration, and composables.\n\n::tip\nYou can use any http client for this pattern\n::\n---\n\n## The pattern\n\nIn a traditional SPA, the browser stores access and refresh tokens in localStorage, sessionStorage, or cookies and sends them directly to resource APIs. This exposes tokens to XSS attacks, browser extensions, and third-party scripts. The BFF pattern eliminates this exposure.\n\n::steps{level=\"4\"}\n#### Browser sends a request to the BFF\n\nThe browser makes a request to the application server. The only credentials the browser carries are `httpOnly`, `Secure`, `SameSite=Strict` cookies that it cannot read or modify via JavaScript.\n\n#### BFF extracts cookies and calls the IAM service\n\nThe BFF reads the `session` (refresh token), `__Secure-a` (access token), and `canary_id` (session binding) cookies from the incoming request. It forwards them to the IAM service as part of a server-to-server call, adding [HMAC signature headers](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac) when configured.\n\n#### IAM validates and responds\n\nThe IAM service runs the full middleware chain on the forwarded request: access token verification, refresh token verification, [device fingerprinting](\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting), and [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies). It responds with the authorization decision, user roles, and, for the metadata route, token TTL information.\n\n#### BFF relays the decision\n\nThe BFF interprets the IAM response. If the user is authorized, the BFF proceeds with the application logic. If tokens need rotation, the BFF calls `POST \u002Fauth\u002Fuser\u002Frefresh-session` and forwards the new `Set-Cookie` headers to the browser. If [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) is required, the BFF relays the challenge to the browser.\n::\n\n---\n\n## IAM service routes\n\nThe IAM service exposes two BFF-specific routes under the `\u002Fsecret` prefix and one configuration endpoint under `\u002Foperational`. These routes are mounted after the authentication, token rotation, and magic link route groups in the Express middleware chain.\n\n```ts\n\u002F\u002F service.ts (simplified mounting order)\napp.use(authenticationRoutes)     \u002F\u002F \u002Fsignup, \u002Flogin, \u002Fauth\u002FOAuth\u002F*\napp.use(tokenRotationRoutes)      \u002F\u002F \u002Fauth\u002Fuser\u002Frefresh-session, \u002Fauth\u002Flogout\napp.use(magicLinks)               \u002F\u002F \u002Fauth\u002Fverify-mfa\u002F*, \u002Fauth\u002Freset-password\u002F*\napp.use(allowBff)                 \u002F\u002F \u002Fsecret\u002Fdata, \u002Fsecret\u002Faccesstoken\u002Fmetadata\napp.use(apiProtectedRoutes())     \u002F\u002F \u002Fapi\u002Fmanage\u002F*\napp.use('\u002Foperational\u002Fconfig', sendOperationalConfig)\n```\n\n### Route table\n\n| Method | Path | Middleware chain | Controller | Purpose |\n|---|---|---|---|---|\n| `GET` | `\u002Fsecret\u002Fdata` | `requireAccessToken`, `requireRefreshToken`, `getFingerPrint`, `checkForActiveMfa`, `protectRoute` | `allowBffAccess` | Authorization check |\n| `GET` | `\u002Fsecret\u002Faccesstoken\u002Fmetadata` | `requireAccessToken`, `requireRefreshToken`, `getFingerPrint`, `checkForActiveMfa`, `protectRoute`, `acceptCookieOnly` | `getAccessTokenPayload` | Token TTL and rotation hint |\n| `GET` | `\u002Foperational\u002Fconfig` | IP restriction (no middleware chain) | `sendOperationalConfig` | Cookie domain and token TTL |\n\n---\n\n## Middleware chain\n\nEvery request to `\u002Fsecret\u002Fdata` and `\u002Fsecret\u002Faccesstoken\u002Fmetadata` passes\nthrough five shared middleware functions before reaching the controller. The\nmetadata route adds a sixth guard. Each middleware either populates request\ncontext for downstream handlers or terminates the request with an error.\n\n### `requireAccessToken`\n\nExtracts the JWT access token from the `Authorization` header. The header must use the `Bearer` scheme. If the header is missing, empty, or uses a different scheme, the middleware responds with HTTP 401 and `{ ok: false, error: \"Missing Bearer token\" }`. On success, it stores the raw token string on `req.token` for the next middleware to verify.\n\n### `requireRefreshToken`\n\nReads the `session` cookie from the request. If the cookie is absent, the middleware responds with HTTP 401 and `{ error: \"Refresh token missing\" }`. It does not validate the token's contents or hash; that responsibility belongs to rotation endpoints. Its purpose here is to confirm that the caller holds a session cookie, which proves the request comes from a context where the user has logged in.\n\n### `getFingerPrint`\n\nExtracts device and network metadata from the request headers and attaches a\nfingerprint to the request context. This includes the raw `User-Agent`, the\nresolved IP address, geolocation data, and parsed browser and device details.\nThe fingerprint is consumed by `protectRoute` for anomaly scoring. See\n[Fingerprinting](\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting) for the full list of\nsignals collected.\n\n### `checkForActiveMfa`\n\nHashes the `session` cookie and checks `anomaliesCache()` for an unresolved MFA\nstate. If the current session already has a resolvable MFA challenge, the\nmiddleware returns `202 { mfa: true }`. If the cached anomaly is not resolvable,\nit returns `401 { error: 'Re-login is required', ... }`. Otherwise it calls\n`next()` and lets `protectRoute` continue.\n\n### `protectRoute`\n\nThe core security layer. It performs two operations:\n\n1. **JWT verification.** Calls `verifyAccessToken` with the token from `req.token`. The verification uses a cache-backed lookup: the SHA-256 hash of the token is checked against stored access token records. If the token is revoked, expired, or not found, the middleware returns HTTP 401.\n\n2. **Anomaly detection.** Calls `strangeThings()` with the verified token data and the fingerprint from `getFingerPrint`. This function runs a series of checks:\n\n   | Check | Trigger | Result |\n   |---|---|---|\n   | Token not found | SHA-256 hash does not match any stored record | HTTP 401, invalid token |\n   | Token revoked | Token's `valid` flag is `false` or `usage_count > 1` | HTTP 401, invalid token |\n   | Canary mismatch | `canary_id` cookie does not match the session record | HTTP 401 + [MFA challenge](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n   | Idle session | User's `last_seen` exceeds 24 hours | HTTP 401 + MFA challenge |\n   | Session overflow | Active sessions exceed `maxAllowedSessionsPerUser` | HTTP 401 + MFA challenge |\n   | Token spam | 3 or more tokens issued within 10 minutes | Revoke all tokens, HTTP 401 |\n   | IP range mismatch | Client IP falls outside the known range for this session | HTTP 401 + MFA challenge |\n   | High suspicion score | Suspicion score reaches 25% of the ban threshold | HTTP 401 + MFA challenge |\n   | Proxy\u002Fhosting detected | IP identified as proxy or hosting and user is not allowlisted | HTTP 401 + MFA challenge |\n   | Device fingerprint mismatch | Browser, OS, or location differs from the session baseline | HTTP 401 + MFA challenge |\n\n   When an anomaly triggers an MFA challenge, the IAM service responds with HTTP 202 and sends a magic link email. The BFF is expected to relay this 202 to the browser so the user can verify their identity. Anomalies that trigger within the [MFA bypass window](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#bypass-window) are skipped.\n\nOn success, `protectRoute` populates `req.user` with:\n\n| Field | Type | Description |\n|---|---|---|\n| `userId` | `string` | The `sub` claim from the JWT |\n| `visitor_id` | `string` | The `visitor` claim from the JWT |\n| `accessTokenId` | `string` | The `jti` claim (JWT ID) |\n| `roles` | `string[]` | The `roles` claim from the JWT (may be undefined) |\n| `payload` | `JwtPayload` | The full decoded JWT payload |\n\n### `acceptCookieOnly` (metadata route only)\n\nThe metadata route adds an extra guard after `protectRoute`. This middleware enforces a strict contract: the request must carry cookies and nothing else. It rejects the request under any of these conditions:\n\n| Condition | Status | Response |\n|---|---|---|\n| `session` cookie is missing | `401` | `{ error: \"Refresh token missing\" }` |\n| Request has a body (parsed JSON, `content-length > 0`, or `transfer-encoding: chunked`) | `400` | `{ error: \"Request body not allowed\" }` |\n| Query string is present | `400` | `{ error: \"Query string not allowed\" }` |\n| `Content-Type` header is set | `400` | `{ error: \"Content-Type not allowed\" }` |\n\nThis prevents injection vectors on an endpoint whose only input should be the cookies attached by the browser. No body, query parameters, or content-type declarations are legitimate for this route.\n\n---\n\n## Authorization check: `GET \u002Fsecret\u002Fdata`\n\nThe `allowBffAccess` controller validates that the upstream middleware successfully populated `req.user`, then confirms that the user and their visitor record exist in the database. If everything checks out, it returns a JSON payload with the authorization decision.\n\n### Response shape\n\n```json\n{\n  \"userId\": 42,\n  \"authorized\": true,\n  \"ipAddress\": \"203.0.113.10\",\n  \"userAgent\": \"Mozilla\u002F5.0 ...\",\n  \"date\": \"2025-01-15T10:30:00.000Z\",\n  \"roles\": [\"admin\", \"editor\"]\n}\n```\n\n::field-group\n::field{name=\"userId\" type=\"number\"}\nThe authenticated user's database ID, extracted from the JWT `sub` claim.\n::\n\n::field{name=\"authorized\" type=\"boolean\"}\nAlways `true` in a successful response. The BFF proxy uses this flag to decide whether to proceed with the application request.\n::\n\n::field{name=\"ipAddress\" type=\"string\"}\nThe client IP address as seen by the IAM service. When the BFF forwards `X-Forwarded-For`, this reflects the original browser IP.\n::\n\n::field{name=\"userAgent\" type=\"string\"}\nThe `User-Agent` header value from the request.\n::\n\n::field{name=\"date\" type=\"string\"}\nISO 8601 timestamp of when the response was generated.\n::\n\n::field{name=\"roles\" type=\"string[] | string\"}\nThe roles embedded in the access token. Returns the array of role strings when roles are present, or `\"No roles added with this token.\"` when the token carries no roles.\n::\n::\n\n### Error responses\n\n| Status | Body | Cause |\n|---|---|---|\n| `200` | `{ authorized: true, ... }` | User and visitor exist, middleware chain passed |\n| `202` | MFA challenge (from `protectRoute`) | Anomaly detected, magic link sent |\n| `401` | `{ authorized: false, reason: \"Not authenticated\" }` | `req.user` was not populated by upstream middleware |\n| `404` | `{ authorized: false, reason: \"Not found\" }` | User or visitor record does not exist in the database |\n| `500` | Logged internally | Database query failure. The controller catches the error and logs it without exposing details |\n\n::note\nThe controller trusts that `protectRoute` has already verified the JWT, run anomaly detection, and populated `req.user`. It does not re-validate the token. The database lookups serve as a final consistency check: the user and visitor must still exist at query time.\n::\n\n### Observability\n\nEvery request to this endpoint produces a structured log entry with the client IP, request ID, user agent, original URL, and `canary_id` cookie value. Successful authorizations log at `info` level. Failed attempts log at `warn` level with the specific reason. Database errors log at `error` level without exposing the error details in the response.\n\n---\n\n## Token metadata: `GET \u002Fsecret\u002Faccesstoken\u002Fmetadata`\n\nThe `getAccessTokenPayload` controller returns the decoded JWT payload along with computed TTL information. The BFF proxy uses this data to decide whether to proactively rotate the access token before it expires.\n\n### Response shape\n\n```json\n{\n  \"authorized\": true,\n  \"ipAddress\": \"203.0.113.10\",\n  \"userAgent\": \"Mozilla\u002F5.0 ...\",\n  \"date\": \"2025-01-15T10:30:00.000Z\",\n  \"roles\": [\"admin\"],\n  \"payload\": {\n    \"sub\": \"42\",\n    \"visitor\": \"v_abc123\",\n    \"jti\": \"tok_xyz789\",\n    \"roles\": [\"admin\"],\n    \"iat\": 1736934600,\n    \"exp\": 1736935500,\n    \"aud\": \"example.com\",\n    \"iss\": \"example.com\"\n  },\n  \"msUntilExp\": 540000,\n  \"refreshThreshold\": 225000,\n  \"shouldRotate\": false\n}\n```\n\n::field-group\n::field{name=\"authorized\" type=\"boolean\"}\nAlways `true` in a successful response.\n::\n\n::field{name=\"payload\" type=\"JwtPayload\"}\nThe full decoded JWT payload as attached by `protectRoute`. Contains `sub` (user ID), `visitor` (visitor ID), `jti` (token ID), `roles`, `iat`, `exp`, `aud`, and `iss`.\n::\n\n::field{name=\"msUntilExp\" type=\"number\"}\nMilliseconds remaining until the access token expires. Computed as `Math.max(0, expMs - Date.now())`. Returns `0` when the token has already expired.\n::\n\n::field{name=\"refreshThreshold\" type=\"number\"}\nThe threshold in milliseconds below which the BFF should trigger a rotation. Computed as 25% of the configured access token TTL.\n::\n\n::field{name=\"shouldRotate\" type=\"boolean\"}\n`true` when `msUntilExp \u003C= refreshThreshold`. The BFF uses this flag to trigger proactive rotation by calling `POST \u002Fauth\u002Fuser\u002Frefresh-session`.\n::\n::\n\n### TTL computation\n\nThe controller computes timing values from the JWT's `iat` and `exp` claims combined with the configured access token TTL:\n\n```\nTTL_MS     = config.jwt.access_tokens.expiresInMs ?? 900000   (default: 15 minutes)\nTHRESHOLD  = TTL_MS * 0.25                                    (25% of TTL)\niatMs      = payload.iat * 1000                               (or Date.now() if missing)\nexpMs      = payload.exp * 1000                               (or iatMs + TTL_MS if missing)\nmsUntilExp = max(0, expMs - now)\nshouldRotate = msUntilExp \u003C= THRESHOLD\n```\n\nWhen a 15-minute access token has less than 3 minutes and 45 seconds remaining, `shouldRotate` becomes `true`. The BFF can then call `POST \u002Fauth\u002Fuser\u002Frefresh-session` to obtain fresh tokens before the current ones expire.\n\n::tip\nThe 25% threshold is not configurable. It provides a balance between minimizing unnecessary rotations and ensuring the token is refreshed well before expiry. A 15-minute TTL triggers rotation in the last 3:45. A 5-minute TTL triggers rotation in the last 1:15.\n::\n\n### Error responses\n\n| Status | Body | Cause |\n|---|---|---|\n| `200` | `{ authorized: true, payload, msUntilExp, ... }` | Token valid, metadata computed |\n| `202` | MFA challenge (from `protectRoute`) | Anomaly detected, magic link sent |\n| `401` | `{ authorized: false, reason: \"Not authenticated\" }` | `req.user` was not populated by upstream middleware |\n| `404` | `{ authorized: false, reason: \"Not found\" }` | User or visitor record does not exist in the database |\n| `500` | `{ authorized: false, reason: \"Server error\" }` | Database query failure |\n\n### How the BFF uses this endpoint\n\nA typical BFF proxy calls this endpoint on every authenticated request to determine whether the access token is still usable or needs rotation:\n\n::steps{level=\"4\"}\n#### Call the metadata endpoint\n\nThe BFF sends `GET \u002Fsecret\u002Faccesstoken\u002Fmetadata` with the access token as a `Bearer` header and the `session` and `canary_id` cookies.\n\n#### Read the `shouldRotate` flag\n\nIf `shouldRotate` is `false`, the access token is still fresh. The BFF proceeds with the application request using the current token.\n\n#### Trigger rotation when needed\n\nIf `shouldRotate` is `true`, the BFF calls `POST \u002Fauth\u002Fuser\u002Frefresh-session` on the IAM service, forwarding the `session` and `canary_id` cookies. The IAM service validates the refresh token, generates new tokens, and returns them.\n\n#### Forward new cookies\n\nAfter rotation, the BFF sets the new `__Secure-a`, `session`, and `a-iat` cookies on the browser response. The browser transparently picks up the refreshed tokens on the next request.\n::\n\n::note\nThe BFF can cache the metadata response to avoid calling this endpoint on every single request. A safe cache TTL is `msUntilExp - refreshThreshold - 5000` (5-second safety margin for network latency). This ensures the cache entry expires just before the rotation window opens.\n::\n\n---\n\n## Operational configuration: `GET \u002Foperational\u002Fconfig`\n\nThe IAM service exposes a non-authenticated endpoint that returns runtime settings the BFF needs for cookie management. This endpoint does not use the middleware chain. Instead, it is gated by IP address: only requests from the configured trusted client IP receive a response.\n\n### IP validation\n\nThe controller reads the raw socket address via `req.socket.remoteAddress`, strips the IPv4-mapped IPv6 prefix (`::ffff:`) if present, and compares the result against `config.service.clientIp`. If that field is not set, it falls back to `config.service.proxy.ipToTrust`. Any mismatch returns HTTP 403 with `{ error: \"Forbidden\" }`.\n\n::note\nThe comparison uses `req.socket.remoteAddress` (the physical TCP connection address), not `req.ip` or `X-Forwarded-For`. This means the IP restriction cannot be bypassed by spoofing forwarded headers. The BFF must connect directly from the trusted IP.\n::\n\n### Response shape\n\n```json\n{\n  \"domain\": \".example.com\",\n  \"accessTokenTTL\": 900000\n}\n```\n\n::field-group\n::field{name=\"domain\" type=\"string\"}\nThe cookie domain from `config.jwt.refresh_tokens.domain`. The BFF uses this value as the `Domain` attribute when setting the refresh token cookie on the browser.\n::\n\n::field{name=\"accessTokenTTL\" type=\"number\"}\nThe access token lifetime in milliseconds from `config.jwt.access_tokens.expiresInMs` (defaults to 900000, which is 15 minutes). The BFF uses this as the `maxAge` for the `__Secure-a` cookie.\n::\n::\n\nThe BFF typically calls this endpoint once at startup and caches the result. Since these values change only when the IAM service configuration changes, a 24-hour cache TTL is reasonable.\n\n---\n\n## Exports for library consumers\n\nThe BFF routes and controllers are exported individually from the `@riavzon\u002Fauth` package. This allows library consumers to mount them in their own Express applications, compose them with additional middleware, or use the controllers standalone.\n\n```ts\nimport {\n  bffAccessRoute,\n  allowBffAccess,\n  getAccessTokenPayload,\n  sendOperationalConfig\n} from '@riavzon\u002Fauth'\n```\n\n| Export | Type | Description |\n|---|---|---|\n| `bffAccessRoute` | `Router` | The full router with both `\u002Fsecret\u002Fdata` and `\u002Fsecret\u002Faccesstoken\u002Fmetadata` routes, including all middleware |\n| `allowBffAccess` | `RequestHandler` | The standalone authorization controller. Can be mounted with custom middleware chains |\n| `getAccessTokenPayload` | `RequestHandler` | The standalone metadata controller. Expects `req.user` to be populated by upstream middleware |\n| `sendOperationalConfig` | `RequestHandler` | The operational config controller. Handles its own IP validation internally |\n\nThe supporting middleware functions are also exported for building custom pipelines:\n\n```ts\nimport {\n  requireAccessToken,\n  requireRefreshToken,\n  protectRoute\n} from '@riavzon\u002Fauth'\n```\n\nWhen using the IAM service in standalone mode, all routes are automatically mounted. When using the library, you can mount them yourself with your own middleware composition. For example, you could add rate limiting or logging middleware before the controller without modifying the IAM source.\n\n---\n\n## HMAC integration\n\nWhen the IAM service has HMAC authentication enabled (`service.Hmac`), the `hmacAuth` middleware runs before all route groups, including the BFF routes. Every request to `\u002Fsecret\u002Fdata`, `\u002Fsecret\u002Faccesstoken\u002Fmetadata`, and `\u002Foperational\u002Fconfig` must carry valid HMAC headers in addition to the JWT and refresh token cookies.\n\nThe BFF proxy must sign each outbound request with the same `clientId` and `sharedSecret` configured on the IAM service. See [HMAC Authentication](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac) for the signature format, clock-skew tolerance, and replay protection details.\n\n::warning\nIf HMAC is enabled on the IAM service but the BFF does not sign its requests, all BFF calls will receive HTTP 401 with a specific HMAC failure reason (missing headers, unknown client, stale timestamp, or signature mismatch).\n::\n\n---\n\n## Cookie lifecycle\n\nThe BFF pattern relies on cookies that flow between the browser, the BFF proxy, and the IAM service. The IAM service sets refresh token cookies directly on authentication responses (`POST \u002Flogin`, `POST \u002Fsignup`, `POST \u002Fauth\u002Fuser\u002Frefresh-session`). The BFF proxy is responsible for setting the access token cookie on the browser and forwarding refresh token cookies in both directions.\n\n| Cookie | `httpOnly` | `Secure` | `SameSite` | TTL | Set by | Purpose |\n|---|---|---|---|---|---|---|\n| `__Secure-a` | Yes | Yes | `Strict` | Access token TTL | BFF proxy | Carries the JWT access token |\n| `session` | Yes | Yes | `Strict` | Refresh token TTL | IAM service | Carries the refresh token |\n| `canary_id` | Yes | Yes | `Lax` | 90 days | IAM service | Session binding for [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies#canary-id-lifecycle) |\n| `a-iat` | Yes | Yes | `Strict` | Access token TTL | BFF proxy | Stores the access token's `iat` (issued-at) timestamp |\n\nThe IAM service validates the `session` and `canary_id` cookies on every BFF request. The access token arrives as a `Bearer` header, not from a cookie, because the BFF extracts it from `__Secure-a` and places it in the `Authorization` header before calling the IAM.\n\n::tip\nThe `__Secure-a` prefix enforces that the cookie is `Secure` (HTTPS only). The `a-iat` cookie lets the BFF compare the current token's issued-at time against a cached value to detect when the access token has changed (for example, after another tab triggers a rotation).\n::\n\n---\n\n## What the BFF proxy expects from the IAM service\n\nThe BFF proxy needs specific response patterns from the IAM service to manage the session lifecycle. This table summarizes every IAM endpoint the BFF calls and what it expects in return.\n\n| IAM endpoint | Method | When the BFF calls it | Expected success | Expected errors |\n|---|---|---|---|---|\n| `\u002Fsecret\u002Fdata` | `GET` | On every authenticated request to check authorization | `200` with `{ authorized, userId, roles, ... }` | `202` MFA, `401` unauthorized, `404` not found |\n| `\u002Fsecret\u002Faccesstoken\u002Fmetadata` | `GET` | On every authenticated request to check token freshness | `200` with `{ shouldRotate, msUntilExp, ... }` | `202` MFA, `401` unauthorized, `404` not found |\n| `\u002Foperational\u002Fconfig` | `GET` | Once at startup | `200` with `{ domain, accessTokenTTL }` | `403` IP not trusted |\n| `\u002Flogin` | `POST` | When the user logs in | `201` with access token in body + `Set-Cookie` for refresh token | `401` invalid credentials, `429` rate limited |\n| `\u002Fsignup` | `POST` | When the user signs up | `201` with access token in body + `Set-Cookie` for refresh token | `400` validation error, `429` rate limited |\n| `\u002Fauth\u002Fuser\u002Frefresh-session` | `POST` | When tokens need rotation | `201` with new access token + `Set-Cookie` for new refresh token | `202` MFA, `401` invalid refresh token, `429` rate limited |\n| `\u002Fauth\u002Flogout` | `POST` | When the user logs out | `200` with `Set-Cookie` clearing the refresh token | `401` unauthorized |\n\n::note\nWhen the IAM service returns `202` on any endpoint, it means anomaly detection triggered an MFA challenge and a magic link email has been sent. The BFF must relay this response to the browser so the frontend can prompt the user to check their email. See [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) for the verification flow.\n::\n\n---\n\n## Configuration reference (IAM side)\n\nThe following IAM service configuration fields directly affect BFF behavior. These are set in the IAM service's configuration file, not in the BFF proxy.\n\n### Trusted proxy\n\n::field-group\n::field{name=\"service.clientIp\" type=\"string\"}\nThe IP address of the BFF server. Used by `GET \u002Foperational\u002Fconfig` to restrict access. Must match the BFF's outbound IP as seen by `req.socket.remoteAddress` on the IAM service.\n::\n\n::field{name=\"service.proxy.ipToTrust\" type=\"string\"}\nFallback IP when `service.clientIp` is not set. Also used for proxy trust in `X-Forwarded-For` processing.\n::\n\n::field{name=\"service.proxy.trust\" type=\"boolean\"}\nWhen `true`, the IAM service trusts `X-Forwarded-For` and `X-Real-IP` headers. This must be enabled when the BFF forwards the original client IP so that rate limiting and anomaly detection target the real browser, not the proxy.\n::\n::\n\n### HMAC verification\n\n::field-group\n::field{name=\"service.Hmac.sharedSecret\" type=\"string\"}\nThe shared secret for HMAC-SHA256 verification. Must be identical to the BFF proxy's signing secret. See [HMAC Authentication](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac).\n::\n\n::field{name=\"service.Hmac.clientId\" type=\"string\"}\nThe expected client identifier in the `X-Client-Id` header. Must match the BFF proxy's configured client ID.\n::\n\n::field{name=\"service.Hmac.maxClockSkew\" type=\"number\" default=\"300000\"}\nMaximum allowed clock difference in milliseconds between the BFF and the IAM service. Requests with timestamps outside this window are rejected. Default is 5 minutes.\n::\n::\n\n### Token settings\n\n::field-group\n::field{name=\"jwt.access_tokens.expiresInMs\" type=\"number\" default=\"900000\"}\nAccess token lifetime in milliseconds. Controls how long until `shouldRotate` becomes `true` on the metadata endpoint. Also returned by `GET \u002Foperational\u002Fconfig` as `accessTokenTTL`. Default is 15 minutes.\n::\n\n::field{name=\"jwt.refresh_tokens.domain\" type=\"string\"}\nThe cookie domain for refresh token cookies. Returned by `GET \u002Foperational\u002Fconfig` as `domain`. The BFF uses this when setting cookies on the browser.\n::\n\n::field{name=\"jwt.refresh_tokens.maxAllowedSessionsPerUser\" type=\"number\"}\nMaximum concurrent sessions per user. When this limit is exceeded, the `protectRoute` anomaly detection triggers an MFA challenge on BFF routes.\n::\n\n::field{name=\"jwt.refresh_tokens.byPassAnomaliesFor\" type=\"number\"}\nTime in milliseconds after a successful MFA verification during which anomaly checks are skipped. See [MFA bypass window](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#bypass-window).\n::\n::\n\n---\n\n## Using auth-h3client as the BFF proxy\n\n[auth-h3client](\u002Fdocs\u002Fauth-h3client) is the companion Nuxt module that implements the BFF proxy side of the pattern for [H3](https:\u002F\u002Fh3.unjs.io\u002F)-based frameworks. When installed, it consumes every IAM endpoint listed above and provides:\n\n- **Automatic token rotation** using `GET \u002Fsecret\u002Faccesstoken\u002Fmetadata` and `POST \u002Fauth\u002Fuser\u002Frefresh-session`.\n- **Cookie management** using domain and TTL values from `GET \u002Foperational\u002Fconfig`.\n- **HMAC signing** on every outbound request to the IAM service.\n- **mTLS support** with optional client certificate authentication between the BFF and the IAM service.\n- **OAuth 2.0 and OpenID Connect client** with pre-configured providers (Google, GitHub, X\u002FTwitter, LinkedIn) and support for custom providers. Includes OIDC auto-discovery, PKCE, JWKS-based ID token verification, `at_hash` validation, per-provider scopes, custom email extraction callbacks, and configurable redirect URLs.\n- **Magic link handling** for password reset, email change, and MFA verification flows with configurable redirect paths and a bounce page system.\n- **Client composables** for browser-side auth state.\n- **Global middleware** for IP validation, bot detection, CSRF cookie generation, and visitor fingerprint validation via signed canary cookies.\n- **Server-side route handler** that wraps token rotation, authorization, and MFA relay into a single function.\n- **Input sanitization** with configurable HTML sanitizer iterations, filename sanitization, and image file type\u002Fsize validation.\n- **Structured logging** via Pino with automatic secret redaction, configurable log levels, and per-request ID tracking.\n\nThe module auto-imports both client-side and server-side utilities, registers global middleware, and mounts authentication routes and OAuth routes with built-in CSRF enforcement.\n\nFor installation, configuration, composables API, and handler reference, see the [auth-h3client documentation](\u002Fdocs\u002Fauth-h3client).\n\n---\n\n## Rate limiting on BFF routes\n\nThe IAM service enforces rate limits on the middleware that protects BFF routes. These limits target the access token and refresh token validation layers, not the BFF controllers directly.\n\n| Limiter | Points | Window | Block duration | Scope |\n|---|---|---|---|---|\n| Access token brute force | 2 | 1 second | 30 minutes | Per IP |\n| Access token slow | 3 | 10 minutes | 1 hour | Per IP |\n| Refresh token brute force | 2 | 1 second | 30 minutes | Per IP |\n| Refresh token slow | 4 | 12 hours | 12 hours | Per IP |\n| Blacklist (JTI revocation) | 20 | 24 hours | 72 hours | Per token ID |\n\nWhen a rate limit is hit, the IAM service responds with HTTP 429 and a `Retry-After` header. The BFF proxy should forward this header and status code to the browser.\n\n---\n\n## Security layers\n\nRequests to BFF routes pass through the full IAM security stack. This is the complete order of operations for a request reaching `GET \u002Fsecret\u002Fdata` or `GET \u002Fsecret\u002Faccesstoken\u002Fmetadata`:\n\n::steps{level=\"4\"}\n#### IP validation\n\nThe global `validateIp` middleware verifies that `req.ip` is a valid IP address. Malformed or missing IPs receive HTTP 403.\n\n#### Security headers\n\nThe `helmet` middleware sets `Content-Security-Policy`, `X-Frame-Options: DENY`, `Cross-Origin-Embedder-Policy`, `Referrer-Policy: origin`, and cache directives (`no-cache, private`).\n\n#### HMAC authentication (optional)\n\nWhen `service.Hmac` is configured, `hmacAuth` validates the four HMAC headers. This runs before body parsing and cookie parsing. See [HMAC Authentication](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac).\n\n#### HTTP logging\n\nThe `pino-http` middleware generates a request ID (from `X-Request-ID` or a UUID), sets the `X-Request-Id` response header, and logs the request with redacted `authorization` headers and `session` cookie values.\n\n#### Access token extraction\n\n`requireAccessToken` extracts the Bearer token from the `Authorization` header.\n\n#### Refresh token check\n\n`requireRefreshToken` verifies the `session` cookie is present.\n\n#### Fingerprinting\n\n`getFingerPrint` collects device, browser, geolocation, and network metadata\nfrom the request headers and resolved IP address.\n\n#### Active MFA short-circuit\n\n`checkForActiveMfa` stops requests that already have an unresolved MFA state\ncached for the session before `protectRoute` runs again.\n\n#### JWT verification and anomaly detection\n\n`protectRoute` verifies the JWT and runs the full anomaly detection suite. This is where MFA challenges originate.\n\n#### Cookie-only guard (metadata route)\n\n`acceptCookieOnly` enforces a strict no-body, no-query, no-content-type contract on the metadata endpoint.\n\n#### Controller\n\nThe `allowBffAccess` or `getAccessTokenPayload` controller runs with `req.user` fully populated.\n::\n\n---\n\n## Summary\n\n| System | Integration point |\n|---|---|\n| [Access tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens) | The metadata endpoint returns `shouldRotate` based on [access token](\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens) TTL and the 25% threshold. The BFF stores access tokens as `httpOnly` cookies and rotates them proactively |\n| [Refresh tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens) | Both BFF routes require a valid `session` cookie. The BFF forwards it to `POST \u002Fauth\u002Fuser\u002Frefresh-session` for [rotation](\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens#consuming-a-token) |\n| [Anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies) | Both `\u002Fsecret\u002Fdata` and `\u002Fsecret\u002Faccesstoken\u002Fmetadata` run through `protectRoute`, which calls `strangeThings()` for the full [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies) suite |\n| [Fingerprinting](\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting) | The `getFingerPrint` middleware runs on both BFF routes and builds the server-side fingerprint from the request `User-Agent`, resolved IP, and geolocation data |\n| [HMAC authentication](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac) | When configured, [HMAC](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac) validation runs before all BFF routes, authenticating the proxy as a trusted service |\n| [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) | Anomaly detection on BFF routes can return HTTP 202, triggering an [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) challenge that the BFF relays to the browser |\n| [auth-h3client](\u002Fdocs\u002Fauth-h3client) | The companion Nuxt module that implements the full BFF proxy, consuming every IAM endpoint documented above |",{"title":131,"description":4296},"1eJTgrHkISI0DzP0RnitFMwGg0hg6o5tOZpCzUGJmqg",[4305,4306],{"title":127,"path":128,"stem":129,"children":-1},{"title":135,"path":136,"stem":137,"children":-1},{"id":851,"title":131,"body":4308,"description":4296,"extension":4297,"icon":4298,"meta":6921,"module":4300,"navigation":8,"path":132,"rawbody":4301,"seo":6922,"stem":133,"__hash__":4303},{"type":853,"value":4309,"toc":6879},[4310,4317,4319,4333,4343,4355,4359,4361,4363,4365,4407,4409,4411,4417,4527,4529,4623,4625,4627,4633,4637,4647,4651,4657,4661,4669,4673,4687,4691,4693,4823,4829,4911,4915,4919,4989,4991,4993,4997,5003,5005,5127,5163,5165,5239,5247,5249,5259,5261,5265,5269,5271,5563,5615,5617,5623,5628,5636,5640,5642,5718,5720,5722,5770,5776,5778,5782,5784,5786,5798,5808,5810,5852,5872,5874,5876,5878,5882,5926,5994,5996,6034,6036,6038,6040,6052,6060,6064,6066,6068,6076,6188,6200,6210,6212,6214,6216,6404,6412,6414,6416,6418,6420,6448,6450,6468,6470,6504,6506,6508,6515,6569,6571,6575,6577,6579,6581,6659,6663,6665,6667,6673,6775,6777,6779,6877],[856,4311,858,4312,865,4315,870],{},[860,4313,131],{"href":862,"rel":4314},[864],[867,4316,869],{},[856,4318,873],{},[875,4320,4321,4325,4329],{},[878,4322,4323,883],{},[867,4324,882],{},[878,4326,4327,889],{},[867,4328,888],{},[878,4330,4331,895],{},[867,4332,894],{},[856,4334,898,4335,902,4337,906,4339,910,4341,914],{},[867,4336,901],{},[867,4338,905],{},[867,4340,909],{},[860,4342,913],{"href":136},[856,4344,4345,920,4347,926,4350,932,4353,936],{},[860,4346,919],{"href":22},[860,4348,925],{"href":923,"rel":4349},[864],[860,4351,931],{"href":929,"rel":4352},[864],[860,4354,935],{"href":22},[938,4356,4357],{},[856,4358,942],{},[944,4360],{},[947,4362,950],{"id":949},[856,4364,953],{},[955,4366,4367,4369,4377,4379,4389,4391,4397,4399],{"level":957},[959,4368,962],{"id":961},[856,4370,965,4371,968,4373,968,4375,975],{},[867,4372,869],{},[867,4374,971],{},[867,4376,974],{},[959,4378,979],{"id":978},[856,4380,982,4381,986,4383,990,4385,994,4387,998],{},[867,4382,985],{},[867,4384,989],{},[867,4386,993],{},[860,4388,997],{"href":136},[959,4390,1002],{"id":1001},[856,4392,1005,4393,1009,4395,1013],{},[860,4394,1008],{"href":128},[860,4396,1012],{"href":96},[959,4398,1017],{"id":1016},[856,4400,1020,4401,1024,4403,1028,4405,1031],{},[867,4402,1023],{},[867,4404,1027],{},[860,4406,123],{"href":124},[944,4408],{},[947,4410,1037],{"id":1036},[856,4412,1040,4413,1043,4415,1047],{},[867,4414,901],{},[867,4416,1046],{},[1049,4418,4419],{"className":1051,"code":1052,"language":1053,"meta":1054,"style":1054},[867,4420,4421,4425,4441,4457,4473,4489,4505],{"__ignoreMap":1054},[1058,4422,4423],{"class":1060,"line":1061},[1058,4424,1065],{"class":1064},[1058,4426,4427,4429,4431,4433,4435,4437,4439],{"class":1060,"line":1068},[1058,4428,1072],{"class":1071},[1058,4430,1076],{"class":1075},[1058,4432,1080],{"class":1079},[1058,4434,1083],{"class":1075},[1058,4436,1086],{"class":1071},[1058,4438,1089],{"class":1075},[1058,4440,1092],{"class":1064},[1058,4442,4443,4445,4447,4449,4451,4453,4455],{"class":1060,"line":1095},[1058,4444,1072],{"class":1071},[1058,4446,1076],{"class":1075},[1058,4448,1080],{"class":1079},[1058,4450,1083],{"class":1075},[1058,4452,1106],{"class":1071},[1058,4454,1109],{"class":1075},[1058,4456,1112],{"class":1064},[1058,4458,4459,4461,4463,4465,4467,4469,4471],{"class":1060,"line":1115},[1058,4460,1072],{"class":1071},[1058,4462,1076],{"class":1075},[1058,4464,1080],{"class":1079},[1058,4466,1083],{"class":1075},[1058,4468,1126],{"class":1071},[1058,4470,1129],{"class":1075},[1058,4472,1132],{"class":1064},[1058,4474,4475,4477,4479,4481,4483,4485,4487],{"class":1060,"line":1135},[1058,4476,1072],{"class":1071},[1058,4478,1076],{"class":1075},[1058,4480,1080],{"class":1079},[1058,4482,1083],{"class":1075},[1058,4484,1146],{"class":1071},[1058,4486,1149],{"class":1075},[1058,4488,1152],{"class":1064},[1058,4490,4491,4493,4495,4497,4499,4501,4503],{"class":1060,"line":1155},[1058,4492,1072],{"class":1071},[1058,4494,1076],{"class":1075},[1058,4496,1080],{"class":1079},[1058,4498,1083],{"class":1075},[1058,4500,1166],{"class":1079},[1058,4502,1169],{"class":1075},[1058,4504,1172],{"class":1064},[1058,4506,4507,4509,4511,4513,4515,4517,4519,4521,4523,4525],{"class":1060,"line":1175},[1058,4508,1072],{"class":1071},[1058,4510,1076],{"class":1075},[1058,4512,1080],{"class":1079},[1058,4514,1083],{"class":1075},[1058,4516,1187],{"class":1186},[1058,4518,909],{"class":1190},[1058,4520,1187],{"class":1186},[1058,4522,968],{"class":1075},[1058,4524,1197],{"class":1071},[1058,4526,1200],{"class":1075},[925,4528,1204],{"id":1203},[1206,4530,4531,4545],{},[1209,4532,4533],{},[1212,4534,4535,4537,4539,4541,4543],{},[1215,4536,1217],{},[1215,4538,1220],{},[1215,4540,1223],{},[1215,4542,1226],{},[1215,4544,1229],{},[1231,4546,4547,4575,4605],{},[1212,4548,4549,4553,4557,4569,4573],{},[1236,4550,4551],{},[867,4552,1240],{},[1236,4554,4555],{},[867,4556,1245],{},[1236,4558,4559,968,4561,968,4563,968,4565,968,4567],{},[867,4560,1250],{},[867,4562,1253],{},[867,4564,1256],{},[867,4566,905],{},[867,4568,1261],{},[1236,4570,4571],{},[867,4572,1266],{},[1236,4574,1269],{},[1212,4576,4577,4581,4585,4599,4603],{},[1236,4578,4579],{},[867,4580,1240],{},[1236,4582,4583],{},[867,4584,1280],{},[1236,4586,4587,968,4589,968,4591,968,4593,968,4595,968,4597],{},[867,4588,1250],{},[867,4590,1253],{},[867,4592,1256],{},[867,4594,905],{},[867,4596,1261],{},[867,4598,1295],{},[1236,4600,4601],{},[867,4602,1300],{},[1236,4604,1303],{},[1212,4606,4607,4611,4615,4617,4621],{},[1236,4608,4609],{},[867,4610,1240],{},[1236,4612,4613],{},[867,4614,909],{},[1236,4616,1316],{},[1236,4618,4619],{},[867,4620,1197],{},[1236,4622,1323],{},[944,4624],{},[947,4626,1223],{"id":1328},[856,4628,1331,4629,1334,4631,1337],{},[867,4630,1245],{},[867,4632,1280],{},[925,4634,4635],{"id":1340},[867,4636,1250],{},[856,4638,1345,4639,1349,4641,1353,4643,1357,4645,1361],{},[867,4640,1348],{},[867,4642,1352],{},[867,4644,1356],{},[867,4646,1360],{},[925,4648,4649],{"id":1364},[867,4650,1253],{},[856,4652,1369,4653,1372,4655,1376],{},[867,4654,985],{},[867,4656,1375],{},[925,4658,4659],{"id":1379},[867,4660,1256],{},[856,4662,1384,4663,1388,4665,1391,4667,1394],{},[867,4664,1387],{},[867,4666,1261],{},[860,4668,127],{"href":128},[925,4670,4671],{"id":1397},[867,4672,905],{},[856,4674,1402,4675,1405,4677,1409,4679,1413,4681,1417,4683,1421,4685,1424],{},[867,4676,985],{},[867,4678,1408],{},[867,4680,1412],{},[867,4682,1416],{},[867,4684,1420],{},[867,4686,1261],{},[925,4688,4689],{"id":1427},[867,4690,1261],{},[856,4692,1432],{},[1434,4694,4695,4703],{},[878,4696,4697,1442,4699,1446,4701,1449],{},[1439,4698,1441],{},[867,4700,1445],{},[867,4702,1360],{},[878,4704,4705,1442,4707,1458,4709,1461,4711,4819,1609,4821,1614],{},[1439,4706,1454],{},[867,4708,1457],{},[867,4710,1256],{},[1206,4712,4713,4723],{},[1209,4714,4715],{},[1212,4716,4717,4719,4721],{},[1215,4718,1470],{},[1215,4720,1473],{},[1215,4722,1476],{},[1231,4724,4725,4733,4747,4759,4769,4779,4787,4795,4803,4811],{},[1212,4726,4727,4729,4731],{},[1236,4728,1483],{},[1236,4730,1486],{},[1236,4732,1489],{},[1212,4734,4735,4737,4745],{},[1236,4736,1494],{},[1236,4738,1497,4739,1501,4741,1505,4743],{},[867,4740,1500],{},[867,4742,1504],{},[867,4744,1508],{},[1236,4746,1489],{},[1212,4748,4749,4751,4755],{},[1236,4750,1515],{},[1236,4752,4753,1520],{},[867,4754,993],{},[1236,4756,1523,4757],{},[860,4758,1526],{"href":124},[1212,4760,4761,4763,4767],{},[1236,4762,1531],{},[1236,4764,1534,4765,1538],{},[867,4766,1537],{},[1236,4768,1541],{},[1212,4770,4771,4773,4777],{},[1236,4772,1546],{},[1236,4774,1549,4775],{},[867,4776,1552],{},[1236,4778,1541],{},[1212,4780,4781,4783,4785],{},[1236,4782,1559],{},[1236,4784,1562],{},[1236,4786,1565],{},[1212,4788,4789,4791,4793],{},[1236,4790,1570],{},[1236,4792,1573],{},[1236,4794,1541],{},[1212,4796,4797,4799,4801],{},[1236,4798,1580],{},[1236,4800,1583],{},[1236,4802,1541],{},[1212,4804,4805,4807,4809],{},[1236,4806,1590],{},[1236,4808,1593],{},[1236,4810,1541],{},[1212,4812,4813,4815,4817],{},[1236,4814,1600],{},[1236,4816,1603],{},[1236,4818,1541],{},[1607,4820],{},[860,4822,1613],{"href":1612},[856,4824,1617,4825,1620,4827,1624],{},[867,4826,1261],{},[867,4828,1623],{},[1206,4830,4831,4841],{},[1209,4832,4833],{},[1212,4834,4835,4837,4839],{},[1215,4836,1633],{},[1215,4838,1636],{},[1215,4840,1639],{},[1231,4842,4843,4857,4871,4885,4899],{},[1212,4844,4845,4849,4853],{},[1236,4846,4847],{},[867,4848,1648],{},[1236,4850,4851],{},[867,4852,1653],{},[1236,4854,1656,4855,1660],{},[867,4856,1659],{},[1212,4858,4859,4863,4867],{},[1236,4860,4861],{},[867,4862,1667],{},[1236,4864,4865],{},[867,4866,1653],{},[1236,4868,1656,4869,1660],{},[867,4870,1676],{},[1212,4872,4873,4877,4881],{},[1236,4874,4875],{},[867,4876,1683],{},[1236,4878,4879],{},[867,4880,1653],{},[1236,4882,1656,4883,1693],{},[867,4884,1692],{},[1212,4886,4887,4891,4895],{},[1236,4888,4889],{},[867,4890,1700],{},[1236,4892,4893],{},[867,4894,1705],{},[1236,4896,1656,4897,1710],{},[867,4898,1700],{},[1212,4900,4901,4905,4909],{},[1236,4902,4903],{},[867,4904,1717],{},[1236,4906,4907],{},[867,4908,1722],{},[1236,4910,1725],{},[925,4912,4913,1731],{"id":1728},[867,4914,1295],{},[856,4916,1734,4917,1737],{},[867,4918,1261],{},[1206,4920,4921,4931],{},[1209,4922,4923],{},[1212,4924,4925,4927,4929],{},[1215,4926,1746],{},[1215,4928,1749],{},[1215,4930,1752],{},[1231,4932,4933,4947,4963,4975],{},[1212,4934,4935,4939,4943],{},[1236,4936,4937,1761],{},[867,4938,985],{},[1236,4940,4941],{},[867,4942,1766],{},[1236,4944,4945],{},[867,4946,1375],{},[1212,4948,4949,4955,4959],{},[1236,4950,1775,4951,1779,4953,1783],{},[867,4952,1778],{},[867,4954,1782],{},[1236,4956,4957],{},[867,4958,1788],{},[1236,4960,4961],{},[867,4962,1793],{},[1212,4964,4965,4967,4971],{},[1236,4966,1798],{},[1236,4968,4969],{},[867,4970,1788],{},[1236,4972,4973],{},[867,4974,1807],{},[1212,4976,4977,4981,4985],{},[1236,4978,4979,1815],{},[867,4980,1814],{},[1236,4982,4983],{},[867,4984,1788],{},[1236,4986,4987],{},[867,4988,1824],{},[856,4990,1827],{},[944,4992],{},[947,4994,1833,4995],{"id":1832},[867,4996,882],{},[856,4998,1656,4999,1840,5001,1843],{},[867,5000,1266],{},[867,5002,1623],{},[925,5004,1847],{"id":1846},[1049,5006,5007],{"className":1850,"code":1851,"language":5,"meta":1054,"style":1054},[867,5008,5009,5013,5027,5041,5059,5077,5095,5123],{"__ignoreMap":1054},[1058,5010,5011],{"class":1060,"line":1061},[1058,5012,1858],{"class":1075},[1058,5014,5015,5017,5019,5021,5023,5025],{"class":1060,"line":1068},[1058,5016,1864],{"class":1863},[1058,5018,1648],{"class":1867},[1058,5020,1870],{"class":1863},[1058,5022,1874],{"class":1873},[1058,5024,1878],{"class":1877},[1058,5026,1881],{"class":1075},[1058,5028,5029,5031,5033,5035,5037,5039],{"class":1060,"line":1095},[1058,5030,1864],{"class":1863},[1058,5032,1888],{"class":1867},[1058,5034,1870],{"class":1863},[1058,5036,1874],{"class":1873},[1058,5038,1896],{"class":1895},[1058,5040,1881],{"class":1075},[1058,5042,5043,5045,5047,5049,5051,5053,5055,5057],{"class":1060,"line":1115},[1058,5044,1864],{"class":1863},[1058,5046,1905],{"class":1867},[1058,5048,1870],{"class":1863},[1058,5050,1874],{"class":1873},[1058,5052,1912],{"class":1186},[1058,5054,1915],{"class":1190},[1058,5056,1870],{"class":1186},[1058,5058,1881],{"class":1075},[1058,5060,5061,5063,5065,5067,5069,5071,5073,5075],{"class":1060,"line":1135},[1058,5062,1864],{"class":1863},[1058,5064,1926],{"class":1867},[1058,5066,1870],{"class":1863},[1058,5068,1874],{"class":1873},[1058,5070,1912],{"class":1186},[1058,5072,1935],{"class":1190},[1058,5074,1870],{"class":1186},[1058,5076,1881],{"class":1075},[1058,5078,5079,5081,5083,5085,5087,5089,5091,5093],{"class":1060,"line":1155},[1058,5080,1864],{"class":1863},[1058,5082,1946],{"class":1867},[1058,5084,1870],{"class":1863},[1058,5086,1874],{"class":1873},[1058,5088,1912],{"class":1186},[1058,5090,1955],{"class":1190},[1058,5092,1870],{"class":1186},[1058,5094,1881],{"class":1075},[1058,5096,5097,5099,5101,5103,5105,5107,5109,5111,5113,5115,5117,5119,5121],{"class":1060,"line":1175},[1058,5098,1864],{"class":1863},[1058,5100,1700],{"class":1867},[1058,5102,1870],{"class":1863},[1058,5104,1874],{"class":1873},[1058,5106,1972],{"class":1075},[1058,5108,1870],{"class":1186},[1058,5110,1977],{"class":1190},[1058,5112,1870],{"class":1186},[1058,5114,968],{"class":1075},[1058,5116,1870],{"class":1186},[1058,5118,1986],{"class":1190},[1058,5120,1870],{"class":1186},[1058,5122,1991],{"class":1075},[1058,5124,5125],{"class":1060,"line":1994},[1058,5126,1997],{"class":1075},[1999,5128,5129,5135,5141,5147,5153,5157],{},[2002,5130,5131],{"name":1648,"type":2004},[856,5132,2007,5133,2010],{},[867,5134,1659],{},[2002,5136,5137],{"name":1888,"type":2013},[856,5138,2016,5139,2020],{},[867,5140,2019],{},[2002,5142,5143],{"name":1905,"type":1653},[856,5144,2025,5145,2029],{},[867,5146,2028],{},[2002,5148,5149],{"name":1926,"type":1653},[856,5150,1656,5151,2036],{},[867,5152,1387],{},[2002,5154,5155],{"name":1946,"type":1653},[856,5156,2041],{},[2002,5158,5159],{"name":1700,"type":2044},[856,5160,2047,5161,2051],{},[867,5162,2050],{},[925,5164,2055],{"id":2054},[1206,5166,5167,5177],{},[1209,5168,5169],{},[1212,5170,5171,5173,5175],{},[1215,5172,1749],{},[1215,5174,2066],{},[1215,5176,2069],{},[1231,5178,5179,5191,5203,5217,5229],{},[1212,5180,5181,5185,5189],{},[1236,5182,5183],{},[867,5184,2078],{},[1236,5186,5187],{},[867,5188,2083],{},[1236,5190,2086],{},[1212,5192,5193,5197,5201],{},[1236,5194,5195],{},[867,5196,2093],{},[1236,5198,2096,5199,1783],{},[867,5200,1261],{},[1236,5202,2101],{},[1212,5204,5205,5209,5213],{},[1236,5206,5207],{},[867,5208,1766],{},[1236,5210,5211],{},[867,5212,2112],{},[1236,5214,5215,2117],{},[867,5216,1623],{},[1212,5218,5219,5223,5227],{},[1236,5220,5221],{},[867,5222,2124],{},[1236,5224,5225],{},[867,5226,2129],{},[1236,5228,2132],{},[1212,5230,5231,5235,5237],{},[1236,5232,5233],{},[867,5234,2139],{},[1236,5236,2142],{},[1236,5238,2145],{},[2147,5240,5241],{},[856,5242,2151,5243,2154,5245,2157],{},[867,5244,1261],{},[867,5246,1623],{},[925,5248,2161],{"id":2160},[856,5250,2164,5251,2167,5253,2171,5255,2175,5257,2179],{},[867,5252,993],{},[867,5254,2170],{},[867,5256,2174],{},[867,5258,2178],{},[944,5260],{},[947,5262,2185,5263],{"id":2184},[867,5264,888],{},[856,5266,1656,5267,2192],{},[867,5268,1300],{},[925,5270,1847],{"id":2195},[1049,5272,5273],{"className":1850,"code":2198,"language":5,"meta":1054,"style":1054},[867,5274,5275,5279,5293,5311,5329,5347,5367,5379,5397,5415,5433,5453,5467,5481,5499,5515,5519,5533,5547,5559],{"__ignoreMap":1054},[1058,5276,5277],{"class":1060,"line":1061},[1058,5278,1858],{"class":1075},[1058,5280,5281,5283,5285,5287,5289,5291],{"class":1060,"line":1068},[1058,5282,1864],{"class":1863},[1058,5284,1888],{"class":1867},[1058,5286,1870],{"class":1863},[1058,5288,1874],{"class":1873},[1058,5290,1896],{"class":1895},[1058,5292,1881],{"class":1075},[1058,5294,5295,5297,5299,5301,5303,5305,5307,5309],{"class":1060,"line":1095},[1058,5296,1864],{"class":1863},[1058,5298,1905],{"class":1867},[1058,5300,1870],{"class":1863},[1058,5302,1874],{"class":1873},[1058,5304,1912],{"class":1186},[1058,5306,1915],{"class":1190},[1058,5308,1870],{"class":1186},[1058,5310,1881],{"class":1075},[1058,5312,5313,5315,5317,5319,5321,5323,5325,5327],{"class":1060,"line":1115},[1058,5314,1864],{"class":1863},[1058,5316,1926],{"class":1867},[1058,5318,1870],{"class":1863},[1058,5320,1874],{"class":1873},[1058,5322,1912],{"class":1186},[1058,5324,1935],{"class":1190},[1058,5326,1870],{"class":1186},[1058,5328,1881],{"class":1075},[1058,5330,5331,5333,5335,5337,5339,5341,5343,5345],{"class":1060,"line":1135},[1058,5332,1864],{"class":1863},[1058,5334,1946],{"class":1867},[1058,5336,1870],{"class":1863},[1058,5338,1874],{"class":1873},[1058,5340,1912],{"class":1186},[1058,5342,1955],{"class":1190},[1058,5344,1870],{"class":1186},[1058,5346,1881],{"class":1075},[1058,5348,5349,5351,5353,5355,5357,5359,5361,5363,5365],{"class":1060,"line":1155},[1058,5350,1864],{"class":1863},[1058,5352,1700],{"class":1867},[1058,5354,1870],{"class":1863},[1058,5356,1874],{"class":1873},[1058,5358,1972],{"class":1075},[1058,5360,1870],{"class":1186},[1058,5362,1977],{"class":1190},[1058,5364,1870],{"class":1186},[1058,5366,2293],{"class":1075},[1058,5368,5369,5371,5373,5375,5377],{"class":1060,"line":1175},[1058,5370,1864],{"class":1863},[1058,5372,1717],{"class":1867},[1058,5374,1870],{"class":1863},[1058,5376,1874],{"class":1873},[1058,5378,2306],{"class":1075},[1058,5380,5381,5383,5385,5387,5389,5391,5393,5395],{"class":1060,"line":1994},[1058,5382,2311],{"class":1863},[1058,5384,1659],{"class":1867},[1058,5386,1870],{"class":1863},[1058,5388,1874],{"class":1873},[1058,5390,1912],{"class":1186},[1058,5392,2322],{"class":1190},[1058,5394,1870],{"class":1186},[1058,5396,1881],{"class":1075},[1058,5398,5399,5401,5403,5405,5407,5409,5411,5413],{"class":1060,"line":2329},[1058,5400,2311],{"class":1863},[1058,5402,1676],{"class":1867},[1058,5404,1870],{"class":1863},[1058,5406,1874],{"class":1873},[1058,5408,1912],{"class":1186},[1058,5410,2342],{"class":1190},[1058,5412,1870],{"class":1186},[1058,5414,1881],{"class":1075},[1058,5416,5417,5419,5421,5423,5425,5427,5429,5431],{"class":1060,"line":2349},[1058,5418,2311],{"class":1863},[1058,5420,1692],{"class":1867},[1058,5422,1870],{"class":1863},[1058,5424,1874],{"class":1873},[1058,5426,1912],{"class":1186},[1058,5428,2362],{"class":1190},[1058,5430,1870],{"class":1186},[1058,5432,1881],{"class":1075},[1058,5434,5435,5437,5439,5441,5443,5445,5447,5449,5451],{"class":1060,"line":2369},[1058,5436,2311],{"class":1863},[1058,5438,1700],{"class":1867},[1058,5440,1870],{"class":1863},[1058,5442,1874],{"class":1873},[1058,5444,1972],{"class":1075},[1058,5446,1870],{"class":1186},[1058,5448,1977],{"class":1190},[1058,5450,1870],{"class":1186},[1058,5452,2293],{"class":1075},[1058,5454,5455,5457,5459,5461,5463,5465],{"class":1060,"line":2390},[1058,5456,2311],{"class":1863},[1058,5458,2395],{"class":1867},[1058,5460,1870],{"class":1863},[1058,5462,1874],{"class":1873},[1058,5464,2402],{"class":1877},[1058,5466,1881],{"class":1075},[1058,5468,5469,5471,5473,5475,5477,5479],{"class":1060,"line":2407},[1058,5470,2311],{"class":1863},[1058,5472,2412],{"class":1867},[1058,5474,1870],{"class":1863},[1058,5476,1874],{"class":1873},[1058,5478,2419],{"class":1877},[1058,5480,1881],{"class":1075},[1058,5482,5483,5485,5487,5489,5491,5493,5495,5497],{"class":1060,"line":2424},[1058,5484,2311],{"class":1863},[1058,5486,2429],{"class":1867},[1058,5488,1870],{"class":1863},[1058,5490,1874],{"class":1873},[1058,5492,1912],{"class":1186},[1058,5494,2438],{"class":1190},[1058,5496,1870],{"class":1186},[1058,5498,1881],{"class":1075},[1058,5500,5501,5503,5505,5507,5509,5511,5513],{"class":1060,"line":2445},[1058,5502,2311],{"class":1863},[1058,5504,2450],{"class":1867},[1058,5506,1870],{"class":1863},[1058,5508,1874],{"class":1873},[1058,5510,1912],{"class":1186},[1058,5512,2438],{"class":1190},[1058,5514,2461],{"class":1186},[1058,5516,5517],{"class":1060,"line":2464},[1058,5518,2467],{"class":1075},[1058,5520,5521,5523,5525,5527,5529,5531],{"class":1060,"line":2470},[1058,5522,1864],{"class":1863},[1058,5524,2475],{"class":1867},[1058,5526,1870],{"class":1863},[1058,5528,1874],{"class":1873},[1058,5530,2482],{"class":1877},[1058,5532,1881],{"class":1075},[1058,5534,5535,5537,5539,5541,5543,5545],{"class":1060,"line":2487},[1058,5536,1864],{"class":1863},[1058,5538,2492],{"class":1867},[1058,5540,1870],{"class":1863},[1058,5542,1874],{"class":1873},[1058,5544,2499],{"class":1877},[1058,5546,1881],{"class":1075},[1058,5548,5549,5551,5553,5555,5557],{"class":1060,"line":2504},[1058,5550,1864],{"class":1863},[1058,5552,2509],{"class":1867},[1058,5554,1870],{"class":1863},[1058,5556,1874],{"class":1873},[1058,5558,2516],{"class":1895},[1058,5560,5561],{"class":1060,"line":2519},[1058,5562,1997],{"class":1075},[1999,5564,5565,5571,5593,5601,5605],{},[2002,5566,5567],{"name":1888,"type":2013},[856,5568,2016,5569,2530],{},[867,5570,2019],{},[2002,5572,5573],{"name":1717,"type":1722},[856,5574,2535,5575,2538,5577,2541,5579,2544,5581,2547,5583,968,5585,968,5587,968,5589,1009,5591,1076],{},[867,5576,1261],{},[867,5578,1659],{},[867,5580,1676],{},[867,5582,1692],{},[867,5584,1700],{},[867,5586,2395],{},[867,5588,2412],{},[867,5590,2429],{},[867,5592,2450],{},[2002,5594,5595],{"name":2475,"type":2004},[856,5596,2562,5597,2566,5599,2570],{},[867,5598,2565],{},[867,5600,2569],{},[2002,5602,5603],{"name":2492,"type":2004},[856,5604,2575],{},[2002,5606,5607],{"name":2509,"type":2013},[856,5608,5609,2582,5611,2586,5613,1076],{},[867,5610,2019],{},[867,5612,2585],{},[867,5614,1023],{},[925,5616,2592],{"id":2591},[856,5618,2595,5619,1334,5621,2600],{},[867,5620,2395],{},[867,5622,2412],{},[1049,5624,5626],{"className":5625,"code":2605,"language":2606},[2604],[867,5627,2605],{"__ignoreMap":1054},[856,5629,2611,5630,2614,5632,2617,5634,2620],{},[867,5631,2509],{},[867,5633,2019],{},[867,5635,1023],{},[938,5637,5638],{},[856,5639,2625],{},[925,5641,2055],{"id":2628},[1206,5643,5644,5654],{},[1209,5645,5646],{},[1212,5647,5648,5650,5652],{},[1215,5649,1749],{},[1215,5651,2066],{},[1215,5653,2069],{},[1231,5655,5656,5668,5680,5694,5706],{},[1212,5657,5658,5662,5666],{},[1236,5659,5660],{},[867,5661,2078],{},[1236,5663,5664],{},[867,5665,2653],{},[1236,5667,2656],{},[1212,5669,5670,5674,5678],{},[1236,5671,5672],{},[867,5673,2093],{},[1236,5675,2096,5676,1783],{},[867,5677,1261],{},[1236,5679,2101],{},[1212,5681,5682,5686,5690],{},[1236,5683,5684],{},[867,5685,1766],{},[1236,5687,5688],{},[867,5689,2112],{},[1236,5691,5692,2117],{},[867,5693,1623],{},[1212,5695,5696,5700,5704],{},[1236,5697,5698],{},[867,5699,2124],{},[1236,5701,5702],{},[867,5703,2129],{},[1236,5705,2132],{},[1212,5707,5708,5712,5716],{},[1236,5709,5710],{},[867,5711,2139],{},[1236,5713,5714],{},[867,5715,2705],{},[1236,5717,2708],{},[925,5719,2712],{"id":2711},[856,5721,2715],{},[955,5723,5724,5726,5736,5740,5746,5748,5760,5762],{"level":957},[959,5725,2721],{"id":2720},[856,5727,2724,5728,2727,5730,2730,5732,1334,5734,2735],{},[867,5729,888],{},[867,5731,1352],{},[867,5733,985],{},[867,5735,993],{},[959,5737,2739,5738,2742],{"id":2738},[867,5739,2509],{},[856,5741,2745,5742,2748,5744,2751],{},[867,5743,2509],{},[867,5745,1504],{},[959,5747,2755],{"id":2754},[856,5749,2745,5750,2748,5752,2762,5754,2765,5756,1334,5758,2770],{},[867,5751,2509],{},[867,5753,2019],{},[867,5755,1023],{},[867,5757,985],{},[867,5759,993],{},[959,5761,2774],{"id":2773},[856,5763,2777,5764,968,5766,1009,5768,2785],{},[867,5765,989],{},[867,5767,985],{},[867,5769,2784],{},[2147,5771,5772],{},[856,5773,2790,5774,2794],{},[867,5775,2793],{},[944,5777],{},[947,5779,2800,5780],{"id":2799},[867,5781,894],{},[856,5783,2805],{},[925,5785,2809],{"id":2808},[856,5787,2812,5788,2816,5790,2820,5792,2824,5794,2828,5796,1076],{},[867,5789,2815],{},[867,5791,2819],{},[867,5793,2823],{},[867,5795,2827],{},[867,5797,2831],{},[2147,5799,5800],{},[856,5801,2836,5802,2839,5804,1505,5806,2845],{},[867,5803,2815],{},[867,5805,2842],{},[867,5807,2028],{},[925,5809,1847],{"id":2848},[1049,5811,5812],{"className":1850,"code":2851,"language":5,"meta":1054,"style":1054},[867,5813,5814,5818,5836,5848],{"__ignoreMap":1054},[1058,5815,5816],{"class":1060,"line":1061},[1058,5817,1858],{"class":1075},[1058,5819,5820,5822,5824,5826,5828,5830,5832,5834],{"class":1060,"line":1068},[1058,5821,1864],{"class":1863},[1058,5823,2864],{"class":1867},[1058,5825,1870],{"class":1863},[1058,5827,1874],{"class":1873},[1058,5829,1912],{"class":1186},[1058,5831,2873],{"class":1190},[1058,5833,1870],{"class":1186},[1058,5835,1881],{"class":1075},[1058,5837,5838,5840,5842,5844,5846],{"class":1060,"line":1095},[1058,5839,1864],{"class":1863},[1058,5841,2884],{"class":1867},[1058,5843,1870],{"class":1863},[1058,5845,1874],{"class":1873},[1058,5847,2891],{"class":1877},[1058,5849,5850],{"class":1060,"line":1115},[1058,5851,1997],{"class":1075},[1999,5853,5854,5862],{},[2002,5855,5856],{"name":2864,"type":1653},[856,5857,2902,5858,2906,5860,2910],{},[867,5859,2905],{},[867,5861,2909],{},[2002,5863,5864],{"name":2884,"type":2004},[856,5865,2915,5866,2919,5868,2923,5870,2926],{},[867,5867,2918],{},[867,5869,2922],{},[867,5871,989],{},[856,5873,2929],{},[944,5875],{},[947,5877,2935],{"id":2934},[856,5879,2938,5880,2942],{},[867,5881,2941],{},[1049,5883,5884],{"className":1051,"code":2945,"language":1053,"meta":1054,"style":1054},[867,5885,5886,5892,5898,5904,5910,5914],{"__ignoreMap":1054},[1058,5887,5888,5890],{"class":1060,"line":1061},[1058,5889,2953],{"class":2952},[1058,5891,2306],{"class":1075},[1058,5893,5894,5896],{"class":1060,"line":1068},[1058,5895,2960],{"class":1071},[1058,5897,1881],{"class":1075},[1058,5899,5900,5902],{"class":1060,"line":1095},[1058,5901,2967],{"class":1071},[1058,5903,1881],{"class":1075},[1058,5905,5906,5908],{"class":1060,"line":1115},[1058,5907,2974],{"class":1071},[1058,5909,1881],{"class":1075},[1058,5911,5912],{"class":1060,"line":1135},[1058,5913,2981],{"class":1071},[1058,5915,5916,5918,5920,5922,5924],{"class":1060,"line":1155},[1058,5917,2986],{"class":1075},[1058,5919,2989],{"class":2952},[1058,5921,2992],{"class":1186},[1058,5923,2941],{"class":1190},[1058,5925,2997],{"class":1186},[1206,5927,5928,5938],{},[1209,5929,5930],{},[1212,5931,5932,5934,5936],{},[1215,5933,3006],{},[1215,5935,1636],{},[1215,5937,1639],{},[1231,5939,5940,5956,5968,5982],{},[1212,5941,5942,5946,5950],{},[1236,5943,5944],{},[867,5945,3019],{},[1236,5947,5948],{},[867,5949,3024],{},[1236,5951,3027,5952,1334,5954,3032],{},[867,5953,1245],{},[867,5955,1280],{},[1212,5957,5958,5962,5966],{},[1236,5959,5960],{},[867,5961,1266],{},[1236,5963,5964],{},[867,5965,3043],{},[1236,5967,3046],{},[1212,5969,5970,5974,5978],{},[1236,5971,5972],{},[867,5973,1300],{},[1236,5975,5976],{},[867,5977,3043],{},[1236,5979,3059,5980,3062],{},[867,5981,1623],{},[1212,5983,5984,5988,5992],{},[1236,5985,5986],{},[867,5987,1197],{},[1236,5989,5990],{},[867,5991,3043],{},[1236,5993,3075],{},[856,5995,3078],{},[1049,5997,5998],{"className":1051,"code":3081,"language":1053,"meta":1054,"style":1054},[867,5999,6000,6006,6012,6018,6022],{"__ignoreMap":1054},[1058,6001,6002,6004],{"class":1060,"line":1061},[1058,6003,2953],{"class":2952},[1058,6005,2306],{"class":1075},[1058,6007,6008,6010],{"class":1060,"line":1068},[1058,6009,3094],{"class":1071},[1058,6011,1881],{"class":1075},[1058,6013,6014,6016],{"class":1060,"line":1095},[1058,6015,3101],{"class":1071},[1058,6017,1881],{"class":1075},[1058,6019,6020],{"class":1060,"line":1115},[1058,6021,3108],{"class":1071},[1058,6023,6024,6026,6028,6030,6032],{"class":1060,"line":1135},[1058,6025,2986],{"class":1075},[1058,6027,2989],{"class":2952},[1058,6029,2992],{"class":1186},[1058,6031,2941],{"class":1190},[1058,6033,2997],{"class":1186},[856,6035,3123],{},[944,6037],{},[947,6039,3129],{"id":3128},[856,6041,3132,6042,3136,6044,3140,6046,968,6048,1009,6050,3147],{},[867,6043,3135],{},[867,6045,3139],{},[867,6047,1245],{},[867,6049,1280],{},[867,6051,909],{},[856,6053,3150,6054,1334,6056,3157,6058,3160],{},[867,6055,3153],{},[867,6057,3156],{},[860,6059,135],{"href":136},[3162,6061,6062],{},[856,6063,3166],{},[944,6065],{},[947,6067,3172],{"id":3171},[856,6069,3175,6070,968,6072,968,6074,3184],{},[867,6071,3178],{},[867,6073,3181],{},[867,6075,1023],{},[1206,6077,6078,6102],{},[1209,6079,6080],{},[1212,6081,6082,6084,6088,6092,6096,6098,6100],{},[1215,6083,3193],{},[1215,6085,6086],{},[867,6087,869],{},[1215,6089,6090],{},[867,6091,971],{},[1215,6093,6094],{},[867,6095,3206],{},[1215,6097,3209],{},[1215,6099,3212],{},[1215,6101,1229],{},[1231,6103,6104,6124,6144,6166],{},[1212,6105,6106,6110,6112,6114,6118,6120,6122],{},[1236,6107,6108],{},[867,6109,989],{},[1236,6111,3225],{},[1236,6113,3225],{},[1236,6115,6116],{},[867,6117,3232],{},[1236,6119,3235],{},[1236,6121,3238],{},[1236,6123,3241],{},[1212,6125,6126,6130,6132,6134,6138,6140,6142],{},[1236,6127,6128],{},[867,6129,985],{},[1236,6131,3225],{},[1236,6133,3225],{},[1236,6135,6136],{},[867,6137,3232],{},[1236,6139,3258],{},[1236,6141,3261],{},[1236,6143,3264],{},[1212,6145,6146,6150,6152,6154,6158,6160,6162],{},[1236,6147,6148],{},[867,6149,993],{},[1236,6151,3225],{},[1236,6153,3225],{},[1236,6155,6156],{},[867,6157,3279],{},[1236,6159,3282],{},[1236,6161,3261],{},[1236,6163,3287,6164],{},[860,6165,1012],{"href":3290},[1212,6167,6168,6172,6174,6176,6180,6182,6184],{},[1236,6169,6170],{},[867,6171,2784],{},[1236,6173,3225],{},[1236,6175,3225],{},[1236,6177,6178],{},[867,6179,3232],{},[1236,6181,3235],{},[1236,6183,3238],{},[1236,6185,3311,6186,3314],{},[867,6187,2395],{},[856,6189,3317,6190,1334,6192,3322,6194,3325,6196,3328,6198,3331],{},[867,6191,985],{},[867,6193,993],{},[867,6195,1352],{},[867,6197,989],{},[867,6199,1348],{},[938,6201,6202],{},[856,6203,1656,6204,3338,6206,3341,6208,3344],{},[867,6205,989],{},[867,6207,971],{},[867,6209,2784],{},[944,6211],{},[947,6213,3350],{"id":3349},[856,6215,3353],{},[1206,6217,6218,6232],{},[1209,6219,6220],{},[1212,6221,6222,6224,6226,6228,6230],{},[1215,6223,3362],{},[1215,6225,1217],{},[1215,6227,3367],{},[1215,6229,3370],{},[1215,6231,3373],{},[1231,6233,6234,6260,6286,6308,6332,6356,6382],{},[1212,6235,6236,6240,6244,6246,6252],{},[1236,6237,6238],{},[867,6239,1245],{},[1236,6241,6242],{},[867,6243,1240],{},[1236,6245,3388],{},[1236,6247,6248,3393,6250],{},[867,6249,2078],{},[867,6251,3396],{},[1236,6253,6254,3401,6256,3404,6258,3407],{},[867,6255,2093],{},[867,6257,1766],{},[867,6259,2124],{},[1212,6261,6262,6266,6270,6272,6278],{},[1236,6263,6264],{},[867,6265,1280],{},[1236,6267,6268],{},[867,6269,1240],{},[1236,6271,3420],{},[1236,6273,6274,3393,6276],{},[867,6275,2078],{},[867,6277,3427],{},[1236,6279,6280,3401,6282,3404,6284,3407],{},[867,6281,2093],{},[867,6283,1766],{},[867,6285,2124],{},[1212,6287,6288,6292,6296,6298,6304],{},[1236,6289,6290],{},[867,6291,909],{},[1236,6293,6294],{},[867,6295,1240],{},[1236,6297,3448],{},[1236,6299,6300,3393,6302],{},[867,6301,2078],{},[867,6303,3455],{},[1236,6305,6306,3461],{},[867,6307,3460],{},[1212,6309,6310,6314,6318,6320,6326],{},[1236,6311,6312],{},[867,6313,3468],{},[1236,6315,6316],{},[867,6317,3473],{},[1236,6319,3476],{},[1236,6321,6322,3482,6324,3485],{},[867,6323,3481],{},[867,6325,1027],{},[1236,6327,6328,3490,6330,3494],{},[867,6329,1766],{},[867,6331,3493],{},[1212,6333,6334,6338,6342,6344,6350],{},[1236,6335,6336],{},[867,6337,3501],{},[1236,6339,6340],{},[867,6341,3473],{},[1236,6343,3508],{},[1236,6345,6346,3482,6348,3485],{},[867,6347,3481],{},[867,6349,1027],{},[1236,6351,6352,3519,6354,3494],{},[867,6353,1788],{},[867,6355,3493],{},[1212,6357,6358,6362,6366,6368,6374],{},[1236,6359,6360],{},[867,6361,3528],{},[1236,6363,6364],{},[867,6365,3473],{},[1236,6367,3535],{},[1236,6369,6370,3540,6372,3543],{},[867,6371,3481],{},[867,6373,1027],{},[1236,6375,6376,3401,6378,3550,6380,3494],{},[867,6377,2093],{},[867,6379,1766],{},[867,6381,3493],{},[1212,6383,6384,6388,6392,6394,6400],{},[1236,6385,6386],{},[867,6387,3559],{},[1236,6389,6390],{},[867,6391,3473],{},[1236,6393,3566],{},[1236,6395,6396,3393,6398,3573],{},[867,6397,2078],{},[867,6399,1027],{},[1236,6401,6402,3578],{},[867,6403,1766],{},[2147,6405,6406],{},[856,6407,3583,6408,3586,6410,3589],{},[867,6409,2093],{},[860,6411,123],{"href":124},[944,6413],{},[947,6415,3595],{"id":3594},[856,6417,3598],{},[925,6419,3602],{"id":3601},[1999,6421,6422,6430,6438],{},[2002,6423,6424],{"name":3607,"type":1653},[856,6425,3610,6426,3613,6428,3616],{},[867,6427,894],{},[867,6429,2815],{},[2002,6431,6432],{"name":3619,"type":1653},[856,6433,3622,6434,3625,6436,3628],{},[867,6435,3607],{},[867,6437,2028],{},[2002,6439,6440],{"name":3631,"type":2013},[856,6441,3634,6442,3637,6444,1334,6446,3643],{},[867,6443,2019],{},[867,6445,2028],{},[867,6447,3642],{},[925,6449,3647],{"id":3646},[1999,6451,6452,6458,6464],{},[2002,6453,6454],{"name":3652,"type":1653},[856,6455,3655,6456,1076],{},[860,6457,135],{"href":136},[2002,6459,6460],{"name":3660,"type":1653},[856,6461,3663,6462,3667],{},[867,6463,3666],{},[2002,6465,6466],{"name":3670,"type":2004,"default":3671},[856,6467,3674],{},[925,6469,3678],{"id":3677},[1999,6471,6472,6484,6492,6498],{},[2002,6473,6474],{"name":3683,"type":2004,"default":3684},[856,6475,3687,6476,2614,6478,3692,6480,3695,6482,3698],{},[867,6477,2509],{},[867,6479,2019],{},[867,6481,894],{},[867,6483,2884],{},[2002,6485,6486],{"name":3701,"type":1653},[856,6487,3704,6488,3695,6490,3709],{},[867,6489,894],{},[867,6491,2864],{},[2002,6493,6494],{"name":3712,"type":2004},[856,6495,3715,6496,3718],{},[867,6497,1261],{},[2002,6499,6500],{"name":3721,"type":2004},[856,6501,3724,6502,1076],{},[860,6503,1613],{"href":1612},[944,6505],{},[947,6507,3732],{"id":3731},[856,6509,6510,3737,6512,3743],{},[860,6511,919],{"href":22},[860,6513,3742],{"href":3740,"rel":6514},[864],[875,6516,6517,6525,6531,6535,6539,6545,6549,6553,6557,6561,6565],{},[878,6518,6519,3751,6521,1334,6523,1076],{},[1439,6520,3750],{},[867,6522,888],{},[867,6524,1023],{},[878,6526,6527,3761,6529,1076],{},[1439,6528,3760],{},[867,6530,894],{},[878,6532,6533,3769],{},[1439,6534,3768],{},[878,6536,6537,3775],{},[1439,6538,3774],{},[878,6540,6541,3781,6543,3785],{},[1439,6542,3780],{},[867,6544,3784],{},[878,6546,6547,3791],{},[1439,6548,3790],{},[878,6550,6551,3797],{},[1439,6552,3796],{},[878,6554,6555,3803],{},[1439,6556,3802],{},[878,6558,6559,3809],{},[1439,6560,3808],{},[878,6562,6563,3815],{},[1439,6564,3814],{},[878,6566,6567,3821],{},[1439,6568,3820],{},[856,6570,3824],{},[856,6572,3827,6573,1076],{},[860,6574,935],{"href":22},[944,6576],{},[947,6578,3835],{"id":3834},[856,6580,3838],{},[1206,6582,6583,6597],{},[1209,6584,6585],{},[1212,6586,6587,6589,6591,6593,6595],{},[1215,6588,3847],{},[1215,6590,3850],{},[1215,6592,3853],{},[1215,6594,3856],{},[1215,6596,3859],{},[1231,6598,6599,6611,6623,6635,6647],{},[1212,6600,6601,6603,6605,6607,6609],{},[1236,6602,3866],{},[1236,6604,3869],{},[1236,6606,3872],{},[1236,6608,3875],{},[1236,6610,3878],{},[1212,6612,6613,6615,6617,6619,6621],{},[1236,6614,3883],{},[1236,6616,3886],{},[1236,6618,3889],{},[1236,6620,3892],{},[1236,6622,3878],{},[1212,6624,6625,6627,6629,6631,6633],{},[1236,6626,3899],{},[1236,6628,3869],{},[1236,6630,3872],{},[1236,6632,3875],{},[1236,6634,3878],{},[1212,6636,6637,6639,6641,6643,6645],{},[1236,6638,3912],{},[1236,6640,957],{},[1236,6642,3917],{},[1236,6644,3917],{},[1236,6646,3878],{},[1212,6648,6649,6651,6653,6655,6657],{},[1236,6650,3926],{},[1236,6652,3929],{},[1236,6654,3932],{},[1236,6656,3935],{},[1236,6658,3938],{},[856,6660,3941,6661,3945],{},[867,6662,3944],{},[944,6664],{},[947,6666,3951],{"id":3950},[856,6668,3954,6669,1505,6671,1874],{},[867,6670,882],{},[867,6672,888],{},[955,6674,6675,6677,6683,6685,6699,6701,6709,6711,6723,6725,6731,6733,6739,6741,6745,6747,6753,6755,6759,6761,6765,6767],{"level":957},[959,6676,2809],{"id":3963},[856,6678,3966,6679,3970,6681,3973],{},[867,6680,3969],{},[867,6682,2842],{},[959,6684,3977],{"id":3976},[856,6686,1656,6687,3983,6689,968,6691,968,6693,968,6695,3996,6697,4000],{},[867,6688,3982],{},[867,6690,3986],{},[867,6692,3989],{},[867,6694,3992],{},[867,6696,3995],{},[867,6698,3999],{},[959,6700,4004],{"id":4003},[856,6702,3634,6703,4009,6705,4012,6707,1076],{},[867,6704,3135],{},[867,6706,3139],{},[860,6708,135],{"href":136},[959,6710,4018],{"id":4017},[856,6712,1656,6713,4024,6715,4028,6717,4032,6719,4036,6721,4039],{},[867,6714,4023],{},[867,6716,4027],{},[867,6718,4031],{},[867,6720,4035],{},[867,6722,985],{},[959,6724,4043],{"id":4042},[856,6726,6727,4048,6729,4051],{},[867,6728,1250],{},[867,6730,1348],{},[959,6732,4055],{"id":4054},[856,6734,6735,4060,6737,4063],{},[867,6736,1253],{},[867,6738,985],{},[959,6740,127],{"id":4066},[856,6742,6743,4071],{},[867,6744,1256],{},[959,6746,4075],{"id":4074},[856,6748,6749,4080,6751,4083],{},[867,6750,905],{},[867,6752,1261],{},[959,6754,4087],{"id":4086},[856,6756,6757,4092],{},[867,6758,1261],{},[959,6760,4096],{"id":4095},[856,6762,6763,4101],{},[867,6764,1295],{},[959,6766,1226],{"id":4104},[856,6768,1656,6769,1505,6771,4111,6773,4114],{},[867,6770,1266],{},[867,6772,1300],{},[867,6774,1623],{},[944,6776],{},[947,6778,4120],{"id":4119},[1206,6780,6781,6789],{},[1209,6782,6783],{},[1212,6784,6785,6787],{},[1215,6786,4129],{},[1215,6788,4132],{},[1231,6790,6791,6805,6819,6837,6849,6859,6869],{},[1212,6792,6793,6797],{},[1236,6794,6795],{},[860,6796,4141],{"href":88},[1236,6798,4144,6799,4147,6801,4151,6803,4154],{},[867,6800,2509],{},[860,6802,4150],{"href":88},[867,6804,869],{},[1212,6806,6807,6811],{},[1236,6808,6809],{},[860,6810,4161],{"href":92},[1236,6812,4164,6813,4167,6815,4170,6817],{},[867,6814,985],{},[867,6816,1023],{},[860,6818,4174],{"href":4173},[1212,6820,6821,6825],{},[1236,6822,6823],{},[860,6824,4181],{"href":96},[1236,6826,898,6827,1334,6829,4188,6831,4191,6833,4194,6835,4197],{},[867,6828,1245],{},[867,6830,1280],{},[867,6832,1261],{},[867,6834,1457],{},[860,6836,1012],{"href":96},[1212,6838,6839,6843],{},[1236,6840,6841],{},[860,6842,127],{"href":128},[1236,6844,1656,6845,4208,6847,4211],{},[867,6846,1256],{},[867,6848,1387],{},[1212,6850,6851,6855],{},[1236,6852,6853],{},[860,6854,4218],{"href":136},[1236,6856,4221,6857,4224],{},[860,6858,913],{"href":136},[1212,6860,6861,6865],{},[1236,6862,6863],{},[860,6864,123],{"href":124},[1236,6866,4233,6867,4236],{},[860,6868,123],{"href":124},[1212,6870,6871,6875],{},[1236,6872,6873],{},[860,6874,919],{"href":22},[1236,6876,4245],{},[4247,6878,4249],{},{"title":1054,"searchDepth":1068,"depth":1068,"links":6880},[6881,6882,6885,6893,6898,6904,6908,6909,6910,6911,6912,6917,6918,6919,6920],{"id":949,"depth":1068,"text":950},{"id":1036,"depth":1068,"text":1037,"children":6883},[6884],{"id":1203,"depth":1095,"text":1204},{"id":1328,"depth":1068,"text":1223,"children":6886},[6887,6888,6889,6890,6891,6892],{"id":1340,"depth":1095,"text":1250},{"id":1364,"depth":1095,"text":1253},{"id":1379,"depth":1095,"text":1256},{"id":1397,"depth":1095,"text":905},{"id":1427,"depth":1095,"text":1261},{"id":1728,"depth":1095,"text":4264},{"id":1832,"depth":1068,"text":4266,"children":6894},[6895,6896,6897],{"id":1846,"depth":1095,"text":1847},{"id":2054,"depth":1095,"text":2055},{"id":2160,"depth":1095,"text":2161},{"id":2184,"depth":1068,"text":4272,"children":6899},[6900,6901,6902,6903],{"id":2195,"depth":1095,"text":1847},{"id":2591,"depth":1095,"text":2592},{"id":2628,"depth":1095,"text":2055},{"id":2711,"depth":1095,"text":2712},{"id":2799,"depth":1068,"text":4279,"children":6905},[6906,6907],{"id":2808,"depth":1095,"text":2809},{"id":2848,"depth":1095,"text":1847},{"id":2934,"depth":1068,"text":2935},{"id":3128,"depth":1068,"text":3129},{"id":3171,"depth":1068,"text":3172},{"id":3349,"depth":1068,"text":3350},{"id":3594,"depth":1068,"text":3595,"children":6913},[6914,6915,6916],{"id":3601,"depth":1095,"text":3602},{"id":3646,"depth":1095,"text":3647},{"id":3677,"depth":1095,"text":3678},{"id":3731,"depth":1068,"text":3732},{"id":3834,"depth":1068,"text":3835},{"id":3950,"depth":1068,"text":3951},{"id":4119,"depth":1068,"text":4120},{},{"title":131,"description":4296},1780436282468]