[{"data":1,"prerenderedAt":3106},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fiam":64,"navigation":257,"navLinks_footer":837,"\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin_page":850,"\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin_surround":2163,"\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin":2166},{"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":103,"body":852,"description":2155,"extension":2156,"icon":2157,"meta":2158,"module":2159,"navigation":8,"path":104,"rawbody":2160,"seo":2161,"stem":105,"__hash__":2162},"docs\u002Fdocs\u002Fiam\u002F01.essentials\u002F05.login.md",{"type":853,"value":854,"toc":2137},"minimark",[855,873,876,881,920,926,936,954,962,964,968,1145,1159,1161,1165,1299,1301,1305,1308,1313,1316,1320,1345,1364,1370,1376,1380,1383,1440,1448,1452,1455,1558,1566,1570,1584,1595,1634,1640,1642,1646,1650,1776,1788,1792,1850,1852,1856,1863,1957,1965,1967,1971,1974,2128,2133],[856,857,858,859,866,867,872],"p",{},"The login route authenticates an existing user by email and password. The request passes through three rate limiters (IP, email, and a composite key), a Zod schema validation with XSS detection, a database lookup, and an ",[860,861,865],"a",{"href":862,"rel":863},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FArgon2",[864],"nofollow","Argon2"," password verification. On success, the service issues a refresh token and an access token, optionally re-baselines the device fingerprint, and checks the password against the ",[860,868,871],{"href":869,"rel":870},"https:\u002F\u002Fhaveibeenpwned.com\u002FAPI\u002Fv3#PwnedPasswords",[864],"Have I Been Pwned"," API as an advisory.",[874,875],"hr",{},[877,878,880],"h2",{"id":879},"route","Route",[882,883,884,900],"table",{},[885,886,887],"thead",{},[888,889,890,894,897],"tr",{},[891,892,893],"th",{},"Method",[891,895,896],{},"Path",[891,898,899],{},"Body limit",[901,902,903],"tbody",{},[888,904,905,912,917],{},[906,907,908],"td",{},[909,910,911],"code",{},"POST",[906,913,914],{},[909,915,916],{},"\u002Flogin",[906,918,919],{},"1 KB",[856,921,922],{},[923,924,925],"strong",{},"Middleware chain:",[927,928,933],"pre",{"className":929,"code":931,"language":932},[930],"language-text","validateContentType('application\u002Fjson'), express.json({ limit: '1kb' }), canary_id check, handleLogin\n","text",[909,934,931],{"__ignoreMap":935},"",[856,937,938,939,942,943,945,946,949,950,953],{},"The ",[909,940,941],{},"canary_id"," cookie must be present. This cookie is set by the ",[860,944,399],{"href":404}," on first contact and serves as the device anchor for ",[860,947,948],{"href":96},"anomaly detection",". If the cookie is missing, the route returns ",[909,951,952],{},"400",".",[856,955,956,957,961],{},"See ",[860,958,960],{"href":959},"\u002Fdocs\u002Fiam\u002Froutes","Routes"," for how to mount the authentication router.",[874,963],{},[877,965,967],{"id":966},"request-body","Request body",[927,969,973],{"className":970,"code":971,"language":972,"meta":935,"style":935},"language-ts shiki shiki-themes light-plus light-plus dracula","import { z } from 'zod'\n\nconst loginSchema = z.object({\n  email: z.string().min(10).max(80).email(),     \u002F\u002F XSS-sanitized via makeSafeString\n  password: z.string().min(12).max(64),           \u002F\u002F Must match password policy regex\n}).required()\n","ts",[909,974,975,1009,1015,1042,1095,1133],{"__ignoreMap":935},[976,977,980,984,988,992,995,998,1002,1006],"span",{"class":978,"line":979},"line",1,[976,981,983],{"class":982},"sZ328","import",[976,985,987],{"class":986},"sDd4n"," { ",[976,989,991],{"class":990},"sjsA6","z",[976,993,994],{"class":986}," } ",[976,996,997],{"class":982},"from",[976,999,1001],{"class":1000},"sFkSl"," '",[976,1003,1005],{"class":1004},"sFB1V","zod",[976,1007,1008],{"class":1000},"'\n",[976,1010,1012],{"class":978,"line":1011},2,[976,1013,1014],{"emptyLinePlaceholder":8},"\n",[976,1016,1018,1022,1026,1030,1033,1035,1039],{"class":978,"line":1017},3,[976,1019,1021],{"class":1020},"sl46w","const",[976,1023,1025],{"class":1024},"s3JHE"," loginSchema",[976,1027,1029],{"class":1028},"saOXh"," =",[976,1031,1032],{"class":990}," z",[976,1034,953],{"class":986},[976,1036,1038],{"class":1037},"sHOzp","object",[976,1040,1041],{"class":986},"({\n",[976,1043,1045,1048,1052,1054,1056,1059,1062,1065,1068,1072,1075,1078,1080,1083,1085,1088,1091],{"class":978,"line":1044},4,[976,1046,1047],{"class":990},"  email",[976,1049,1051],{"class":1050},"s34zl",":",[976,1053,1032],{"class":990},[976,1055,953],{"class":986},[976,1057,1058],{"class":1037},"string",[976,1060,1061],{"class":986},"().",[976,1063,1064],{"class":1037},"min",[976,1066,1067],{"class":986},"(",[976,1069,1071],{"class":1070},"spgvN","10",[976,1073,1074],{"class":986},").",[976,1076,1077],{"class":1037},"max",[976,1079,1067],{"class":986},[976,1081,1082],{"class":1070},"80",[976,1084,1074],{"class":986},[976,1086,1087],{"class":1037},"email",[976,1089,1090],{"class":986},"(),     ",[976,1092,1094],{"class":1093},"sghk6","\u002F\u002F XSS-sanitized via makeSafeString\n",[976,1096,1098,1101,1103,1105,1107,1109,1111,1113,1115,1118,1120,1122,1124,1127,1130],{"class":978,"line":1097},5,[976,1099,1100],{"class":990},"  password",[976,1102,1051],{"class":1050},[976,1104,1032],{"class":990},[976,1106,953],{"class":986},[976,1108,1058],{"class":1037},[976,1110,1061],{"class":986},[976,1112,1064],{"class":1037},[976,1114,1067],{"class":986},[976,1116,1117],{"class":1070},"12",[976,1119,1074],{"class":986},[976,1121,1077],{"class":1037},[976,1123,1067],{"class":986},[976,1125,1126],{"class":1070},"64",[976,1128,1129],{"class":986},"),           ",[976,1131,1132],{"class":1093},"\u002F\u002F Must match password policy regex\n",[976,1134,1136,1139,1142],{"class":978,"line":1135},6,[976,1137,1138],{"class":986},"}).",[976,1140,1141],{"class":1037},"required",[976,1143,1144],{"class":986},"()\n",[856,1146,1147,1148,1151,1152,1154,1155,1158],{},"The password regex is the same one used during ",[860,1149,1150],{"href":100},"signup",": at least one lowercase, one uppercase, one digit, one special character, no whitespace. The ",[909,1153,1087],{}," field passes through ",[909,1156,1157],{},"makeSafeString"," for XSS detection.",[874,1160],{},[877,1162,1164],{"id":1163},"the-authentication-pipeline","The authentication pipeline",[1166,1167,1169,1174,1187,1191,1202,1206,1226,1230,1233,1237,1244,1248,1280,1284],"steps",{"level":1168},"4",[1170,1171,1173],"h4",{"id":1172},"content-type-check","Content-Type check",[856,1175,938,1176,1179,1180,1183,1184,953],{},[909,1177,1178],{},"validateContentType"," middleware rejects requests that are not ",[909,1181,1182],{},"application\u002Fjson"," with ",[909,1185,1186],{},"403",[1170,1188,1190],{"id":1189},"rate-limiting-ip","Rate limiting (IP)",[856,1192,1193,1194,1197,1198,1201],{},"An IP-based rate limiter allows 15 points per 24 hours. After exhaustion, the IP is blocked for 3 hours. A consecutive-violation cache escalates the block duration if the same IP is repeatedly rate-limited. See ",[860,1195,147],{"href":1196},"\u002Fdocs\u002Fiam\u002Frate-limiting"," for the ",[909,1199,1200],{},"guard()"," architecture.",[1170,1203,1205],{"id":1204},"schema-validation","Schema validation",[856,1207,1208,1209,1211,1212,1215,1216,1218,1219,1222,1223,1225],{},"The request body is validated against the Zod schema. If the ",[909,1210,1157],{}," check detects HTML tags in the email field, ",[909,1213,1214],{},"handleXSS"," bans the IP through the ",[860,1217,399],{"href":35},", marks the visitor as a bot, and returns ",[909,1220,1221],{},"403 { banned: true }",". If validation fails for other reasons, the controller returns ",[909,1224,952],{}," with Zod errors.",[1170,1227,1229],{"id":1228},"rate-limiting-email","Rate limiting (email)",[856,1231,1232],{},"An email-keyed limiter allows 5 points per 24 hours, blocking for 5 hours on exhaustion. This prevents brute force against a specific account.",[1170,1234,1236],{"id":1235},"rate-limiting-composite-key","Rate limiting (composite key)",[856,1238,1239,1240,1243],{},"A union limiter keyed on ",[909,1241,1242],{},"ip + email"," combines a burst guard (1 point per second, blocks 30 minutes) with a slow guard (5 points per hour, blocks 30 minutes). This catches distributed attacks targeting a single account from rotating IPs.",[1170,1245,1247],{"id":1246},"database-lookup","Database lookup",[856,1249,1250,1251,1254,1255,1258,1259,1262,1263,1266,1267,1270,1271,1274,1275,953],{},"The controller queries the ",[909,1252,1253],{},"users"," table for a row matching the email with ",[909,1256,1257],{},"active_user = 1",". The query returns the ",[909,1260,1261],{},"visitor_id",", ",[909,1264,1265],{},"password_hash",", and ",[909,1268,1269],{},"id"," columns. If no active user is found, the controller returns ",[909,1272,1273],{},"401 { error: 'Invalid email or password' }",". The same generic message is returned for both \"user not found\" and \"wrong password\" to prevent ",[860,1276,1279],{"href":1277,"rel":1278},"https:\u002F\u002Fowasp.org\u002Fwww-project-web-security-testing-guide\u002Flatest\u002F4-Web_Application_Security_Testing\u002F03-Identity_Management_Testing\u002F04-Testing_for_Account_Enumeration_and_Guessable_User_Account",[864],"user enumeration",[1170,1281,1283],{"id":1282},"password-verification","Password verification",[856,1285,1286,1287,1290,1291,1294,1295,1298],{},"The stored Argon2 hash is verified against the provided password using the same pepper from the ",[860,1288,1289],{"href":238},"configuration",". The ",[909,1292,1293],{},"verifyPassword"," function returns a boolean. On failure, the controller returns ",[909,1296,1297],{},"401"," with the same generic message.",[874,1300],{},[877,1302,1304],{"id":1303},"on-successful-authentication","On successful authentication",[856,1306,1307],{},"After the password is verified, the controller performs several actions before sending the response.",[1309,1310,1312],"h3",{"id":1311},"rate-limiter-reset","Rate limiter reset",[856,1314,1315],{},"All three consecutive-violation caches (IP, email, composite) are cleared for the successful keys, and the union limiter counters for the composite key are reset in the database. This prevents a legitimate user from being locked out after a few typos followed by a correct login.",[1309,1317,1319],{"id":1318},"device-trust","Device trust",[856,1321,1322,1323,1326,1327,1330,1331,1333,1334,1336,1337,1340,1341,1344],{},"If ",[909,1324,1325],{},"trustUserDeviceOnAuth"," is ",[909,1328,1329],{},"true"," in the configuration and the ",[860,1332,399],{"href":35}," assigned a new ",[909,1335,1261],{}," for this request (",[909,1338,1339],{},"req.newVisitorId"," is set), the controller calls ",[909,1342,1343],{},"trustVisitor()",". This function:",[1346,1347,1348,1357],"ol",{},[1349,1350,1351,1352,1354,1355],"li",{},"Updates the user's ",[909,1353,1261],{}," foreign key to point at the visitor row associated with the current ",[909,1356,941],{},[1349,1358,1359,1360,1363],{},"Calls ",[909,1361,1362],{},"updateVisitors()"," from the Bot Detector to overwrite the visitor's fingerprint fields (geo, UA, device metadata) with the current request's data",[856,1365,1366,1367,1369],{},"The effect is that the ",[860,1368,948],{"href":96}," engine compares future requests against the device the user most recently logged in from, rather than the original device. Without this option, users who switch devices accumulate canary-cookie mismatches and trigger MFA challenges.",[856,1371,1372,1373,1375],{},"The same device-trust logic runs in the ",[860,1374,111],{"href":112}," flow.",[1309,1377,1379],{"id":1378},"token-issuance","Token issuance",[856,1381,1382],{},"Two tokens are generated:",[882,1384,1385,1398],{},[885,1386,1387],{},[888,1388,1389,1392,1395],{},[891,1390,1391],{},"Token",[891,1393,1394],{},"Function",[891,1396,1397],{},"Description",[901,1399,1400,1417],{},[888,1401,1402,1405,1410],{},[906,1403,1404],{},"Refresh token",[906,1406,1407],{},[909,1408,1409],{},"generateRefreshToken(refresh_ttl, userId)",[906,1411,1412,1413,1416],{},"64 random bytes, hex encoded, SHA-256 hashed and stored in the ",[909,1414,1415],{},"refresh_tokens"," table",[888,1418,1419,1422,1427],{},[906,1420,1421],{},"Access token",[906,1423,1424],{},[909,1425,1426],{},"generateAccessToken({ id, visitor_id, jti })",[906,1428,1429,1430,1433,1434,1436,1437],{},"Signed JWT (HS512) with a random ",[909,1431,1432],{},"jti",", the user's ",[909,1435,1261],{},", and any configured ",[909,1438,1439],{},"roles",[856,1441,956,1442,1444,1445,1447],{},[860,1443,91],{"href":92}," and ",[860,1446,87],{"href":88}," for the full token lifecycle.",[1309,1449,1451],{"id":1450},"cookie-setting","Cookie setting",[856,1453,1454],{},"Two cookies are set alongside the JSON response:",[882,1456,1457,1492],{},[885,1458,1459],{},[888,1460,1461,1464,1467,1472,1477,1482,1487],{},[891,1462,1463],{},"Cookie",[891,1465,1466],{},"Value",[891,1468,1469],{},[909,1470,1471],{},"sameSite",[891,1473,1474],{},[909,1475,1476],{},"httpOnly",[891,1478,1479],{},[909,1480,1481],{},"secure",[891,1483,1484],{},[909,1485,1486],{},"path",[891,1488,1489],{},[909,1490,1491],{},"domain",[901,1493,1494,1528],{},[888,1495,1496,1501,1507,1512,1516,1520,1525],{},[906,1497,1498],{},[909,1499,1500],{},"iat",[906,1502,1503,1506],{},[909,1504,1505],{},"Date.now()"," as string",[906,1508,1509],{},[909,1510,1511],{},"strict",[906,1513,1514],{},[909,1515,1329],{},[906,1517,1518],{},[909,1519,1329],{},[906,1521,1522],{},[909,1523,1524],{},"\u002F",[906,1526,1527],{},"—",[888,1529,1530,1535,1538,1542,1546,1550,1552],{},[906,1531,1532],{},[909,1533,1534],{},"session",[906,1536,1537],{},"Raw refresh token",[906,1539,1540],{},[909,1541,1511],{},[906,1543,1544],{},[909,1545,1329],{},[906,1547,1548],{},[909,1549,1329],{},[906,1551,1527],{},[906,1553,1554,1555],{},"From ",[909,1556,1557],{},"jwt.refresh_tokens.domain",[856,1559,938,1560,1562,1563,1565],{},[909,1561,1500],{}," cookie tells the frontend when the access token was issued. The ",[909,1564,1534],{}," cookie carries the raw refresh token, scoped to the configured domain.",[1309,1567,1569],{"id":1568},"breach-advisory","Breach advisory",[856,1571,1572,1573,1576,1577,1580,1581,1583],{},"After tokens are issued and cookies are set, the controller checks the password against the ",[860,1574,871],{"href":869,"rel":1575},[864]," Passwords API using the same ",[909,1578,1579],{},"isPwned"," function described in the ",[860,1582,1150],{"href":100}," docs.",[856,1585,1586,1587,1590,1591,1594],{},"On login, a breached password ",[923,1588,1589],{},"does not block authentication",". Instead, a ",[909,1592,1593],{},"breached"," field is included in the JSON response with an advisory message. The client can display this message to encourage the user to change their password.",[927,1596,1599],{"className":1597,"code":1598,"language":5,"meta":935,"style":935},"language-json shiki shiki-themes light-plus light-plus dracula","{\n  \"breached\": \"Our system identified this password in 12,345 data breaches. Please consider changing your password.\"\n}\n",[909,1600,1601,1606,1629],{"__ignoreMap":935},[976,1602,1603],{"class":978,"line":979},[976,1604,1605],{"class":986},"{\n",[976,1607,1608,1612,1615,1618,1620,1623,1626],{"class":978,"line":1011},[976,1609,1611],{"class":1610},"saJyd","  \"",[976,1613,1593],{"class":1614},"s_W10",[976,1616,1617],{"class":1610},"\"",[976,1619,1051],{"class":1028},[976,1621,1622],{"class":1000}," \"",[976,1624,1625],{"class":1004},"Our system identified this password in 12,345 data breaches. Please consider changing your password.",[976,1627,1628],{"class":1000},"\"\n",[976,1630,1631],{"class":978,"line":1017},[976,1632,1633],{"class":986},"}\n",[1635,1636,1637],"tip",{},[856,1638,1639],{},"The difference between login and signup is intentional. Blocking a breached password during signup prevents weak accounts from being created. Blocking during login would lock out existing users who have not changed their password yet. The advisory approach nudges users toward better passwords without denying access.",[874,1641],{},[877,1643,1645],{"id":1644},"response","Response",[1309,1647,1649],{"id":1648},"success-200","Success (200)",[927,1651,1653],{"className":1597,"code":1652,"language":5,"meta":935,"style":935},"{\n  \"ok\": true,\n  \"receivedAt\": \"2025-01-15T10:30:00.000Z\",\n  \"accessToken\": \"\u003Csigned JWT>\",\n  \"banned\": false,\n  \"accessIat\": \"1705312200000\",\n  \"breached\": \"...\"\n}\n",[909,1654,1655,1659,1677,1697,1717,1733,1753,1771],{"__ignoreMap":935},[976,1656,1657],{"class":978,"line":979},[976,1658,1605],{"class":986},[976,1660,1661,1663,1666,1668,1670,1674],{"class":978,"line":1011},[976,1662,1611],{"class":1610},[976,1664,1665],{"class":1614},"ok",[976,1667,1617],{"class":1610},[976,1669,1051],{"class":1028},[976,1671,1673],{"class":1672},"sjR7W"," true",[976,1675,1676],{"class":986},",\n",[976,1678,1679,1681,1684,1686,1688,1690,1693,1695],{"class":978,"line":1017},[976,1680,1611],{"class":1610},[976,1682,1683],{"class":1614},"receivedAt",[976,1685,1617],{"class":1610},[976,1687,1051],{"class":1028},[976,1689,1622],{"class":1000},[976,1691,1692],{"class":1004},"2025-01-15T10:30:00.000Z",[976,1694,1617],{"class":1000},[976,1696,1676],{"class":986},[976,1698,1699,1701,1704,1706,1708,1710,1713,1715],{"class":978,"line":1044},[976,1700,1611],{"class":1610},[976,1702,1703],{"class":1614},"accessToken",[976,1705,1617],{"class":1610},[976,1707,1051],{"class":1028},[976,1709,1622],{"class":1000},[976,1711,1712],{"class":1004},"\u003Csigned JWT>",[976,1714,1617],{"class":1000},[976,1716,1676],{"class":986},[976,1718,1719,1721,1724,1726,1728,1731],{"class":978,"line":1097},[976,1720,1611],{"class":1610},[976,1722,1723],{"class":1614},"banned",[976,1725,1617],{"class":1610},[976,1727,1051],{"class":1028},[976,1729,1730],{"class":1672}," false",[976,1732,1676],{"class":986},[976,1734,1735,1737,1740,1742,1744,1746,1749,1751],{"class":978,"line":1135},[976,1736,1611],{"class":1610},[976,1738,1739],{"class":1614},"accessIat",[976,1741,1617],{"class":1610},[976,1743,1051],{"class":1028},[976,1745,1622],{"class":1000},[976,1747,1748],{"class":1004},"1705312200000",[976,1750,1617],{"class":1000},[976,1752,1676],{"class":986},[976,1754,1756,1758,1760,1762,1764,1766,1769],{"class":978,"line":1755},7,[976,1757,1611],{"class":1610},[976,1759,1593],{"class":1614},[976,1761,1617],{"class":1610},[976,1763,1051],{"class":1028},[976,1765,1622],{"class":1000},[976,1767,1768],{"class":1004},"...",[976,1770,1628],{"class":1000},[976,1772,1774],{"class":978,"line":1773},8,[976,1775,1633],{"class":986},[856,1777,938,1778,1780,1781,1783,1784,1787],{},[909,1779,1593],{}," field is only present when the password is found in a data breach. The ",[909,1782,1723],{}," field is always ",[909,1785,1786],{},"false"," on success (a banned client never reaches this point).",[1309,1789,1791],{"id":1790},"error-responses","Error responses",[882,1793,1794,1804],{},[885,1795,1796],{},[888,1797,1798,1801],{},[891,1799,1800],{},"Status",[891,1802,1803],{},"Condition",[901,1805,1806,1818,1827,1840],{},[888,1807,1808,1812],{},[906,1809,1810],{},[909,1811,952],{},[906,1813,1814,1815,1817],{},"Missing ",[909,1816,941],{}," cookie, empty body, schema validation failure",[888,1819,1820,1824],{},[906,1821,1822],{},[909,1823,1297],{},[906,1825,1826],{},"User not found or wrong password",[888,1828,1829,1833],{},[906,1830,1831],{},[909,1832,1186],{},[906,1834,1835,1836,1839],{},"HTML\u002FXSS detected in input, or ",[909,1837,1838],{},"Content-Type"," mismatch",[888,1841,1842,1847],{},[906,1843,1844],{},[909,1845,1846],{},"429",[906,1848,1849],{},"Rate limit exceeded on any of the three limiters",[874,1851],{},[877,1853,1855],{"id":1854},"rate-limiter-reference","Rate limiter reference",[856,1857,1858,1859,1862],{},"All login rate limiters are configurable under ",[909,1860,1861],{},"rate_limiters"," in the configuration object. The defaults are:",[882,1864,1865,1884],{},[885,1866,1867],{},[888,1868,1869,1872,1875,1878,1881],{},[891,1870,1871],{},"Limiter",[891,1873,1874],{},"Key",[891,1876,1877],{},"Points",[891,1879,1880],{},"Window",[891,1882,1883],{},"Block duration",[901,1885,1886,1905,1922,1940],{},[888,1887,1888,1891,1896,1899,1902],{},[906,1889,1890],{},"IP",[906,1892,1893],{},[909,1894,1895],{},"req.ip",[906,1897,1898],{},"15 \u002F 24 hours",[906,1900,1901],{},"24 h",[906,1903,1904],{},"3 h",[888,1906,1907,1910,1914,1917,1919],{},[906,1908,1909],{},"Email",[906,1911,1912],{},[909,1913,1087],{},[906,1915,1916],{},"5 \u002F 24 hours",[906,1918,1901],{},[906,1920,1921],{},"5 h",[888,1923,1924,1927,1931,1934,1937],{},[906,1925,1926],{},"Composite burst",[906,1928,1929],{},[909,1930,1242],{},[906,1932,1933],{},"1 \u002F 1 second",[906,1935,1936],{},"1 s",[906,1938,1939],{},"30 min",[888,1941,1942,1945,1949,1952,1955],{},[906,1943,1944],{},"Composite slow",[906,1946,1947],{},[909,1948,1242],{},[906,1950,1951],{},"5 \u002F 1 hour",[906,1953,1954],{},"1 h",[906,1956,1939],{},[856,1958,938,1959,1961,1962,1964],{},[909,1960,1200],{}," function also uses in-memory LRU caches (max 2000 entries, 24-hour TTL for IP and email, 1-hour TTL for the 429 composite cache) to track consecutive violations. See ",[860,1963,147],{"href":1196}," for the escalation mechanics.",[874,1966],{},[877,1968,1970],{"id":1969},"configuration-reference","Configuration reference",[856,1972,1973],{},"Options that affect the login flow:",[882,1975,1976,1994],{},[885,1977,1978],{},[888,1979,1980,1983,1986,1989,1992],{},[891,1981,1982],{},"Option",[891,1984,1985],{},"Location",[891,1987,1988],{},"Type",[891,1990,1991],{},"Default",[891,1993,1397],{},[901,1995,1996,2017,2035,2057,2079,2108],{},[888,1997,1998,2002,2005,2010,2014],{},[906,1999,2000],{},[909,2001,1325],{},[906,2003,2004],{},"Root config",[906,2006,2007],{},[909,2008,2009],{},"boolean",[906,2011,2012],{},[909,2013,1786],{},[906,2015,2016],{},"Whether login re-baselines the visitor fingerprint to the current device",[888,2018,2019,2024,2026,2030,2032],{},[906,2020,2021],{},[909,2022,2023],{},"password.pepper",[906,2025,2004],{},[906,2027,2028],{},[909,2029,1058],{},[906,2031,1527],{},[906,2033,2034],{},"Server-side Argon2 pepper (must match the value used during signup)",[888,2036,2037,2042,2047,2052,2054],{},[906,2038,2039],{},[909,2040,2041],{},"jwt.refresh_tokens.refresh_ttl",[906,2043,2044],{},[909,2045,2046],{},"jwt",[906,2048,2049],{},[909,2050,2051],{},"number",[906,2053,1527],{},[906,2055,2056],{},"Refresh token lifetime in milliseconds",[888,2058,2059,2063,2067,2071,2073],{},[906,2060,2061],{},[909,2062,1557],{},[906,2064,2065],{},[909,2066,2046],{},[906,2068,2069],{},[909,2070,1058],{},[906,2072,1527],{},[906,2074,2075,2076,2078],{},"Cookie domain for the ",[909,2077,1534],{}," cookie",[888,2080,2081,2086,2090,2094,2099],{},[906,2082,2083],{},[909,2084,2085],{},"jwt.access_tokens.expiresIn",[906,2087,2088],{},[909,2089,2046],{},[906,2091,2092],{},[909,2093,1058],{},[906,2095,2096],{},[909,2097,2098],{},"'15m'",[906,2100,2101,2102,1262,2104,2107],{},"Access token JWT expiry (e.g. ",[909,2103,2098],{},[909,2105,2106],{},"'1h'",")",[888,2109,2110,2115,2119,2123,2125],{},[906,2111,2112],{},[909,2113,2114],{},"jwt.jwt_secret_key",[906,2116,2117],{},[909,2118,2046],{},[906,2120,2121],{},[909,2122,1058],{},[906,2124,1527],{},[906,2126,2127],{},"Secret key for signing access tokens (HS512)",[856,2129,956,2130,2132],{},[860,2131,237],{"href":238}," for the full schema reference.",[2134,2135,2136],"style",{},"html pre.shiki code .sZ328, html code.shiki .sZ328{--shiki-light:#AF00DB;--shiki-default:#AF00DB;--shiki-dark:#FF79C6}html pre.shiki code .sDd4n, html code.shiki .sDd4n{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#F8F8F2}html pre.shiki code .sjsA6, html code.shiki .sjsA6{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#F8F8F2}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 pre.shiki code .sl46w, html code.shiki .sl46w{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#FF79C6}html pre.shiki code .s3JHE, html code.shiki .s3JHE{--shiki-light:#0070C1;--shiki-default:#0070C1;--shiki-dark:#F8F8F2}html pre.shiki code .saOXh, html code.shiki .saOXh{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#FF79C6}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}html pre.shiki code .s34zl, html code.shiki .s34zl{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#FF79C6}html pre.shiki code .spgvN, html code.shiki .spgvN{--shiki-light:#098658;--shiki-default:#098658;--shiki-dark:#BD93F9}html pre.shiki code .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}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 .sjR7W, html code.shiki .sjR7W{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#BD93F9}",{"title":935,"searchDepth":1011,"depth":1011,"links":2138},[2139,2140,2141,2142,2149,2153,2154],{"id":879,"depth":1011,"text":880},{"id":966,"depth":1011,"text":967},{"id":1163,"depth":1011,"text":1164},{"id":1303,"depth":1011,"text":1304,"children":2143},[2144,2145,2146,2147,2148],{"id":1311,"depth":1017,"text":1312},{"id":1318,"depth":1017,"text":1319},{"id":1378,"depth":1017,"text":1379},{"id":1450,"depth":1017,"text":1451},{"id":1568,"depth":1017,"text":1569},{"id":1644,"depth":1011,"text":1645,"children":2150},[2151,2152],{"id":1648,"depth":1017,"text":1649},{"id":1790,"depth":1017,"text":1791},{"id":1854,"depth":1011,"text":1855},{"id":1969,"depth":1011,"text":1970},"How the IAM service authenticates users with email and password, the rate limiting and validation pipeline, device trust, token issuance, and the breach advisory system.","md","i-lucide-log-in",{},null,"---\ntitle: Login\ndescription: How the IAM service authenticates users with email and password, the rate limiting and validation pipeline, device trust, token issuance, and the breach advisory system.\nicon: i-lucide-log-in\n---\n\nThe login route authenticates an existing user by email and password. The request passes through three rate limiters (IP, email, and a composite key), a Zod schema validation with XSS detection, a database lookup, and an [Argon2](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FArgon2) password verification. On success, the service issues a refresh token and an access token, optionally re-baselines the device fingerprint, and checks the password against the [Have I Been Pwned](https:\u002F\u002Fhaveibeenpwned.com\u002FAPI\u002Fv3#PwnedPasswords) API as an advisory.\n\n---\n\n## Route\n\n| Method | Path | Body limit |\n|---|---|---|\n| `POST` | `\u002Flogin` | 1 KB |\n\n**Middleware chain:**\n\n```\nvalidateContentType('application\u002Fjson'), express.json({ limit: '1kb' }), canary_id check, handleLogin\n```\n\nThe `canary_id` cookie must be present. This cookie is set by the [Bot Detector](\u002Fdocs\u002Fbot-detection\u002Fgetting-started) on first contact and serves as the device anchor for [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies). If the cookie is missing, the route returns `400`.\n\nSee [Routes](\u002Fdocs\u002Fiam\u002Froutes) for how to mount the authentication router.\n\n---\n\n## Request body\n\n```ts\nimport { z } from 'zod'\n\nconst loginSchema = z.object({\n  email: z.string().min(10).max(80).email(),     \u002F\u002F XSS-sanitized via makeSafeString\n  password: z.string().min(12).max(64),           \u002F\u002F Must match password policy regex\n}).required()\n```\n\nThe password regex is the same one used during [signup](\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup): at least one lowercase, one uppercase, one digit, one special character, no whitespace. The `email` field passes through `makeSafeString` for XSS detection.\n\n---\n\n## The authentication pipeline\n\n::steps{level=\"4\"}\n#### Content-Type check\n\nThe `validateContentType` middleware rejects requests that are not `application\u002Fjson` with `403`.\n\n#### Rate limiting (IP)\n\nAn IP-based rate limiter allows 15 points per 24 hours. After exhaustion, the IP is blocked for 3 hours. A consecutive-violation cache escalates the block duration if the same IP is repeatedly rate-limited. See [Rate Limiting](\u002Fdocs\u002Fiam\u002Frate-limiting) for the `guard()` architecture.\n\n#### Schema validation\n\nThe request body is validated against the Zod schema. If the `makeSafeString` check detects HTML tags in the email field, `handleXSS` bans the IP through the [Bot Detector](\u002Fdocs\u002Fbot-detection), marks the visitor as a bot, and returns `403 { banned: true }`. If validation fails for other reasons, the controller returns `400` with Zod errors.\n\n#### Rate limiting (email)\n\nAn email-keyed limiter allows 5 points per 24 hours, blocking for 5 hours on exhaustion. This prevents brute force against a specific account.\n\n#### Rate limiting (composite key)\n\nA union limiter keyed on `ip + email` combines a burst guard (1 point per second, blocks 30 minutes) with a slow guard (5 points per hour, blocks 30 minutes). This catches distributed attacks targeting a single account from rotating IPs.\n\n#### Database lookup\n\nThe controller queries the `users` table for a row matching the email with `active_user = 1`. The query returns the `visitor_id`, `password_hash`, and `id` columns. If no active user is found, the controller returns `401 { error: 'Invalid email or password' }`. The same generic message is returned for both \"user not found\" and \"wrong password\" to prevent [user enumeration](https:\u002F\u002Fowasp.org\u002Fwww-project-web-security-testing-guide\u002Flatest\u002F4-Web_Application_Security_Testing\u002F03-Identity_Management_Testing\u002F04-Testing_for_Account_Enumeration_and_Guessable_User_Account).\n\n#### Password verification\n\nThe stored Argon2 hash is verified against the provided password using the same pepper from the [configuration](\u002Fdocs\u002Fiam\u002Fconfiguration). The `verifyPassword` function returns a boolean. On failure, the controller returns `401` with the same generic message.\n::\n\n---\n\n## On successful authentication\n\nAfter the password is verified, the controller performs several actions before sending the response.\n\n### Rate limiter reset\n\nAll three consecutive-violation caches (IP, email, composite) are cleared for the successful keys, and the union limiter counters for the composite key are reset in the database. This prevents a legitimate user from being locked out after a few typos followed by a correct login.\n\n### Device trust\n\nIf `trustUserDeviceOnAuth` is `true` in the configuration and the [Bot Detector](\u002Fdocs\u002Fbot-detection) assigned a new `visitor_id` for this request (`req.newVisitorId` is set), the controller calls `trustVisitor()`. This function:\n\n1. Updates the user's `visitor_id` foreign key to point at the visitor row associated with the current `canary_id`\n2. Calls `updateVisitors()` from the Bot Detector to overwrite the visitor's fingerprint fields (geo, UA, device metadata) with the current request's data\n\nThe effect is that the [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies) engine compares future requests against the device the user most recently logged in from, rather than the original device. Without this option, users who switch devices accumulate canary-cookie mismatches and trigger MFA challenges.\n\nThe same device-trust logic runs in the [OAuth](\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth) flow.\n\n### Token issuance\n\nTwo tokens are generated:\n\n| Token | Function | Description |\n|---|---|---|\n| Refresh token | `generateRefreshToken(refresh_ttl, userId)` | 64 random bytes, hex encoded, SHA-256 hashed and stored in the `refresh_tokens` table |\n| Access token | `generateAccessToken({ id, visitor_id, jti })` | Signed JWT (HS512) with a random `jti`, the user's `visitor_id`, and any configured `roles` |\n\nSee [Refresh Tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens) and [Access Tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens) for the full token lifecycle.\n\n### Cookie setting\n\nTwo cookies are set alongside the JSON response:\n\n| Cookie | Value | `sameSite` | `httpOnly` | `secure` | `path` | `domain` |\n|---|---|---|---|---|---|---|\n| `iat` | `Date.now()` as string | `strict` | `true` | `true` | `\u002F` | — |\n| `session` | Raw refresh token | `strict` | `true` | `true` | — | From `jwt.refresh_tokens.domain` |\n\nThe `iat` cookie tells the frontend when the access token was issued. The `session` cookie carries the raw refresh token, scoped to the configured domain.\n\n### Breach advisory\n\nAfter tokens are issued and cookies are set, the controller checks the password against the [Have I Been Pwned](https:\u002F\u002Fhaveibeenpwned.com\u002FAPI\u002Fv3#PwnedPasswords) Passwords API using the same `isPwned` function described in the [signup](\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup) docs.\n\nOn login, a breached password **does not block authentication**. Instead, a `breached` field is included in the JSON response with an advisory message. The client can display this message to encourage the user to change their password.\n\n```json\n{\n  \"breached\": \"Our system identified this password in 12,345 data breaches. Please consider changing your password.\"\n}\n```\n\n::tip\nThe difference between login and signup is intentional. Blocking a breached password during signup prevents weak accounts from being created. Blocking during login would lock out existing users who have not changed their password yet. The advisory approach nudges users toward better passwords without denying access.\n::\n\n---\n\n## Response\n\n### Success (200)\n\n```json\n{\n  \"ok\": true,\n  \"receivedAt\": \"2025-01-15T10:30:00.000Z\",\n  \"accessToken\": \"\u003Csigned JWT>\",\n  \"banned\": false,\n  \"accessIat\": \"1705312200000\",\n  \"breached\": \"...\"\n}\n```\n\nThe `breached` field is only present when the password is found in a data breach. The `banned` field is always `false` on success (a banned client never reaches this point).\n\n### Error responses\n\n| Status | Condition |\n|---|---|\n| `400` | Missing `canary_id` cookie, empty body, schema validation failure |\n| `401` | User not found or wrong password |\n| `403` | HTML\u002FXSS detected in input, or `Content-Type` mismatch |\n| `429` | Rate limit exceeded on any of the three limiters |\n\n---\n\n## Rate limiter reference\n\nAll login rate limiters are configurable under `rate_limiters` in the configuration object. The defaults are:\n\n| Limiter | Key | Points | Window | Block duration |\n|---|---|---|---|---|\n| IP | `req.ip` | 15 \u002F 24 hours | 24 h | 3 h |\n| Email | `email` | 5 \u002F 24 hours | 24 h | 5 h |\n| Composite burst | `ip + email` | 1 \u002F 1 second | 1 s | 30 min |\n| Composite slow | `ip + email` | 5 \u002F 1 hour | 1 h | 30 min |\n\nThe `guard()` function also uses in-memory LRU caches (max 2000 entries, 24-hour TTL for IP and email, 1-hour TTL for the 429 composite cache) to track consecutive violations. See [Rate Limiting](\u002Fdocs\u002Fiam\u002Frate-limiting) for the escalation mechanics.\n\n---\n\n## Configuration reference\n\nOptions that affect the login flow:\n\n| Option | Location | Type | Default | Description |\n|---|---|---|---|---|\n| `trustUserDeviceOnAuth` | Root config | `boolean` | `false` | Whether login re-baselines the visitor fingerprint to the current device |\n| `password.pepper` | Root config | `string` | — | Server-side Argon2 pepper (must match the value used during signup) |\n| `jwt.refresh_tokens.refresh_ttl` | `jwt` | `number` | — | Refresh token lifetime in milliseconds |\n| `jwt.refresh_tokens.domain` | `jwt` | `string` | — | Cookie domain for the `session` cookie |\n| `jwt.access_tokens.expiresIn` | `jwt` | `string` | `'15m'` | Access token JWT expiry (e.g. `'15m'`, `'1h'`) |\n| `jwt.jwt_secret_key` | `jwt` | `string` | — | Secret key for signing access tokens (HS512) |\n\nSee [Configuration](\u002Fdocs\u002Fiam\u002Fconfiguration) for the full schema reference.",{"title":103,"description":2155},"43jB7ztwBW8GIo1p9Ro9ZTr1nIdlRhCKw-uSva-fIJs",[2164,2165],{"title":99,"path":100,"stem":101,"children":-1},{"title":107,"path":108,"stem":109,"children":-1},{"id":851,"title":103,"body":2167,"description":2155,"extension":2156,"icon":2157,"meta":3104,"module":2159,"navigation":8,"path":104,"rawbody":2160,"seo":3105,"stem":105,"__hash__":2162},{"type":853,"value":2168,"toc":3086},[2169,2177,2179,2181,2207,2211,2216,2226,2230,2232,2234,2352,2360,2362,2364,2437,2439,2441,2443,2445,2447,2449,2463,2475,2479,2483,2485,2487,2529,2535,2537,2539,2625,2631,2633,2642,2648,2676,2680,2682,2684,2686,2796,2804,2806,2854,2856,2858,2862,2936,2942,2944,2946,2948,3080,3084],[856,2170,858,2171,866,2174,872],{},[860,2172,865],{"href":862,"rel":2173},[864],[860,2175,871],{"href":869,"rel":2176},[864],[874,2178],{},[877,2180,880],{"id":879},[882,2182,2183,2193],{},[885,2184,2185],{},[888,2186,2187,2189,2191],{},[891,2188,893],{},[891,2190,896],{},[891,2192,899],{},[901,2194,2195],{},[888,2196,2197,2201,2205],{},[906,2198,2199],{},[909,2200,911],{},[906,2202,2203],{},[909,2204,916],{},[906,2206,919],{},[856,2208,2209],{},[923,2210,925],{},[927,2212,2214],{"className":2213,"code":931,"language":932},[930],[909,2215,931],{"__ignoreMap":935},[856,2217,938,2218,942,2220,945,2222,949,2224,953],{},[909,2219,941],{},[860,2221,399],{"href":404},[860,2223,948],{"href":96},[909,2225,952],{},[856,2227,956,2228,961],{},[860,2229,960],{"href":959},[874,2231],{},[877,2233,967],{"id":966},[927,2235,2236],{"className":970,"code":971,"language":972,"meta":935,"style":935},[909,2237,2238,2256,2260,2276,2312,2344],{"__ignoreMap":935},[976,2239,2240,2242,2244,2246,2248,2250,2252,2254],{"class":978,"line":979},[976,2241,983],{"class":982},[976,2243,987],{"class":986},[976,2245,991],{"class":990},[976,2247,994],{"class":986},[976,2249,997],{"class":982},[976,2251,1001],{"class":1000},[976,2253,1005],{"class":1004},[976,2255,1008],{"class":1000},[976,2257,2258],{"class":978,"line":1011},[976,2259,1014],{"emptyLinePlaceholder":8},[976,2261,2262,2264,2266,2268,2270,2272,2274],{"class":978,"line":1017},[976,2263,1021],{"class":1020},[976,2265,1025],{"class":1024},[976,2267,1029],{"class":1028},[976,2269,1032],{"class":990},[976,2271,953],{"class":986},[976,2273,1038],{"class":1037},[976,2275,1041],{"class":986},[976,2277,2278,2280,2282,2284,2286,2288,2290,2292,2294,2296,2298,2300,2302,2304,2306,2308,2310],{"class":978,"line":1044},[976,2279,1047],{"class":990},[976,2281,1051],{"class":1050},[976,2283,1032],{"class":990},[976,2285,953],{"class":986},[976,2287,1058],{"class":1037},[976,2289,1061],{"class":986},[976,2291,1064],{"class":1037},[976,2293,1067],{"class":986},[976,2295,1071],{"class":1070},[976,2297,1074],{"class":986},[976,2299,1077],{"class":1037},[976,2301,1067],{"class":986},[976,2303,1082],{"class":1070},[976,2305,1074],{"class":986},[976,2307,1087],{"class":1037},[976,2309,1090],{"class":986},[976,2311,1094],{"class":1093},[976,2313,2314,2316,2318,2320,2322,2324,2326,2328,2330,2332,2334,2336,2338,2340,2342],{"class":978,"line":1097},[976,2315,1100],{"class":990},[976,2317,1051],{"class":1050},[976,2319,1032],{"class":990},[976,2321,953],{"class":986},[976,2323,1058],{"class":1037},[976,2325,1061],{"class":986},[976,2327,1064],{"class":1037},[976,2329,1067],{"class":986},[976,2331,1117],{"class":1070},[976,2333,1074],{"class":986},[976,2335,1077],{"class":1037},[976,2337,1067],{"class":986},[976,2339,1126],{"class":1070},[976,2341,1129],{"class":986},[976,2343,1132],{"class":1093},[976,2345,2346,2348,2350],{"class":978,"line":1135},[976,2347,1138],{"class":986},[976,2349,1141],{"class":1037},[976,2351,1144],{"class":986},[856,2353,1147,2354,1151,2356,1154,2358,1158],{},[860,2355,1150],{"href":100},[909,2357,1087],{},[909,2359,1157],{},[874,2361],{},[877,2363,1164],{"id":1163},[1166,2365,2366,2368,2376,2378,2384,2386,2398,2400,2402,2404,2408,2410,2427,2429],{"level":1168},[1170,2367,1173],{"id":1172},[856,2369,938,2370,1179,2372,1183,2374,953],{},[909,2371,1178],{},[909,2373,1182],{},[909,2375,1186],{},[1170,2377,1190],{"id":1189},[856,2379,1193,2380,1197,2382,1201],{},[860,2381,147],{"href":1196},[909,2383,1200],{},[1170,2385,1205],{"id":1204},[856,2387,1208,2388,1211,2390,1215,2392,1218,2394,1222,2396,1225],{},[909,2389,1157],{},[909,2391,1214],{},[860,2393,399],{"href":35},[909,2395,1221],{},[909,2397,952],{},[1170,2399,1229],{"id":1228},[856,2401,1232],{},[1170,2403,1236],{"id":1235},[856,2405,1239,2406,1243],{},[909,2407,1242],{},[1170,2409,1247],{"id":1246},[856,2411,1250,2412,1254,2414,1258,2416,1262,2418,1266,2420,1270,2422,1274,2424,953],{},[909,2413,1253],{},[909,2415,1257],{},[909,2417,1261],{},[909,2419,1265],{},[909,2421,1269],{},[909,2423,1273],{},[860,2425,1279],{"href":1277,"rel":2426},[864],[1170,2428,1283],{"id":1282},[856,2430,1286,2431,1290,2433,1294,2435,1298],{},[860,2432,1289],{"href":238},[909,2434,1293],{},[909,2436,1297],{},[874,2438],{},[877,2440,1304],{"id":1303},[856,2442,1307],{},[1309,2444,1312],{"id":1311},[856,2446,1315],{},[1309,2448,1319],{"id":1318},[856,2450,1322,2451,1326,2453,1330,2455,1333,2457,1336,2459,1340,2461,1344],{},[909,2452,1325],{},[909,2454,1329],{},[860,2456,399],{"href":35},[909,2458,1261],{},[909,2460,1339],{},[909,2462,1343],{},[1346,2464,2465,2471],{},[1349,2466,1351,2467,1354,2469],{},[909,2468,1261],{},[909,2470,941],{},[1349,2472,1359,2473,1363],{},[909,2474,1362],{},[856,2476,1366,2477,1369],{},[860,2478,948],{"href":96},[856,2480,1372,2481,1375],{},[860,2482,111],{"href":112},[1309,2484,1379],{"id":1378},[856,2486,1382],{},[882,2488,2489,2499],{},[885,2490,2491],{},[888,2492,2493,2495,2497],{},[891,2494,1391],{},[891,2496,1394],{},[891,2498,1397],{},[901,2500,2501,2513],{},[888,2502,2503,2505,2509],{},[906,2504,1404],{},[906,2506,2507],{},[909,2508,1409],{},[906,2510,1412,2511,1416],{},[909,2512,1415],{},[888,2514,2515,2517,2521],{},[906,2516,1421],{},[906,2518,2519],{},[909,2520,1426],{},[906,2522,1429,2523,1433,2525,1436,2527],{},[909,2524,1432],{},[909,2526,1261],{},[909,2528,1439],{},[856,2530,956,2531,1444,2533,1447],{},[860,2532,91],{"href":92},[860,2534,87],{"href":88},[1309,2536,1451],{"id":1450},[856,2538,1454],{},[882,2540,2541,2569],{},[885,2542,2543],{},[888,2544,2545,2547,2549,2553,2557,2561,2565],{},[891,2546,1463],{},[891,2548,1466],{},[891,2550,2551],{},[909,2552,1471],{},[891,2554,2555],{},[909,2556,1476],{},[891,2558,2559],{},[909,2560,1481],{},[891,2562,2563],{},[909,2564,1486],{},[891,2566,2567],{},[909,2568,1491],{},[901,2570,2571,2599],{},[888,2572,2573,2577,2581,2585,2589,2593,2597],{},[906,2574,2575],{},[909,2576,1500],{},[906,2578,2579,1506],{},[909,2580,1505],{},[906,2582,2583],{},[909,2584,1511],{},[906,2586,2587],{},[909,2588,1329],{},[906,2590,2591],{},[909,2592,1329],{},[906,2594,2595],{},[909,2596,1524],{},[906,2598,1527],{},[888,2600,2601,2605,2607,2611,2615,2619,2621],{},[906,2602,2603],{},[909,2604,1534],{},[906,2606,1537],{},[906,2608,2609],{},[909,2610,1511],{},[906,2612,2613],{},[909,2614,1329],{},[906,2616,2617],{},[909,2618,1329],{},[906,2620,1527],{},[906,2622,1554,2623],{},[909,2624,1557],{},[856,2626,938,2627,1562,2629,1565],{},[909,2628,1500],{},[909,2630,1534],{},[1309,2632,1569],{"id":1568},[856,2634,1572,2635,1576,2638,1580,2640,1583],{},[860,2636,871],{"href":869,"rel":2637},[864],[909,2639,1579],{},[860,2641,1150],{"href":100},[856,2643,1586,2644,1590,2646,1594],{},[923,2645,1589],{},[909,2647,1593],{},[927,2649,2650],{"className":1597,"code":1598,"language":5,"meta":935,"style":935},[909,2651,2652,2656,2672],{"__ignoreMap":935},[976,2653,2654],{"class":978,"line":979},[976,2655,1605],{"class":986},[976,2657,2658,2660,2662,2664,2666,2668,2670],{"class":978,"line":1011},[976,2659,1611],{"class":1610},[976,2661,1593],{"class":1614},[976,2663,1617],{"class":1610},[976,2665,1051],{"class":1028},[976,2667,1622],{"class":1000},[976,2669,1625],{"class":1004},[976,2671,1628],{"class":1000},[976,2673,2674],{"class":978,"line":1017},[976,2675,1633],{"class":986},[1635,2677,2678],{},[856,2679,1639],{},[874,2681],{},[877,2683,1645],{"id":1644},[1309,2685,1649],{"id":1648},[927,2687,2688],{"className":1597,"code":1652,"language":5,"meta":935,"style":935},[909,2689,2690,2694,2708,2726,2744,2758,2776,2792],{"__ignoreMap":935},[976,2691,2692],{"class":978,"line":979},[976,2693,1605],{"class":986},[976,2695,2696,2698,2700,2702,2704,2706],{"class":978,"line":1011},[976,2697,1611],{"class":1610},[976,2699,1665],{"class":1614},[976,2701,1617],{"class":1610},[976,2703,1051],{"class":1028},[976,2705,1673],{"class":1672},[976,2707,1676],{"class":986},[976,2709,2710,2712,2714,2716,2718,2720,2722,2724],{"class":978,"line":1017},[976,2711,1611],{"class":1610},[976,2713,1683],{"class":1614},[976,2715,1617],{"class":1610},[976,2717,1051],{"class":1028},[976,2719,1622],{"class":1000},[976,2721,1692],{"class":1004},[976,2723,1617],{"class":1000},[976,2725,1676],{"class":986},[976,2727,2728,2730,2732,2734,2736,2738,2740,2742],{"class":978,"line":1044},[976,2729,1611],{"class":1610},[976,2731,1703],{"class":1614},[976,2733,1617],{"class":1610},[976,2735,1051],{"class":1028},[976,2737,1622],{"class":1000},[976,2739,1712],{"class":1004},[976,2741,1617],{"class":1000},[976,2743,1676],{"class":986},[976,2745,2746,2748,2750,2752,2754,2756],{"class":978,"line":1097},[976,2747,1611],{"class":1610},[976,2749,1723],{"class":1614},[976,2751,1617],{"class":1610},[976,2753,1051],{"class":1028},[976,2755,1730],{"class":1672},[976,2757,1676],{"class":986},[976,2759,2760,2762,2764,2766,2768,2770,2772,2774],{"class":978,"line":1135},[976,2761,1611],{"class":1610},[976,2763,1739],{"class":1614},[976,2765,1617],{"class":1610},[976,2767,1051],{"class":1028},[976,2769,1622],{"class":1000},[976,2771,1748],{"class":1004},[976,2773,1617],{"class":1000},[976,2775,1676],{"class":986},[976,2777,2778,2780,2782,2784,2786,2788,2790],{"class":978,"line":1755},[976,2779,1611],{"class":1610},[976,2781,1593],{"class":1614},[976,2783,1617],{"class":1610},[976,2785,1051],{"class":1028},[976,2787,1622],{"class":1000},[976,2789,1768],{"class":1004},[976,2791,1628],{"class":1000},[976,2793,2794],{"class":978,"line":1773},[976,2795,1633],{"class":986},[856,2797,938,2798,1780,2800,1783,2802,1787],{},[909,2799,1593],{},[909,2801,1723],{},[909,2803,1786],{},[1309,2805,1791],{"id":1790},[882,2807,2808,2816],{},[885,2809,2810],{},[888,2811,2812,2814],{},[891,2813,1800],{},[891,2815,1803],{},[901,2817,2818,2828,2836,2846],{},[888,2819,2820,2824],{},[906,2821,2822],{},[909,2823,952],{},[906,2825,1814,2826,1817],{},[909,2827,941],{},[888,2829,2830,2834],{},[906,2831,2832],{},[909,2833,1297],{},[906,2835,1826],{},[888,2837,2838,2842],{},[906,2839,2840],{},[909,2841,1186],{},[906,2843,1835,2844,1839],{},[909,2845,1838],{},[888,2847,2848,2852],{},[906,2849,2850],{},[909,2851,1846],{},[906,2853,1849],{},[874,2855],{},[877,2857,1855],{"id":1854},[856,2859,1858,2860,1862],{},[909,2861,1861],{},[882,2863,2864,2878],{},[885,2865,2866],{},[888,2867,2868,2870,2872,2874,2876],{},[891,2869,1871],{},[891,2871,1874],{},[891,2873,1877],{},[891,2875,1880],{},[891,2877,1883],{},[901,2879,2880,2894,2908,2922],{},[888,2881,2882,2884,2888,2890,2892],{},[906,2883,1890],{},[906,2885,2886],{},[909,2887,1895],{},[906,2889,1898],{},[906,2891,1901],{},[906,2893,1904],{},[888,2895,2896,2898,2902,2904,2906],{},[906,2897,1909],{},[906,2899,2900],{},[909,2901,1087],{},[906,2903,1916],{},[906,2905,1901],{},[906,2907,1921],{},[888,2909,2910,2912,2916,2918,2920],{},[906,2911,1926],{},[906,2913,2914],{},[909,2915,1242],{},[906,2917,1933],{},[906,2919,1936],{},[906,2921,1939],{},[888,2923,2924,2926,2930,2932,2934],{},[906,2925,1944],{},[906,2927,2928],{},[909,2929,1242],{},[906,2931,1951],{},[906,2933,1954],{},[906,2935,1939],{},[856,2937,938,2938,1961,2940,1964],{},[909,2939,1200],{},[860,2941,147],{"href":1196},[874,2943],{},[877,2945,1970],{"id":1969},[856,2947,1973],{},[882,2949,2950,2964],{},[885,2951,2952],{},[888,2953,2954,2956,2958,2960,2962],{},[891,2955,1982],{},[891,2957,1985],{},[891,2959,1988],{},[891,2961,1991],{},[891,2963,1397],{},[901,2965,2966,2984,3000,3018,3038,3062],{},[888,2967,2968,2972,2974,2978,2982],{},[906,2969,2970],{},[909,2971,1325],{},[906,2973,2004],{},[906,2975,2976],{},[909,2977,2009],{},[906,2979,2980],{},[909,2981,1786],{},[906,2983,2016],{},[888,2985,2986,2990,2992,2996,2998],{},[906,2987,2988],{},[909,2989,2023],{},[906,2991,2004],{},[906,2993,2994],{},[909,2995,1058],{},[906,2997,1527],{},[906,2999,2034],{},[888,3001,3002,3006,3010,3014,3016],{},[906,3003,3004],{},[909,3005,2041],{},[906,3007,3008],{},[909,3009,2046],{},[906,3011,3012],{},[909,3013,2051],{},[906,3015,1527],{},[906,3017,2056],{},[888,3019,3020,3024,3028,3032,3034],{},[906,3021,3022],{},[909,3023,1557],{},[906,3025,3026],{},[909,3027,2046],{},[906,3029,3030],{},[909,3031,1058],{},[906,3033,1527],{},[906,3035,2075,3036,2078],{},[909,3037,1534],{},[888,3039,3040,3044,3048,3052,3056],{},[906,3041,3042],{},[909,3043,2085],{},[906,3045,3046],{},[909,3047,2046],{},[906,3049,3050],{},[909,3051,1058],{},[906,3053,3054],{},[909,3055,2098],{},[906,3057,2101,3058,1262,3060,2107],{},[909,3059,2098],{},[909,3061,2106],{},[888,3063,3064,3068,3072,3076,3078],{},[906,3065,3066],{},[909,3067,2114],{},[906,3069,3070],{},[909,3071,2046],{},[906,3073,3074],{},[909,3075,1058],{},[906,3077,1527],{},[906,3079,2127],{},[856,3081,956,3082,2132],{},[860,3083,237],{"href":238},[2134,3085,2136],{},{"title":935,"searchDepth":1011,"depth":1011,"links":3087},[3088,3089,3090,3091,3098,3102,3103],{"id":879,"depth":1011,"text":880},{"id":966,"depth":1011,"text":967},{"id":1163,"depth":1011,"text":1164},{"id":1303,"depth":1011,"text":1304,"children":3092},[3093,3094,3095,3096,3097],{"id":1311,"depth":1017,"text":1312},{"id":1318,"depth":1017,"text":1319},{"id":1378,"depth":1017,"text":1379},{"id":1450,"depth":1017,"text":1451},{"id":1568,"depth":1017,"text":1569},{"id":1644,"depth":1011,"text":1645,"children":3099},[3100,3101],{"id":1648,"depth":1017,"text":1649},{"id":1790,"depth":1017,"text":1791},{"id":1854,"depth":1011,"text":1855},{"id":1969,"depth":1011,"text":1970},{},{"title":103,"description":2155},1780436281072]