[{"data":1,"prerenderedAt":3900},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fiam":64,"navigation":257,"navLinks_footer":837,"\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting_page":850,"\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting_surround":2602,"\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting":2605},{"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":127,"body":852,"description":2594,"extension":2595,"icon":2596,"meta":2597,"module":2598,"navigation":8,"path":128,"rawbody":2599,"seo":2600,"stem":129,"__hash__":2601},"docs\u002Fdocs\u002Fiam\u002F01.essentials\u002F11.fingerprinting.md",{"type":853,"value":854,"toc":2579},"minimark",[855,877,880,883,888,905,1078,1088,1093,1224,1228,1333,1354,1356,1360,1365,1493,1508,1510,1514,1527,1546,1550,1611,1616,1621,1637,1646,1800,1802,1806,1824,1827,1970,1986,1988,1994,2002,2072,2074,2078,2087,2551,2575],[856,857,858,859,863,864,867,868,872,873,876],"p",{},"Every time a sensitive request reaches the IAM service, the ",[860,861,862],"code",{},"getFingerPrint"," middleware extracts a composite device fingerprint from the incoming request. The fingerprint combines IP-based geolocation data (country, city, coordinates, ISP, proxy\u002Fhosting flags) with parsed user-agent fields (browser, OS, device type, vendor, model) and a bot detection signal. This data is attached to ",[860,865,866],{},"req.fingerPrint"," and flows into three downstream systems: ",[869,870,871],"a",{"href":96},"anomaly detection",", ",[869,874,875],{"href":124},"MFA verification",", and visitor trust management.",[856,878,879],{},"The fingerprint is not a browser-side fingerprint (no canvas hashing, no WebGL probing). It is a server-side composite built entirely from request headers and IP metadata. The service compares incoming fingerprints against stored visitor records to detect device changes, geographic shifts, and suspicious infrastructure usage.",[881,882],"hr",{},[884,885,887],"h2",{"id":886},"data-collection","Data collection",[856,889,890,891,893,894,896,897,900,901,904],{},"The ",[860,892,862],{}," middleware runs on every route that involves session verification, token rotation, or MFA. It calls two functions from the ",[869,895,399],{"href":35}," package: ",[860,898,899],{},"getGeoData"," for IP geolocation and ",[860,902,903],{},"parseUA"," for user-agent parsing.",[906,907,912],"pre",{"className":908,"code":909,"language":910,"meta":911,"style":911},"language-ts shiki shiki-themes light-plus light-plus dracula","import { getFingerPrint } from '@riavzon\u002Fauth'\n\n\u002F\u002F Mount on any route that needs fingerprint data\nrouter.post('\u002Fsensitive-action', getFingerPrint, async (req, res) => {\n  const { ipAddress, country, city, browser, os, device } = req.fingerPrint\n  \u002F\u002F ...\n})\n","ts","",[860,913,914,947,953,960,1015,1066,1072],{"__ignoreMap":911},[915,916,919,923,927,930,933,936,940,944],"span",{"class":917,"line":918},"line",1,[915,920,922],{"class":921},"sZ328","import",[915,924,926],{"class":925},"sDd4n"," { ",[915,928,862],{"class":929},"sjsA6",[915,931,932],{"class":925}," } ",[915,934,935],{"class":921},"from",[915,937,939],{"class":938},"sFkSl"," '",[915,941,943],{"class":942},"sFB1V","@riavzon\u002Fauth",[915,945,946],{"class":938},"'\n",[915,948,950],{"class":917,"line":949},2,[915,951,952],{"emptyLinePlaceholder":8},"\n",[915,954,956],{"class":917,"line":955},3,[915,957,959],{"class":958},"sghk6","\u002F\u002F Mount on any route that needs fingerprint data\n",[915,961,963,966,969,973,976,979,982,984,986,988,990,994,997,1001,1003,1006,1009,1012],{"class":917,"line":962},4,[915,964,965],{"class":929},"router",[915,967,968],{"class":925},".",[915,970,972],{"class":971},"sHOzp","post",[915,974,975],{"class":925},"(",[915,977,978],{"class":938},"'",[915,980,981],{"class":942},"\u002Fsensitive-action",[915,983,978],{"class":938},[915,985,872],{"class":925},[915,987,862],{"class":929},[915,989,872],{"class":925},[915,991,993],{"class":992},"sl46w","async",[915,995,996],{"class":925}," (",[915,998,1000],{"class":999},"sygFZ","req",[915,1002,872],{"class":925},[915,1004,1005],{"class":999},"res",[915,1007,1008],{"class":925},") ",[915,1010,1011],{"class":992},"=>",[915,1013,1014],{"class":925}," {\n",[915,1016,1018,1021,1023,1027,1029,1032,1034,1037,1039,1042,1044,1047,1049,1052,1054,1058,1061,1063],{"class":917,"line":1017},5,[915,1019,1020],{"class":992},"  const",[915,1022,926],{"class":925},[915,1024,1026],{"class":1025},"s3JHE","ipAddress",[915,1028,872],{"class":925},[915,1030,1031],{"class":1025},"country",[915,1033,872],{"class":925},[915,1035,1036],{"class":1025},"city",[915,1038,872],{"class":925},[915,1040,1041],{"class":1025},"browser",[915,1043,872],{"class":925},[915,1045,1046],{"class":1025},"os",[915,1048,872],{"class":925},[915,1050,1051],{"class":1025},"device",[915,1053,932],{"class":925},[915,1055,1057],{"class":1056},"saOXh","=",[915,1059,1060],{"class":929}," req",[915,1062,968],{"class":925},[915,1064,1065],{"class":929},"fingerPrint\n",[915,1067,1069],{"class":917,"line":1068},6,[915,1070,1071],{"class":958},"  \u002F\u002F ...\n",[915,1073,1075],{"class":917,"line":1074},7,[915,1076,1077],{"class":925},"})\n",[856,1079,1080,1081,1083,1084,1087],{},"The middleware populates ",[860,1082,866],{}," with the full ",[860,1085,1086],{},"FingerPrint"," interface:",[1089,1090,1092],"h3",{"id":1091},"geolocation-fields","Geolocation fields",[1094,1095,1096,1108,1117,1126,1136,1145,1152,1158,1164,1170,1179,1188,1194,1200,1206,1216],"field-group",{},[1097,1098,1101],"field",{"name":1026,"type":1099,":required":1100},"string","true",[856,1102,1103,1104,1107],{},"The client IP address from ",[860,1105,1106],{},"req.ip",". Used as the primary key for geo lookups and anomaly comparisons.",[1097,1109,1110],{"name":1031,"type":1099},[856,1111,1112,1113,1116],{},"Full country name (e.g. ",[860,1114,1115],{},"'Germany'",").",[1097,1118,1120],{"name":1119,"type":1099},"countryCode",[856,1121,1122,1123,1116],{},"ISO 3166-1 alpha-2 country code (e.g. ",[860,1124,1125],{},"'DE'",[1097,1127,1129],{"name":1128,"type":1099},"region",[856,1130,1131,1132,1135],{},"Region or state code (e.g. ",[860,1133,1134],{},"'BE'"," for Berlin).",[1097,1137,1139],{"name":1138,"type":1099},"regionName",[856,1140,1141,1142,1116],{},"Full region name (e.g. ",[860,1143,1144],{},"'Berlin'",[1097,1146,1147],{"name":1036,"type":1099},[856,1148,1149,1150,1116],{},"City name (e.g. ",[860,1151,1144],{},[1097,1153,1155],{"name":1154,"type":1099},"district",[856,1156,1157],{},"District or subdivision within the city, when available.",[1097,1159,1161],{"name":1160,"type":1099},"lat",[856,1162,1163],{},"Latitude coordinate of the IP location.",[1097,1165,1167],{"name":1166,"type":1099},"lon",[856,1168,1169],{},"Longitude coordinate of the IP location.",[1097,1171,1173],{"name":1172,"type":1099},"timezone",[856,1174,1175,1176,1116],{},"IANA timezone identifier (e.g. ",[860,1177,1178],{},"'Europe\u002FBerlin'",[1097,1180,1182],{"name":1181,"type":1099},"currency",[856,1183,1184,1185,1116],{},"ISO 4217 currency code associated with the country (e.g. ",[860,1186,1187],{},"'EUR'",[1097,1189,1191],{"name":1190,"type":1099},"isp",[856,1192,1193],{},"Internet Service Provider name.",[1097,1195,1197],{"name":1196,"type":1099},"org",[856,1198,1199],{},"Organization name associated with the IP range.",[1097,1201,1203],{"name":1202,"type":1099},"as_org",[856,1204,1205],{},"Autonomous System organization name.",[1097,1207,1210],{"name":1208,"type":1209},"proxy","boolean",[856,1211,1212,1213,1215],{},"Whether the IP is associated with a known proxy service. Triggers anomaly detection when ",[860,1214,1100],{}," and the user has not been verified via MFA.",[1097,1217,1219],{"name":1218,"type":1209},"hosting",[856,1220,1221,1222,968],{},"Whether the IP belongs to a known hosting or datacenter provider. Same anomaly behavior as ",[860,1223,1208],{},[1089,1225,1227],{"id":1226},"user-agent-fields","User-agent fields",[1094,1229,1230,1240,1254,1266,1278,1292,1298,1307,1321,1327],{},[1097,1231,1233],{"name":1232,"type":1099,":required":1100},"userAgent",[856,1234,1235,1236,1239],{},"The raw ",[860,1237,1238],{},"User-Agent"," header string, stored as-is for logging and comparison.",[1097,1241,1242],{"name":1051,"type":1099,":required":1100},[856,1243,1244,1245,872,1248,872,1251,1116],{},"Device type classification (e.g. ",[860,1246,1247],{},"'desktop'",[860,1249,1250],{},"'mobile'",[860,1252,1253],{},"'tablet'",[1097,1255,1257],{"name":1256,"type":1099},"deviceVendor",[856,1258,1259,1260,872,1263,1116],{},"Device manufacturer (e.g. ",[860,1261,1262],{},"'Apple'",[860,1264,1265],{},"'Samsung'",[1097,1267,1269],{"name":1268,"type":1099},"deviceModel",[856,1270,1271,1272,872,1275,1116],{},"Device model name (e.g. ",[860,1273,1274],{},"'iPhone'",[860,1276,1277],{},"'Galaxy S24'",[1097,1279,1280],{"name":1041,"type":1099},[856,1281,1282,1283,872,1286,872,1289,1116],{},"Browser name (e.g. ",[860,1284,1285],{},"'Chrome'",[860,1287,1288],{},"'Safari'",[860,1290,1291],{},"'Firefox'",[1097,1293,1295],{"name":1294,"type":1099},"browserType",[856,1296,1297],{},"Browser engine or type classifier.",[1097,1299,1301],{"name":1300,"type":1099},"browserVersion",[856,1302,1303,1304,1116],{},"Full browser version string (e.g. ",[860,1305,1306],{},"'125.0.0.0'",[1097,1308,1309],{"name":1046,"type":1099},[856,1310,1311,1312,872,1315,872,1318,1116],{},"Operating system name (e.g. ",[860,1313,1314],{},"'macOS'",[860,1316,1317],{},"'Windows 11'",[860,1319,1320],{},"'Android 14'",[1097,1322,1324],{"name":1323,"type":1209,":required":1100},"botAI",[856,1325,1326],{},"Whether the user agent matches a known AI crawler pattern.",[1097,1328,1330],{"name":1329,"type":1209,":required":1100},"bot",[856,1331,1332],{},"Whether the user agent matches a known bot pattern.",[1334,1335,1336],"tip",{},[856,1337,1338,1339,1341,1342,1345,1346,872,1348,1350,1351,1353],{},"The geolocation data comes from the ",[869,1340,399],{"href":35}," package, which uses  databases generated by ",[869,1343,1344],{"href":42},"shield-base",". The accuracy of ",[860,1347,1036],{},[860,1349,1160],{},", and ",[860,1352,1166],{}," depends on the database edition and the client's ISP.",[881,1355],{},[884,1357,1359],{"id":1358},"where-fingerprints-are-collected","Where fingerprints are collected",[856,1361,890,1362,1364],{},[860,1363,862],{}," middleware is mounted on routes where the service needs to verify or update the client's identity:",[1366,1367,1368,1381],"table",{},[1369,1370,1371],"thead",{},[1372,1373,1374,1378],"tr",{},[1375,1376,1377],"th",{},"Route",[1375,1379,1380],{},"Purpose",[1382,1383,1384,1399,1413,1423,1433,1443,1453,1463,1473,1483],"tbody",{},[1372,1385,1386,1392],{},[1387,1388,1389],"td",{},[860,1390,1391],{},"POST \u002Fauth\u002Fuser\u002Frefresh-session",[1387,1393,1394,1395,1398],{},"Token rotation. Fingerprint feeds into ",[860,1396,1397],{},"strangeThings()"," anomaly checks.",[1372,1400,1401,1406],{},[1387,1402,1403],{},[860,1404,1405],{},"POST \u002Fauth\u002Fverify-mfa",[1387,1407,1408,1409,1412],{},"Adaptive MFA verification. Fingerprint is persisted via ",[860,1410,1411],{},"updateVisitors"," on success.",[1372,1414,1415,1420],{},[1387,1416,1417],{},[860,1418,1419],{},"POST \u002Fcustom\u002Fmfa\u002F:reason",[1387,1421,1422],{},"Custom MFA initiation. Fingerprint metadata is included in the OTP email.",[1372,1424,1425,1430],{},[1387,1426,1427],{},[860,1428,1429],{},"GET \u002Fauth\u002Fverify-custom-mfa",[1387,1431,1432],{},"Custom MFA link validation.",[1372,1434,1435,1440],{},[1387,1436,1437],{},[860,1438,1439],{},"POST \u002Fauth\u002Fverify-custom-mfa",[1387,1441,1442],{},"Custom MFA code submission.",[1372,1444,1445,1450],{},[1387,1446,1447],{},[860,1448,1449],{},"POST \u002Fupdate\u002Femail",[1387,1451,1452],{},"Email update. Fingerprint is persisted after successful verification.",[1372,1454,1455,1460],{},[1387,1456,1457],{},[860,1458,1459],{},"GET \u002Fauth\u002Freset-password",[1387,1461,1462],{},"Password reset link validation.",[1372,1464,1465,1470],{},[1387,1466,1467],{},[860,1468,1469],{},"POST \u002Fauth\u002Freset-password",[1387,1471,1472],{},"Password reset submission.",[1372,1474,1475,1480],{},[1387,1476,1477],{},[860,1478,1479],{},"GET \u002Fsecret\u002Fdata",[1387,1481,1482],{},"BFF access route.",[1372,1484,1485,1490],{},[1387,1486,1487],{},[860,1488,1489],{},"GET \u002Fsecret\u002Faccesstoken\u002Fmetadata",[1387,1491,1492],{},"BFF metadata route.",[1494,1495,1496],"note",{},[856,1497,1498,1499,1501,1502,1504,1505,1507],{},"The login and signup controllers do not use ",[860,1500,862],{}," middleware directly. They extract device metadata through ",[860,1503,903],{}," and ",[860,1506,899],{}," inline and pass it to the visitor creation flow.",[881,1509],{},[884,1511,1513],{"id":1512},"visitor-persistence","Visitor persistence",[856,1515,1516,1517,1519,1520,1522,1523,1526],{},"When a user completes MFA verification or logs in from a new device, the service calls ",[860,1518,1411],{}," from the ",[869,1521,399],{"href":35}," package to persist the fingerprint to the ",[860,1524,1525],{},"visitors"," table. This stored record becomes the baseline for future anomaly comparisons.",[856,1528,890,1529,1531,1532,1535,1536,1539,1540,1542,1543,1545],{},[860,1530,1525],{}," table stores one record per ",[860,1533,1534],{},"visitor_id"," (linked to the ",[860,1537,1538],{},"canary_id"," cookie). Each record contains the full set of geolocation and user-agent fields from the ",[860,1541,1086],{}," interface. When ",[860,1544,1411],{}," is called, it overwrites the stored record with the current request's fingerprint data.",[1089,1547,1549],{"id":1548},"when-fingerprints-are-persisted","When fingerprints are persisted",[1366,1551,1552,1565],{},[1369,1553,1554],{},[1372,1555,1556,1559,1562],{},[1375,1557,1558],{},"Event",[1375,1560,1561],{},"Trigger",[1375,1563,1564],{},"Function",[1382,1566,1567,1579,1595],{},[1372,1568,1569,1571,1574],{},[1387,1570,103],{},[1387,1572,1573],{},"Successful credential verification",[1387,1575,1576,1578],{},[860,1577,1411],{}," called with parsed request data",[1372,1580,1581,1583,1586],{},[1387,1582,875],{},[1387,1584,1585],{},"OTP code validated",[1387,1587,1588,1591,1592,1594],{},[860,1589,1590],{},"verifyMfaCode"," calls ",[860,1593,1411],{}," after token rotation",[1372,1596,1597,1600,1603],{},[1387,1598,1599],{},"Visitor trust",[1387,1601,1602],{},"New device verified",[1387,1604,1605,1591,1608,1610],{},[860,1606,1607],{},"trustVisitor",[860,1609,1411],{}," inside a transaction",[1089,1612,1614],{"id":1613},"trustvisitor",[860,1615,1607],{},[856,1617,1618,1620],{},[860,1619,1607],{}," is called when a user verifies a new device through MFA. It performs two operations inside a single database transaction:",[1622,1623,1624,1631],"ol",{},[1625,1626,1627,1628,1630],"li",{},"Updates the user's ",[860,1629,1534],{}," to point to the newly verified visitor record",[1625,1632,1633,1634,1636],{},"Calls ",[860,1635,1411],{}," to persist the current fingerprint to that visitor record",[856,1638,1639,1640,1642,1643,1645],{},"If ",[860,1641,1411],{}," fails, the transaction rolls back and the user's ",[860,1644,1534],{}," is not updated. This ensures the user is never associated with a visitor record that has incomplete fingerprint data.",[906,1647,1649],{"className":908,"code":1648,"language":910,"meta":911,"style":911},"import { trustVisitor } from '@riavzon\u002Fauth'\n\nconst result = await trustVisitor(\n  userId,\n  visitorIdToTrust,\n  canaryId,\n  req.fingerPrint,\n  log\n)\n\nif (!result.ok) {\n  \u002F\u002F Transaction failed — visitor not trusted\n  log.error(result.data)\n}\n",[860,1650,1651,1669,1673,1693,1701,1708,1715,1727,1733,1739,1744,1766,1772,1794],{"__ignoreMap":911},[915,1652,1653,1655,1657,1659,1661,1663,1665,1667],{"class":917,"line":918},[915,1654,922],{"class":921},[915,1656,926],{"class":925},[915,1658,1607],{"class":929},[915,1660,932],{"class":925},[915,1662,935],{"class":921},[915,1664,939],{"class":938},[915,1666,943],{"class":942},[915,1668,946],{"class":938},[915,1670,1671],{"class":917,"line":949},[915,1672,952],{"emptyLinePlaceholder":8},[915,1674,1675,1678,1681,1684,1687,1690],{"class":917,"line":955},[915,1676,1677],{"class":992},"const",[915,1679,1680],{"class":1025}," result",[915,1682,1683],{"class":1056}," =",[915,1685,1686],{"class":921}," await",[915,1688,1689],{"class":971}," trustVisitor",[915,1691,1692],{"class":925},"(\n",[915,1694,1695,1698],{"class":917,"line":962},[915,1696,1697],{"class":929},"  userId",[915,1699,1700],{"class":925},",\n",[915,1702,1703,1706],{"class":917,"line":1017},[915,1704,1705],{"class":929},"  visitorIdToTrust",[915,1707,1700],{"class":925},[915,1709,1710,1713],{"class":917,"line":1068},[915,1711,1712],{"class":929},"  canaryId",[915,1714,1700],{"class":925},[915,1716,1717,1720,1722,1725],{"class":917,"line":1074},[915,1718,1719],{"class":929},"  req",[915,1721,968],{"class":925},[915,1723,1724],{"class":929},"fingerPrint",[915,1726,1700],{"class":925},[915,1728,1730],{"class":917,"line":1729},8,[915,1731,1732],{"class":929},"  log\n",[915,1734,1736],{"class":917,"line":1735},9,[915,1737,1738],{"class":925},")\n",[915,1740,1742],{"class":917,"line":1741},10,[915,1743,952],{"emptyLinePlaceholder":8},[915,1745,1747,1750,1752,1755,1758,1760,1763],{"class":917,"line":1746},11,[915,1748,1749],{"class":921},"if",[915,1751,996],{"class":925},[915,1753,1754],{"class":1056},"!",[915,1756,1757],{"class":929},"result",[915,1759,968],{"class":925},[915,1761,1762],{"class":929},"ok",[915,1764,1765],{"class":925},") {\n",[915,1767,1769],{"class":917,"line":1768},12,[915,1770,1771],{"class":958},"  \u002F\u002F Transaction failed — visitor not trusted\n",[915,1773,1775,1778,1780,1783,1785,1787,1789,1792],{"class":917,"line":1774},13,[915,1776,1777],{"class":929},"  log",[915,1779,968],{"class":925},[915,1781,1782],{"class":971},"error",[915,1784,975],{"class":925},[915,1786,1757],{"class":929},[915,1788,968],{"class":925},[915,1790,1791],{"class":929},"data",[915,1793,1738],{"class":925},[915,1795,1797],{"class":917,"line":1796},14,[915,1798,1799],{"class":925},"}\n",[881,1801],{},[884,1803,1805],{"id":1804},"anomaly-detection-integration","Anomaly detection integration",[856,1807,1808,1809,1811,1812,1814,1815,872,1818,1350,1821,1823],{},"The stored fingerprint is the reference point for ",[869,1810,871],{"href":96},". When ",[860,1813,1397],{}," runs on a refresh-token use, it joins the ",[860,1816,1817],{},"refresh_tokens",[860,1819,1820],{},"users",[860,1822,1525],{}," tables to compare the incoming request against the stored visitor record.",[856,1825,1826],{},"The following fingerprint fields are compared:",[1366,1828,1829,1842],{},[1369,1830,1831],{},[1372,1832,1833,1836,1839],{},[1375,1834,1835],{},"Field",[1375,1837,1838],{},"Anomaly type",[1375,1840,1841],{},"Result",[1382,1843,1844,1862,1877,1895,1911,1925,1939,1952],{},[1372,1845,1846,1853,1856],{},[1387,1847,1848,1850,1851],{},[860,1849,1538],{}," cookie vs stored ",[860,1852,1538],{},[1387,1854,1855],{},"Device change",[1387,1857,1858,1861],{},[860,1859,1860],{},"reqMFA: true"," (new device)",[1372,1863,1864,1869,1872],{},[1387,1865,1866,1868],{},[860,1867,1026],{}," vs stored IP range",[1387,1870,1871],{},"Network change",[1387,1873,1874,1876],{},[860,1875,1860],{}," (different network)",[1372,1878,1879,1884,1887],{},[1387,1880,1881,1883],{},[860,1882,1208],{}," flag",[1387,1885,1886],{},"Proxy detected",[1387,1888,1889,1891,1892],{},[860,1890,1860],{}," if ",[860,1893,1894],{},"proxy_allowed = 0",[1372,1896,1897,1901,1904],{},[1387,1898,1899,1883],{},[860,1900,1218],{},[1387,1902,1903],{},"Hosting\u002Fdatacenter detected",[1387,1905,1906,1891,1908],{},[860,1907,1860],{},[860,1909,1910],{},"hosting_allowed = 0",[1372,1912,1913,1918,1921],{},[1387,1914,1915,1917],{},[860,1916,1051],{}," type",[1387,1919,1920],{},"Device type change",[1387,1922,1923],{},[860,1924,1860],{},[1372,1926,1927,1932,1935],{},[1387,1928,1929,1931],{},[860,1930,1041],{}," name",[1387,1933,1934],{},"Browser change",[1387,1936,1937],{},[860,1938,1860],{},[1372,1940,1941,1945,1948],{},[1387,1942,1943,1931],{},[860,1944,1046],{},[1387,1946,1947],{},"OS change",[1387,1949,1950],{},[860,1951,1860],{},[1372,1953,1954,1962,1965],{},[1387,1955,1956,1958,1959,1961],{},[860,1957,1160],{}," \u002F ",[860,1960,1166],{}," coordinates",[1387,1963,1964],{},"Geographic shift",[1387,1966,1967,1969],{},[860,1968,1860],{}," (significant distance)",[1971,1972,1973],"warning",{},[856,1974,1975,1976,1978,1979,1504,1982,1985],{},"After a successful MFA verification, ",[860,1977,1590],{}," sets ",[860,1980,1981],{},"proxy_allowed = 1",[860,1983,1984],{},"hosting_allowed = 1"," on the visitor record. This grants trusted status to the verified device, suppressing proxy and hosting anomalies until the next suspicious event resets them.",[881,1987],{},[884,1989,890,1991,1993],{"id":1990},"the-canary_id-lifecycle",[860,1992,1538],{}," lifecycle",[856,1995,890,1996,1998,1999,2001],{},[860,1997,1538],{}," is a long-lived browser cookie that serves as the persistent device identifier. It ties a specific browser instance to a ",[860,2000,1534],{}," in the database.",[2003,2004,2006,2011,2017,2021,2041,2045,2054,2058],"steps",{"level":2005},"4",[2007,2008,2010],"h4",{"id":2009},"cookie-creation","Cookie creation",[856,2012,2013,2014,2016],{},"A ",[860,2015,1538],{}," cookie is set when a user first interacts with the authentication system (signup or login). The value is a randomly generated identifier.",[2007,2018,2020],{"id":2019},"storage-linkage","Storage linkage",[856,2022,890,2023,2025,2026,2028,2029,2031,2032,2034,2035,2037,2038,968],{},[860,2024,1538],{}," is stored alongside the user's ",[860,2027,1534],{}," in the ",[860,2030,1525],{}," table. The ",[860,2033,1820],{}," table references the ",[860,2036,1534],{},", creating a chain: ",[860,2039,2040],{},"user → visitor_id → visitors table → canary_id",[2007,2042,2044],{"id":2043},"anomaly-comparison","Anomaly comparison",[856,2046,2047,2048,2050,2051,2053],{},"On every authenticated request, ",[860,2049,1397],{}," compares the ",[860,2052,1538],{}," from the incoming cookie against the value stored in the database. A mismatch indicates a new or different device, triggering an MFA challenge.",[2007,2055,2057],{"id":2056},"xss-integration","XSS integration",[856,2059,2060,2061,2064,2065,2068,2069,2071],{},"When an ",[869,2062,2063],{"href":140},"XSS attempt"," is detected, ",[860,2066,2067],{},"handleXSS"," uses the ",[860,2070,1538],{}," to permanently mark the corresponding visitor as a bot and update the banned IP record. This means the ban follows the device, not just the IP.",[881,2073],{},[884,2075,2077],{"id":2076},"accessing-fingerprints-in-custom-handlers","Accessing fingerprints in custom handlers",[856,2079,2080,2081,2083,2084,2086],{},"After the ",[860,2082,862],{}," middleware runs, the full fingerprint is available on ",[860,2085,866],{},". Use it for logging, analytics, or custom security logic.",[906,2088,2090],{"className":908,"code":2089,"language":910,"meta":911,"style":911},"import { getFingerPrint } from '@riavzon\u002Fauth'\n\nrouter.post('\u002Ftransfer',\n  getFingerPrint,\n  async (req, res) => {\n    const fp = req.fingerPrint\n\n    \u002F\u002F Log the request context\n    log.info({\n      ip: fp.ipAddress,\n      country: fp.country,\n      browser: fp.browser,\n      device: fp.device,\n      proxy: fp.proxy,\n    }, 'Transfer request')\n\n    \u002F\u002F Block requests from hosting providers\n    if (fp.hosting) {\n      return res.status(403).json({ error: 'Requests from hosting providers are not allowed.' })\n    }\n\n    \u002F\u002F Include location in the confirmation email\n    const location = [fp.city, fp.regionName, fp.country].filter(Boolean).join(', ')\n\n    await sendConfirmationEmail(user.email, {\n      amount: req.body.amount,\n      location,\n      device: `${fp.browser} on ${fp.os}`,\n    })\n  }\n)\n",[860,2091,2092,2110,2114,2133,2140,2159,2175,2179,2184,2197,2214,2229,2244,2259,2274,2289,2294,2300,2317,2358,2364,2369,2375,2436,2441,2463,2485,2493,2534,2540,2546],{"__ignoreMap":911},[915,2093,2094,2096,2098,2100,2102,2104,2106,2108],{"class":917,"line":918},[915,2095,922],{"class":921},[915,2097,926],{"class":925},[915,2099,862],{"class":929},[915,2101,932],{"class":925},[915,2103,935],{"class":921},[915,2105,939],{"class":938},[915,2107,943],{"class":942},[915,2109,946],{"class":938},[915,2111,2112],{"class":917,"line":949},[915,2113,952],{"emptyLinePlaceholder":8},[915,2115,2116,2118,2120,2122,2124,2126,2129,2131],{"class":917,"line":955},[915,2117,965],{"class":929},[915,2119,968],{"class":925},[915,2121,972],{"class":971},[915,2123,975],{"class":925},[915,2125,978],{"class":938},[915,2127,2128],{"class":942},"\u002Ftransfer",[915,2130,978],{"class":938},[915,2132,1700],{"class":925},[915,2134,2135,2138],{"class":917,"line":962},[915,2136,2137],{"class":929},"  getFingerPrint",[915,2139,1700],{"class":925},[915,2141,2142,2145,2147,2149,2151,2153,2155,2157],{"class":917,"line":1017},[915,2143,2144],{"class":992},"  async",[915,2146,996],{"class":925},[915,2148,1000],{"class":999},[915,2150,872],{"class":925},[915,2152,1005],{"class":999},[915,2154,1008],{"class":925},[915,2156,1011],{"class":992},[915,2158,1014],{"class":925},[915,2160,2161,2164,2167,2169,2171,2173],{"class":917,"line":1068},[915,2162,2163],{"class":992},"    const",[915,2165,2166],{"class":1025}," fp",[915,2168,1683],{"class":1056},[915,2170,1060],{"class":929},[915,2172,968],{"class":925},[915,2174,1065],{"class":929},[915,2176,2177],{"class":917,"line":1074},[915,2178,952],{"emptyLinePlaceholder":8},[915,2180,2181],{"class":917,"line":1729},[915,2182,2183],{"class":958},"    \u002F\u002F Log the request context\n",[915,2185,2186,2189,2191,2194],{"class":917,"line":1735},[915,2187,2188],{"class":929},"    log",[915,2190,968],{"class":925},[915,2192,2193],{"class":971},"info",[915,2195,2196],{"class":925},"({\n",[915,2198,2199,2202,2206,2208,2210,2212],{"class":917,"line":1741},[915,2200,2201],{"class":929},"      ip",[915,2203,2205],{"class":2204},"s34zl",":",[915,2207,2166],{"class":929},[915,2209,968],{"class":925},[915,2211,1026],{"class":929},[915,2213,1700],{"class":925},[915,2215,2216,2219,2221,2223,2225,2227],{"class":917,"line":1746},[915,2217,2218],{"class":929},"      country",[915,2220,2205],{"class":2204},[915,2222,2166],{"class":929},[915,2224,968],{"class":925},[915,2226,1031],{"class":929},[915,2228,1700],{"class":925},[915,2230,2231,2234,2236,2238,2240,2242],{"class":917,"line":1768},[915,2232,2233],{"class":929},"      browser",[915,2235,2205],{"class":2204},[915,2237,2166],{"class":929},[915,2239,968],{"class":925},[915,2241,1041],{"class":929},[915,2243,1700],{"class":925},[915,2245,2246,2249,2251,2253,2255,2257],{"class":917,"line":1774},[915,2247,2248],{"class":929},"      device",[915,2250,2205],{"class":2204},[915,2252,2166],{"class":929},[915,2254,968],{"class":925},[915,2256,1051],{"class":929},[915,2258,1700],{"class":925},[915,2260,2261,2264,2266,2268,2270,2272],{"class":917,"line":1796},[915,2262,2263],{"class":929},"      proxy",[915,2265,2205],{"class":2204},[915,2267,2166],{"class":929},[915,2269,968],{"class":925},[915,2271,1208],{"class":929},[915,2273,1700],{"class":925},[915,2275,2277,2280,2282,2285,2287],{"class":917,"line":2276},15,[915,2278,2279],{"class":925},"    }, ",[915,2281,978],{"class":938},[915,2283,2284],{"class":942},"Transfer request",[915,2286,978],{"class":938},[915,2288,1738],{"class":925},[915,2290,2292],{"class":917,"line":2291},16,[915,2293,952],{"emptyLinePlaceholder":8},[915,2295,2297],{"class":917,"line":2296},17,[915,2298,2299],{"class":958},"    \u002F\u002F Block requests from hosting providers\n",[915,2301,2303,2306,2308,2311,2313,2315],{"class":917,"line":2302},18,[915,2304,2305],{"class":921},"    if",[915,2307,996],{"class":925},[915,2309,2310],{"class":929},"fp",[915,2312,968],{"class":925},[915,2314,1218],{"class":929},[915,2316,1765],{"class":925},[915,2318,2320,2323,2326,2328,2331,2333,2337,2339,2341,2344,2346,2348,2350,2353,2355],{"class":917,"line":2319},19,[915,2321,2322],{"class":921},"      return",[915,2324,2325],{"class":929}," res",[915,2327,968],{"class":925},[915,2329,2330],{"class":971},"status",[915,2332,975],{"class":925},[915,2334,2336],{"class":2335},"spgvN","403",[915,2338,1116],{"class":925},[915,2340,5],{"class":971},[915,2342,2343],{"class":925},"({ ",[915,2345,1782],{"class":929},[915,2347,2205],{"class":2204},[915,2349,939],{"class":938},[915,2351,2352],{"class":942},"Requests from hosting providers are not allowed.",[915,2354,978],{"class":938},[915,2356,2357],{"class":925}," })\n",[915,2359,2361],{"class":917,"line":2360},20,[915,2362,2363],{"class":925},"    }\n",[915,2365,2367],{"class":917,"line":2366},21,[915,2368,952],{"emptyLinePlaceholder":8},[915,2370,2372],{"class":917,"line":2371},22,[915,2373,2374],{"class":958},"    \u002F\u002F Include location in the confirmation email\n",[915,2376,2378,2380,2383,2385,2388,2390,2392,2394,2396,2398,2400,2402,2404,2406,2408,2410,2413,2416,2418,2421,2423,2426,2428,2430,2432,2434],{"class":917,"line":2377},23,[915,2379,2163],{"class":992},[915,2381,2382],{"class":1025}," location",[915,2384,1683],{"class":1056},[915,2386,2387],{"class":925}," [",[915,2389,2310],{"class":929},[915,2391,968],{"class":925},[915,2393,1036],{"class":929},[915,2395,872],{"class":925},[915,2397,2310],{"class":929},[915,2399,968],{"class":925},[915,2401,1138],{"class":929},[915,2403,872],{"class":925},[915,2405,2310],{"class":929},[915,2407,968],{"class":925},[915,2409,1031],{"class":929},[915,2411,2412],{"class":925},"].",[915,2414,2415],{"class":971},"filter",[915,2417,975],{"class":925},[915,2419,2420],{"class":929},"Boolean",[915,2422,1116],{"class":925},[915,2424,2425],{"class":971},"join",[915,2427,975],{"class":925},[915,2429,978],{"class":938},[915,2431,872],{"class":942},[915,2433,978],{"class":938},[915,2435,1738],{"class":925},[915,2437,2439],{"class":917,"line":2438},24,[915,2440,952],{"emptyLinePlaceholder":8},[915,2442,2444,2447,2450,2452,2455,2457,2460],{"class":917,"line":2443},25,[915,2445,2446],{"class":921},"    await",[915,2448,2449],{"class":971}," sendConfirmationEmail",[915,2451,975],{"class":925},[915,2453,2454],{"class":929},"user",[915,2456,968],{"class":925},[915,2458,2459],{"class":929},"email",[915,2461,2462],{"class":925},", {\n",[915,2464,2466,2469,2471,2473,2475,2478,2480,2483],{"class":917,"line":2465},26,[915,2467,2468],{"class":929},"      amount",[915,2470,2205],{"class":2204},[915,2472,1060],{"class":929},[915,2474,968],{"class":925},[915,2476,2477],{"class":929},"body",[915,2479,968],{"class":925},[915,2481,2482],{"class":929},"amount",[915,2484,1700],{"class":925},[915,2486,2488,2491],{"class":917,"line":2487},27,[915,2489,2490],{"class":929},"      location",[915,2492,1700],{"class":925},[915,2494,2496,2498,2500,2503,2506,2508,2511,2513,2516,2519,2521,2523,2525,2527,2529,2532],{"class":917,"line":2495},28,[915,2497,2248],{"class":929},[915,2499,2205],{"class":2204},[915,2501,2502],{"class":942}," `",[915,2504,2505],{"class":992},"${",[915,2507,2310],{"class":929},[915,2509,968],{"class":2510},"s1lnM",[915,2512,1041],{"class":929},[915,2514,2515],{"class":992},"}",[915,2517,2518],{"class":942}," on ",[915,2520,2505],{"class":992},[915,2522,2310],{"class":929},[915,2524,968],{"class":2510},[915,2526,1046],{"class":929},[915,2528,2515],{"class":992},[915,2530,2531],{"class":942},"`",[915,2533,1700],{"class":925},[915,2535,2537],{"class":917,"line":2536},29,[915,2538,2539],{"class":925},"    })\n",[915,2541,2543],{"class":917,"line":2542},30,[915,2544,2545],{"class":925},"  }\n",[915,2547,2549],{"class":917,"line":2548},31,[915,2550,1738],{"class":925},[1334,2552,2553],{},[856,2554,890,2555,2557,2558,2560,2561,2563,2564,2567,2568,2570,2571,2574],{},[860,2556,862],{}," middleware fails gracefully. If ",[860,2559,899],{}," or ",[860,2562,903],{}," throws (,the IP geolocation database is unavailable), the middleware logs the error and calls ",[860,2565,2566],{},"next()"," without populating ",[860,2569,866],{},". Always check for ",[860,2572,2573],{},"undefined"," fields when accessing geolocation data in production.",[2576,2577,2578],"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 .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}html pre.shiki code .sl46w, html code.shiki .sl46w{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#FF79C6}html pre.shiki code .sygFZ, html code.shiki .sygFZ{--shiki-light:#001080;--shiki-light-font-style:inherit;--shiki-default:#001080;--shiki-default-font-style:inherit;--shiki-dark:#FFB86C;--shiki-dark-font-style:italic}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 .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 .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 .s1lnM, html code.shiki .s1lnM{--shiki-light:#000000FF;--shiki-default:#000000FF;--shiki-dark:#F8F8F2}",{"title":911,"searchDepth":949,"depth":949,"links":2580},[2581,2585,2586,2590,2591,2593],{"id":886,"depth":949,"text":887,"children":2582},[2583,2584],{"id":1091,"depth":955,"text":1092},{"id":1226,"depth":955,"text":1227},{"id":1358,"depth":949,"text":1359},{"id":1512,"depth":949,"text":1513,"children":2587},[2588,2589],{"id":1548,"depth":955,"text":1549},{"id":1613,"depth":955,"text":1607},{"id":1804,"depth":949,"text":1805},{"id":1990,"depth":949,"text":2592},"The canary_id lifecycle",{"id":2076,"depth":949,"text":2077},"How the IAM service builds a composite device fingerprint from IP geolocation and user-agent parsing, how it persists and compares fingerprints for anomaly detection, and how to access fingerprint data in custom handlers.","md","i-lucide-fingerprint",{},null,"---\ntitle: Fingerprinting\ndescription: How the IAM service builds a composite device fingerprint from IP geolocation and user-agent parsing, how it persists and compares fingerprints for anomaly detection, and how to access fingerprint data in custom handlers.\nicon: i-lucide-fingerprint\n---\n\nEvery time a sensitive request reaches the IAM service, the `getFingerPrint` middleware extracts a composite device fingerprint from the incoming request. The fingerprint combines IP-based geolocation data (country, city, coordinates, ISP, proxy\u002Fhosting flags) with parsed user-agent fields (browser, OS, device type, vendor, model) and a bot detection signal. This data is attached to `req.fingerPrint` and flows into three downstream systems: [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies), [MFA verification](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa), and visitor trust management.\n\nThe fingerprint is not a browser-side fingerprint (no canvas hashing, no WebGL probing). It is a server-side composite built entirely from request headers and IP metadata. The service compares incoming fingerprints against stored visitor records to detect device changes, geographic shifts, and suspicious infrastructure usage.\n\n---\n\n## Data collection\n\nThe `getFingerPrint` middleware runs on every route that involves session verification, token rotation, or MFA. It calls two functions from the [Bot Detector](\u002Fdocs\u002Fbot-detection) package: `getGeoData` for IP geolocation and `parseUA` for user-agent parsing.\n\n```ts\nimport { getFingerPrint } from '@riavzon\u002Fauth'\n\n\u002F\u002F Mount on any route that needs fingerprint data\nrouter.post('\u002Fsensitive-action', getFingerPrint, async (req, res) => {\n  const { ipAddress, country, city, browser, os, device } = req.fingerPrint\n  \u002F\u002F ...\n})\n```\n\nThe middleware populates `req.fingerPrint` with the full `FingerPrint` interface:\n\n### Geolocation fields\n\n::field-group\n::field{name=\"ipAddress\" type=\"string\" required}\nThe client IP address from `req.ip`. Used as the primary key for geo lookups and anomaly comparisons.\n::\n\n::field{name=\"country\" type=\"string\"}\nFull country name (e.g. `'Germany'`).\n::\n\n::field{name=\"countryCode\" type=\"string\"}\nISO 3166-1 alpha-2 country code (e.g. `'DE'`).\n::\n\n::field{name=\"region\" type=\"string\"}\nRegion or state code (e.g. `'BE'` for Berlin).\n::\n\n::field{name=\"regionName\" type=\"string\"}\nFull region name (e.g. `'Berlin'`).\n::\n\n::field{name=\"city\" type=\"string\"}\nCity name (e.g. `'Berlin'`).\n::\n\n::field{name=\"district\" type=\"string\"}\nDistrict or subdivision within the city, when available.\n::\n\n::field{name=\"lat\" type=\"string\"}\nLatitude coordinate of the IP location.\n::\n\n::field{name=\"lon\" type=\"string\"}\nLongitude coordinate of the IP location.\n::\n\n::field{name=\"timezone\" type=\"string\"}\nIANA timezone identifier (e.g. `'Europe\u002FBerlin'`).\n::\n\n::field{name=\"currency\" type=\"string\"}\nISO 4217 currency code associated with the country (e.g. `'EUR'`).\n::\n\n::field{name=\"isp\" type=\"string\"}\nInternet Service Provider name.\n::\n\n::field{name=\"org\" type=\"string\"}\nOrganization name associated with the IP range.\n::\n\n::field{name=\"as_org\" type=\"string\"}\nAutonomous System organization name.\n::\n\n::field{name=\"proxy\" type=\"boolean\"}\nWhether the IP is associated with a known proxy service. Triggers anomaly detection when `true` and the user has not been verified via MFA.\n::\n\n::field{name=\"hosting\" type=\"boolean\"}\nWhether the IP belongs to a known hosting or datacenter provider. Same anomaly behavior as `proxy`.\n::\n::\n\n### User-agent fields\n\n::field-group\n::field{name=\"userAgent\" type=\"string\" required}\nThe raw `User-Agent` header string, stored as-is for logging and comparison.\n::\n\n::field{name=\"device\" type=\"string\" required}\nDevice type classification (e.g. `'desktop'`, `'mobile'`, `'tablet'`).\n::\n\n::field{name=\"deviceVendor\" type=\"string\"}\nDevice manufacturer (e.g. `'Apple'`, `'Samsung'`).\n::\n\n::field{name=\"deviceModel\" type=\"string\"}\nDevice model name (e.g. `'iPhone'`, `'Galaxy S24'`).\n::\n\n::field{name=\"browser\" type=\"string\"}\nBrowser name (e.g. `'Chrome'`, `'Safari'`, `'Firefox'`).\n::\n\n::field{name=\"browserType\" type=\"string\"}\nBrowser engine or type classifier.\n::\n\n::field{name=\"browserVersion\" type=\"string\"}\nFull browser version string (e.g. `'125.0.0.0'`).\n::\n\n::field{name=\"os\" type=\"string\"}\nOperating system name (e.g. `'macOS'`, `'Windows 11'`, `'Android 14'`).\n::\n\n::field{name=\"botAI\" type=\"boolean\" required}\nWhether the user agent matches a known AI crawler pattern.\n::\n\n::field{name=\"bot\" type=\"boolean\" required}\nWhether the user agent matches a known bot pattern.\n::\n::\n\n::tip\nThe geolocation data comes from the [Bot Detector](\u002Fdocs\u002Fbot-detection) package, which uses  databases generated by [shield-base](\u002Fdocs\u002Fshield-base). The accuracy of `city`, `lat`, and `lon` depends on the database edition and the client's ISP.\n::\n\n---\n\n## Where fingerprints are collected\n\nThe `getFingerPrint` middleware is mounted on routes where the service needs to verify or update the client's identity:\n\n| Route | Purpose |\n|---|---|\n| `POST \u002Fauth\u002Fuser\u002Frefresh-session` | Token rotation. Fingerprint feeds into `strangeThings()` anomaly checks. |\n| `POST \u002Fauth\u002Fverify-mfa` | Adaptive MFA verification. Fingerprint is persisted via `updateVisitors` on success. |\n| `POST \u002Fcustom\u002Fmfa\u002F:reason` | Custom MFA initiation. Fingerprint metadata is included in the OTP email. |\n| `GET \u002Fauth\u002Fverify-custom-mfa` | Custom MFA link validation. |\n| `POST \u002Fauth\u002Fverify-custom-mfa` | Custom MFA code submission. |\n| `POST \u002Fupdate\u002Femail` | Email update. Fingerprint is persisted after successful verification. |\n| `GET \u002Fauth\u002Freset-password` | Password reset link validation. |\n| `POST \u002Fauth\u002Freset-password` | Password reset submission. |\n| `GET \u002Fsecret\u002Fdata` | BFF access route. |\n| `GET \u002Fsecret\u002Faccesstoken\u002Fmetadata` | BFF metadata route. |\n\n::note\nThe login and signup controllers do not use `getFingerPrint` middleware directly. They extract device metadata through `parseUA` and `getGeoData` inline and pass it to the visitor creation flow.\n::\n\n---\n\n## Visitor persistence\n\nWhen a user completes MFA verification or logs in from a new device, the service calls `updateVisitors` from the [Bot Detector](\u002Fdocs\u002Fbot-detection) package to persist the fingerprint to the `visitors` table. This stored record becomes the baseline for future anomaly comparisons.\n\nThe `visitors` table stores one record per `visitor_id` (linked to the `canary_id` cookie). Each record contains the full set of geolocation and user-agent fields from the `FingerPrint` interface. When `updateVisitors` is called, it overwrites the stored record with the current request's fingerprint data.\n\n### When fingerprints are persisted\n\n| Event | Trigger | Function |\n|---|---|---|\n| Login | Successful credential verification | `updateVisitors` called with parsed request data |\n| MFA verification | OTP code validated | `verifyMfaCode` calls `updateVisitors` after token rotation |\n| Visitor trust | New device verified | `trustVisitor` calls `updateVisitors` inside a transaction |\n\n### `trustVisitor`\n\n`trustVisitor` is called when a user verifies a new device through MFA. It performs two operations inside a single database transaction:\n\n1. Updates the user's `visitor_id` to point to the newly verified visitor record\n2. Calls `updateVisitors` to persist the current fingerprint to that visitor record\n\nIf `updateVisitors` fails, the transaction rolls back and the user's `visitor_id` is not updated. This ensures the user is never associated with a visitor record that has incomplete fingerprint data.\n\n```ts\nimport { trustVisitor } from '@riavzon\u002Fauth'\n\nconst result = await trustVisitor(\n  userId,\n  visitorIdToTrust,\n  canaryId,\n  req.fingerPrint,\n  log\n)\n\nif (!result.ok) {\n  \u002F\u002F Transaction failed — visitor not trusted\n  log.error(result.data)\n}\n```\n\n---\n\n## Anomaly detection integration\n\nThe stored fingerprint is the reference point for [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies). When `strangeThings()` runs on a refresh-token use, it joins the `refresh_tokens`, `users`, and `visitors` tables to compare the incoming request against the stored visitor record.\n\nThe following fingerprint fields are compared:\n\n| Field | Anomaly type | Result |\n|---|---|---|\n| `canary_id` cookie vs stored `canary_id` | Device change | `reqMFA: true` (new device) |\n| `ipAddress` vs stored IP range | Network change | `reqMFA: true` (different network) |\n| `proxy` flag | Proxy detected | `reqMFA: true` if `proxy_allowed = 0` |\n| `hosting` flag | Hosting\u002Fdatacenter detected | `reqMFA: true` if `hosting_allowed = 0` |\n| `device` type | Device type change | `reqMFA: true` |\n| `browser` name | Browser change | `reqMFA: true` |\n| `os` name | OS change | `reqMFA: true` |\n| `lat` \u002F `lon` coordinates | Geographic shift | `reqMFA: true` (significant distance) |\n\n::warning\nAfter a successful MFA verification, `verifyMfaCode` sets `proxy_allowed = 1` and `hosting_allowed = 1` on the visitor record. This grants trusted status to the verified device, suppressing proxy and hosting anomalies until the next suspicious event resets them.\n::\n\n---\n\n## The `canary_id` lifecycle\n\nThe `canary_id` is a long-lived browser cookie that serves as the persistent device identifier. It ties a specific browser instance to a `visitor_id` in the database.\n\n::steps{level=\"4\"}\n#### Cookie creation\n\nA `canary_id` cookie is set when a user first interacts with the authentication system (signup or login). The value is a randomly generated identifier.\n\n#### Storage linkage\n\nThe `canary_id` is stored alongside the user's `visitor_id` in the `visitors` table. The `users` table references the `visitor_id`, creating a chain: `user → visitor_id → visitors table → canary_id`.\n\n#### Anomaly comparison\n\nOn every authenticated request, `strangeThings()` compares the `canary_id` from the incoming cookie against the value stored in the database. A mismatch indicates a new or different device, triggering an MFA challenge.\n\n#### XSS integration\n\nWhen an [XSS attempt](\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss) is detected, `handleXSS` uses the `canary_id` to permanently mark the corresponding visitor as a bot and update the banned IP record. This means the ban follows the device, not just the IP.\n::\n\n---\n\n## Accessing fingerprints in custom handlers\n\nAfter the `getFingerPrint` middleware runs, the full fingerprint is available on `req.fingerPrint`. Use it for logging, analytics, or custom security logic.\n\n```ts\nimport { getFingerPrint } from '@riavzon\u002Fauth'\n\nrouter.post('\u002Ftransfer',\n  getFingerPrint,\n  async (req, res) => {\n    const fp = req.fingerPrint\n\n    \u002F\u002F Log the request context\n    log.info({\n      ip: fp.ipAddress,\n      country: fp.country,\n      browser: fp.browser,\n      device: fp.device,\n      proxy: fp.proxy,\n    }, 'Transfer request')\n\n    \u002F\u002F Block requests from hosting providers\n    if (fp.hosting) {\n      return res.status(403).json({ error: 'Requests from hosting providers are not allowed.' })\n    }\n\n    \u002F\u002F Include location in the confirmation email\n    const location = [fp.city, fp.regionName, fp.country].filter(Boolean).join(', ')\n\n    await sendConfirmationEmail(user.email, {\n      amount: req.body.amount,\n      location,\n      device: `${fp.browser} on ${fp.os}`,\n    })\n  }\n)\n```\n\n::tip\nThe `getFingerPrint` middleware fails gracefully. If `getGeoData` or `parseUA` throws (,the IP geolocation database is unavailable), the middleware logs the error and calls `next()` without populating `req.fingerPrint`. Always check for `undefined` fields when accessing geolocation data in production.\n::",{"title":127,"description":2594},"j4oMoTbqOoRsIAfggUCSef2yEte_Lx3fn5EDPd4aSl0",[2603,2604],{"title":123,"path":124,"stem":125,"children":-1},{"title":131,"path":132,"stem":133,"children":-1},{"id":851,"title":127,"body":2606,"description":2594,"extension":2595,"icon":2596,"meta":3898,"module":2598,"navigation":8,"path":128,"rawbody":2599,"seo":3899,"stem":129,"__hash__":2601},{"type":853,"value":2607,"toc":3884},[2608,2618,2620,2622,2624,2634,2748,2754,2756,2842,2844,2916,2930,2932,2934,2938,3034,3044,3046,3048,3056,3068,3070,3118,3122,3126,3136,3142,3264,3266,3268,3280,3282,3400,3410,3412,3416,3422,3464,3466,3468,3474,3866,3882],[856,2609,858,2610,863,2612,867,2614,872,2616,876],{},[860,2611,862],{},[860,2613,866],{},[869,2615,871],{"href":96},[869,2617,875],{"href":124},[856,2619,879],{},[881,2621],{},[884,2623,887],{"id":886},[856,2625,890,2626,893,2628,896,2630,900,2632,904],{},[860,2627,862],{},[869,2629,399],{"href":35},[860,2631,899],{},[860,2633,903],{},[906,2635,2636],{"className":908,"code":909,"language":910,"meta":911,"style":911},[860,2637,2638,2656,2660,2664,2702,2740,2744],{"__ignoreMap":911},[915,2639,2640,2642,2644,2646,2648,2650,2652,2654],{"class":917,"line":918},[915,2641,922],{"class":921},[915,2643,926],{"class":925},[915,2645,862],{"class":929},[915,2647,932],{"class":925},[915,2649,935],{"class":921},[915,2651,939],{"class":938},[915,2653,943],{"class":942},[915,2655,946],{"class":938},[915,2657,2658],{"class":917,"line":949},[915,2659,952],{"emptyLinePlaceholder":8},[915,2661,2662],{"class":917,"line":955},[915,2663,959],{"class":958},[915,2665,2666,2668,2670,2672,2674,2676,2678,2680,2682,2684,2686,2688,2690,2692,2694,2696,2698,2700],{"class":917,"line":962},[915,2667,965],{"class":929},[915,2669,968],{"class":925},[915,2671,972],{"class":971},[915,2673,975],{"class":925},[915,2675,978],{"class":938},[915,2677,981],{"class":942},[915,2679,978],{"class":938},[915,2681,872],{"class":925},[915,2683,862],{"class":929},[915,2685,872],{"class":925},[915,2687,993],{"class":992},[915,2689,996],{"class":925},[915,2691,1000],{"class":999},[915,2693,872],{"class":925},[915,2695,1005],{"class":999},[915,2697,1008],{"class":925},[915,2699,1011],{"class":992},[915,2701,1014],{"class":925},[915,2703,2704,2706,2708,2710,2712,2714,2716,2718,2720,2722,2724,2726,2728,2730,2732,2734,2736,2738],{"class":917,"line":1017},[915,2705,1020],{"class":992},[915,2707,926],{"class":925},[915,2709,1026],{"class":1025},[915,2711,872],{"class":925},[915,2713,1031],{"class":1025},[915,2715,872],{"class":925},[915,2717,1036],{"class":1025},[915,2719,872],{"class":925},[915,2721,1041],{"class":1025},[915,2723,872],{"class":925},[915,2725,1046],{"class":1025},[915,2727,872],{"class":925},[915,2729,1051],{"class":1025},[915,2731,932],{"class":925},[915,2733,1057],{"class":1056},[915,2735,1060],{"class":929},[915,2737,968],{"class":925},[915,2739,1065],{"class":929},[915,2741,2742],{"class":917,"line":1068},[915,2743,1071],{"class":958},[915,2745,2746],{"class":917,"line":1074},[915,2747,1077],{"class":925},[856,2749,1080,2750,1083,2752,1087],{},[860,2751,866],{},[860,2753,1086],{},[1089,2755,1092],{"id":1091},[1094,2757,2758,2764,2770,2776,2782,2788,2794,2798,2802,2806,2812,2818,2822,2826,2830,2836],{},[1097,2759,2760],{"name":1026,"type":1099,":required":1100},[856,2761,1103,2762,1107],{},[860,2763,1106],{},[1097,2765,2766],{"name":1031,"type":1099},[856,2767,1112,2768,1116],{},[860,2769,1115],{},[1097,2771,2772],{"name":1119,"type":1099},[856,2773,1122,2774,1116],{},[860,2775,1125],{},[1097,2777,2778],{"name":1128,"type":1099},[856,2779,1131,2780,1135],{},[860,2781,1134],{},[1097,2783,2784],{"name":1138,"type":1099},[856,2785,1141,2786,1116],{},[860,2787,1144],{},[1097,2789,2790],{"name":1036,"type":1099},[856,2791,1149,2792,1116],{},[860,2793,1144],{},[1097,2795,2796],{"name":1154,"type":1099},[856,2797,1157],{},[1097,2799,2800],{"name":1160,"type":1099},[856,2801,1163],{},[1097,2803,2804],{"name":1166,"type":1099},[856,2805,1169],{},[1097,2807,2808],{"name":1172,"type":1099},[856,2809,1175,2810,1116],{},[860,2811,1178],{},[1097,2813,2814],{"name":1181,"type":1099},[856,2815,1184,2816,1116],{},[860,2817,1187],{},[1097,2819,2820],{"name":1190,"type":1099},[856,2821,1193],{},[1097,2823,2824],{"name":1196,"type":1099},[856,2825,1199],{},[1097,2827,2828],{"name":1202,"type":1099},[856,2829,1205],{},[1097,2831,2832],{"name":1208,"type":1209},[856,2833,1212,2834,1215],{},[860,2835,1100],{},[1097,2837,2838],{"name":1218,"type":1209},[856,2839,1221,2840,968],{},[860,2841,1208],{},[1089,2843,1227],{"id":1226},[1094,2845,2846,2852,2862,2870,2878,2888,2892,2898,2908,2912],{},[1097,2847,2848],{"name":1232,"type":1099,":required":1100},[856,2849,1235,2850,1239],{},[860,2851,1238],{},[1097,2853,2854],{"name":1051,"type":1099,":required":1100},[856,2855,1244,2856,872,2858,872,2860,1116],{},[860,2857,1247],{},[860,2859,1250],{},[860,2861,1253],{},[1097,2863,2864],{"name":1256,"type":1099},[856,2865,1259,2866,872,2868,1116],{},[860,2867,1262],{},[860,2869,1265],{},[1097,2871,2872],{"name":1268,"type":1099},[856,2873,1271,2874,872,2876,1116],{},[860,2875,1274],{},[860,2877,1277],{},[1097,2879,2880],{"name":1041,"type":1099},[856,2881,1282,2882,872,2884,872,2886,1116],{},[860,2883,1285],{},[860,2885,1288],{},[860,2887,1291],{},[1097,2889,2890],{"name":1294,"type":1099},[856,2891,1297],{},[1097,2893,2894],{"name":1300,"type":1099},[856,2895,1303,2896,1116],{},[860,2897,1306],{},[1097,2899,2900],{"name":1046,"type":1099},[856,2901,1311,2902,872,2904,872,2906,1116],{},[860,2903,1314],{},[860,2905,1317],{},[860,2907,1320],{},[1097,2909,2910],{"name":1323,"type":1209,":required":1100},[856,2911,1326],{},[1097,2913,2914],{"name":1329,"type":1209,":required":1100},[856,2915,1332],{},[1334,2917,2918],{},[856,2919,1338,2920,1341,2922,1345,2924,872,2926,1350,2928,1353],{},[869,2921,399],{"href":35},[869,2923,1344],{"href":42},[860,2925,1036],{},[860,2927,1160],{},[860,2929,1166],{},[881,2931],{},[884,2933,1359],{"id":1358},[856,2935,890,2936,1364],{},[860,2937,862],{},[1366,2939,2940,2948],{},[1369,2941,2942],{},[1372,2943,2944,2946],{},[1375,2945,1377],{},[1375,2947,1380],{},[1382,2949,2950,2960,2970,2978,2986,2994,3002,3010,3018,3026],{},[1372,2951,2952,2956],{},[1387,2953,2954],{},[860,2955,1391],{},[1387,2957,1394,2958,1398],{},[860,2959,1397],{},[1372,2961,2962,2966],{},[1387,2963,2964],{},[860,2965,1405],{},[1387,2967,1408,2968,1412],{},[860,2969,1411],{},[1372,2971,2972,2976],{},[1387,2973,2974],{},[860,2975,1419],{},[1387,2977,1422],{},[1372,2979,2980,2984],{},[1387,2981,2982],{},[860,2983,1429],{},[1387,2985,1432],{},[1372,2987,2988,2992],{},[1387,2989,2990],{},[860,2991,1439],{},[1387,2993,1442],{},[1372,2995,2996,3000],{},[1387,2997,2998],{},[860,2999,1449],{},[1387,3001,1452],{},[1372,3003,3004,3008],{},[1387,3005,3006],{},[860,3007,1459],{},[1387,3009,1462],{},[1372,3011,3012,3016],{},[1387,3013,3014],{},[860,3015,1469],{},[1387,3017,1472],{},[1372,3019,3020,3024],{},[1387,3021,3022],{},[860,3023,1479],{},[1387,3025,1482],{},[1372,3027,3028,3032],{},[1387,3029,3030],{},[860,3031,1489],{},[1387,3033,1492],{},[1494,3035,3036],{},[856,3037,1498,3038,1501,3040,1504,3042,1507],{},[860,3039,862],{},[860,3041,903],{},[860,3043,899],{},[881,3045],{},[884,3047,1513],{"id":1512},[856,3049,1516,3050,1519,3052,1522,3054,1526],{},[860,3051,1411],{},[869,3053,399],{"href":35},[860,3055,1525],{},[856,3057,890,3058,1531,3060,1535,3062,1539,3064,1542,3066,1545],{},[860,3059,1525],{},[860,3061,1534],{},[860,3063,1538],{},[860,3065,1086],{},[860,3067,1411],{},[1089,3069,1549],{"id":1548},[1366,3071,3072,3082],{},[1369,3073,3074],{},[1372,3075,3076,3078,3080],{},[1375,3077,1558],{},[1375,3079,1561],{},[1375,3081,1564],{},[1382,3083,3084,3094,3106],{},[1372,3085,3086,3088,3090],{},[1387,3087,103],{},[1387,3089,1573],{},[1387,3091,3092,1578],{},[860,3093,1411],{},[1372,3095,3096,3098,3100],{},[1387,3097,875],{},[1387,3099,1585],{},[1387,3101,3102,1591,3104,1594],{},[860,3103,1590],{},[860,3105,1411],{},[1372,3107,3108,3110,3112],{},[1387,3109,1599],{},[1387,3111,1602],{},[1387,3113,3114,1591,3116,1610],{},[860,3115,1607],{},[860,3117,1411],{},[1089,3119,3120],{"id":1613},[860,3121,1607],{},[856,3123,3124,1620],{},[860,3125,1607],{},[1622,3127,3128,3132],{},[1625,3129,1627,3130,1630],{},[860,3131,1534],{},[1625,3133,1633,3134,1636],{},[860,3135,1411],{},[856,3137,1639,3138,1642,3140,1645],{},[860,3139,1411],{},[860,3141,1534],{},[906,3143,3144],{"className":908,"code":1648,"language":910,"meta":911,"style":911},[860,3145,3146,3164,3168,3182,3188,3194,3200,3210,3214,3218,3222,3238,3242,3260],{"__ignoreMap":911},[915,3147,3148,3150,3152,3154,3156,3158,3160,3162],{"class":917,"line":918},[915,3149,922],{"class":921},[915,3151,926],{"class":925},[915,3153,1607],{"class":929},[915,3155,932],{"class":925},[915,3157,935],{"class":921},[915,3159,939],{"class":938},[915,3161,943],{"class":942},[915,3163,946],{"class":938},[915,3165,3166],{"class":917,"line":949},[915,3167,952],{"emptyLinePlaceholder":8},[915,3169,3170,3172,3174,3176,3178,3180],{"class":917,"line":955},[915,3171,1677],{"class":992},[915,3173,1680],{"class":1025},[915,3175,1683],{"class":1056},[915,3177,1686],{"class":921},[915,3179,1689],{"class":971},[915,3181,1692],{"class":925},[915,3183,3184,3186],{"class":917,"line":962},[915,3185,1697],{"class":929},[915,3187,1700],{"class":925},[915,3189,3190,3192],{"class":917,"line":1017},[915,3191,1705],{"class":929},[915,3193,1700],{"class":925},[915,3195,3196,3198],{"class":917,"line":1068},[915,3197,1712],{"class":929},[915,3199,1700],{"class":925},[915,3201,3202,3204,3206,3208],{"class":917,"line":1074},[915,3203,1719],{"class":929},[915,3205,968],{"class":925},[915,3207,1724],{"class":929},[915,3209,1700],{"class":925},[915,3211,3212],{"class":917,"line":1729},[915,3213,1732],{"class":929},[915,3215,3216],{"class":917,"line":1735},[915,3217,1738],{"class":925},[915,3219,3220],{"class":917,"line":1741},[915,3221,952],{"emptyLinePlaceholder":8},[915,3223,3224,3226,3228,3230,3232,3234,3236],{"class":917,"line":1746},[915,3225,1749],{"class":921},[915,3227,996],{"class":925},[915,3229,1754],{"class":1056},[915,3231,1757],{"class":929},[915,3233,968],{"class":925},[915,3235,1762],{"class":929},[915,3237,1765],{"class":925},[915,3239,3240],{"class":917,"line":1768},[915,3241,1771],{"class":958},[915,3243,3244,3246,3248,3250,3252,3254,3256,3258],{"class":917,"line":1774},[915,3245,1777],{"class":929},[915,3247,968],{"class":925},[915,3249,1782],{"class":971},[915,3251,975],{"class":925},[915,3253,1757],{"class":929},[915,3255,968],{"class":925},[915,3257,1791],{"class":929},[915,3259,1738],{"class":925},[915,3261,3262],{"class":917,"line":1796},[915,3263,1799],{"class":925},[881,3265],{},[884,3267,1805],{"id":1804},[856,3269,1808,3270,1811,3272,1814,3274,872,3276,1350,3278,1823],{},[869,3271,871],{"href":96},[860,3273,1397],{},[860,3275,1817],{},[860,3277,1820],{},[860,3279,1525],{},[856,3281,1826],{},[1366,3283,3284,3294],{},[1369,3285,3286],{},[1372,3287,3288,3290,3292],{},[1375,3289,1835],{},[1375,3291,1838],{},[1375,3293,1841],{},[1382,3295,3296,3310,3322,3336,3350,3362,3374,3386],{},[1372,3297,3298,3304,3306],{},[1387,3299,3300,1850,3302],{},[860,3301,1538],{},[860,3303,1538],{},[1387,3305,1855],{},[1387,3307,3308,1861],{},[860,3309,1860],{},[1372,3311,3312,3316,3318],{},[1387,3313,3314,1868],{},[860,3315,1026],{},[1387,3317,1871],{},[1387,3319,3320,1876],{},[860,3321,1860],{},[1372,3323,3324,3328,3330],{},[1387,3325,3326,1883],{},[860,3327,1208],{},[1387,3329,1886],{},[1387,3331,3332,1891,3334],{},[860,3333,1860],{},[860,3335,1894],{},[1372,3337,3338,3342,3344],{},[1387,3339,3340,1883],{},[860,3341,1218],{},[1387,3343,1903],{},[1387,3345,3346,1891,3348],{},[860,3347,1860],{},[860,3349,1910],{},[1372,3351,3352,3356,3358],{},[1387,3353,3354,1917],{},[860,3355,1051],{},[1387,3357,1920],{},[1387,3359,3360],{},[860,3361,1860],{},[1372,3363,3364,3368,3370],{},[1387,3365,3366,1931],{},[860,3367,1041],{},[1387,3369,1934],{},[1387,3371,3372],{},[860,3373,1860],{},[1372,3375,3376,3380,3382],{},[1387,3377,3378,1931],{},[860,3379,1046],{},[1387,3381,1947],{},[1387,3383,3384],{},[860,3385,1860],{},[1372,3387,3388,3394,3396],{},[1387,3389,3390,1958,3392,1961],{},[860,3391,1160],{},[860,3393,1166],{},[1387,3395,1964],{},[1387,3397,3398,1969],{},[860,3399,1860],{},[1971,3401,3402],{},[856,3403,1975,3404,1978,3406,1504,3408,1985],{},[860,3405,1590],{},[860,3407,1981],{},[860,3409,1984],{},[881,3411],{},[884,3413,890,3414,1993],{"id":1990},[860,3415,1538],{},[856,3417,890,3418,1998,3420,2001],{},[860,3419,1538],{},[860,3421,1534],{},[2003,3423,3424,3426,3430,3432,3446,3448,3454,3456],{"level":2005},[2007,3425,2010],{"id":2009},[856,3427,2013,3428,2016],{},[860,3429,1538],{},[2007,3431,2020],{"id":2019},[856,3433,890,3434,2025,3436,2028,3438,2031,3440,2034,3442,2037,3444,968],{},[860,3435,1538],{},[860,3437,1534],{},[860,3439,1525],{},[860,3441,1820],{},[860,3443,1534],{},[860,3445,2040],{},[2007,3447,2044],{"id":2043},[856,3449,2047,3450,2050,3452,2053],{},[860,3451,1397],{},[860,3453,1538],{},[2007,3455,2057],{"id":2056},[856,3457,2060,3458,2064,3460,2068,3462,2071],{},[869,3459,2063],{"href":140},[860,3461,2067],{},[860,3463,1538],{},[881,3465],{},[884,3467,2077],{"id":2076},[856,3469,2080,3470,2083,3472,2086],{},[860,3471,862],{},[860,3473,866],{},[906,3475,3476],{"className":908,"code":2089,"language":910,"meta":911,"style":911},[860,3477,3478,3496,3500,3518,3524,3542,3556,3560,3564,3574,3588,3602,3616,3630,3644,3656,3660,3664,3678,3710,3714,3718,3722,3776,3780,3796,3814,3820,3854,3858,3862],{"__ignoreMap":911},[915,3479,3480,3482,3484,3486,3488,3490,3492,3494],{"class":917,"line":918},[915,3481,922],{"class":921},[915,3483,926],{"class":925},[915,3485,862],{"class":929},[915,3487,932],{"class":925},[915,3489,935],{"class":921},[915,3491,939],{"class":938},[915,3493,943],{"class":942},[915,3495,946],{"class":938},[915,3497,3498],{"class":917,"line":949},[915,3499,952],{"emptyLinePlaceholder":8},[915,3501,3502,3504,3506,3508,3510,3512,3514,3516],{"class":917,"line":955},[915,3503,965],{"class":929},[915,3505,968],{"class":925},[915,3507,972],{"class":971},[915,3509,975],{"class":925},[915,3511,978],{"class":938},[915,3513,2128],{"class":942},[915,3515,978],{"class":938},[915,3517,1700],{"class":925},[915,3519,3520,3522],{"class":917,"line":962},[915,3521,2137],{"class":929},[915,3523,1700],{"class":925},[915,3525,3526,3528,3530,3532,3534,3536,3538,3540],{"class":917,"line":1017},[915,3527,2144],{"class":992},[915,3529,996],{"class":925},[915,3531,1000],{"class":999},[915,3533,872],{"class":925},[915,3535,1005],{"class":999},[915,3537,1008],{"class":925},[915,3539,1011],{"class":992},[915,3541,1014],{"class":925},[915,3543,3544,3546,3548,3550,3552,3554],{"class":917,"line":1068},[915,3545,2163],{"class":992},[915,3547,2166],{"class":1025},[915,3549,1683],{"class":1056},[915,3551,1060],{"class":929},[915,3553,968],{"class":925},[915,3555,1065],{"class":929},[915,3557,3558],{"class":917,"line":1074},[915,3559,952],{"emptyLinePlaceholder":8},[915,3561,3562],{"class":917,"line":1729},[915,3563,2183],{"class":958},[915,3565,3566,3568,3570,3572],{"class":917,"line":1735},[915,3567,2188],{"class":929},[915,3569,968],{"class":925},[915,3571,2193],{"class":971},[915,3573,2196],{"class":925},[915,3575,3576,3578,3580,3582,3584,3586],{"class":917,"line":1741},[915,3577,2201],{"class":929},[915,3579,2205],{"class":2204},[915,3581,2166],{"class":929},[915,3583,968],{"class":925},[915,3585,1026],{"class":929},[915,3587,1700],{"class":925},[915,3589,3590,3592,3594,3596,3598,3600],{"class":917,"line":1746},[915,3591,2218],{"class":929},[915,3593,2205],{"class":2204},[915,3595,2166],{"class":929},[915,3597,968],{"class":925},[915,3599,1031],{"class":929},[915,3601,1700],{"class":925},[915,3603,3604,3606,3608,3610,3612,3614],{"class":917,"line":1768},[915,3605,2233],{"class":929},[915,3607,2205],{"class":2204},[915,3609,2166],{"class":929},[915,3611,968],{"class":925},[915,3613,1041],{"class":929},[915,3615,1700],{"class":925},[915,3617,3618,3620,3622,3624,3626,3628],{"class":917,"line":1774},[915,3619,2248],{"class":929},[915,3621,2205],{"class":2204},[915,3623,2166],{"class":929},[915,3625,968],{"class":925},[915,3627,1051],{"class":929},[915,3629,1700],{"class":925},[915,3631,3632,3634,3636,3638,3640,3642],{"class":917,"line":1796},[915,3633,2263],{"class":929},[915,3635,2205],{"class":2204},[915,3637,2166],{"class":929},[915,3639,968],{"class":925},[915,3641,1208],{"class":929},[915,3643,1700],{"class":925},[915,3645,3646,3648,3650,3652,3654],{"class":917,"line":2276},[915,3647,2279],{"class":925},[915,3649,978],{"class":938},[915,3651,2284],{"class":942},[915,3653,978],{"class":938},[915,3655,1738],{"class":925},[915,3657,3658],{"class":917,"line":2291},[915,3659,952],{"emptyLinePlaceholder":8},[915,3661,3662],{"class":917,"line":2296},[915,3663,2299],{"class":958},[915,3665,3666,3668,3670,3672,3674,3676],{"class":917,"line":2302},[915,3667,2305],{"class":921},[915,3669,996],{"class":925},[915,3671,2310],{"class":929},[915,3673,968],{"class":925},[915,3675,1218],{"class":929},[915,3677,1765],{"class":925},[915,3679,3680,3682,3684,3686,3688,3690,3692,3694,3696,3698,3700,3702,3704,3706,3708],{"class":917,"line":2319},[915,3681,2322],{"class":921},[915,3683,2325],{"class":929},[915,3685,968],{"class":925},[915,3687,2330],{"class":971},[915,3689,975],{"class":925},[915,3691,2336],{"class":2335},[915,3693,1116],{"class":925},[915,3695,5],{"class":971},[915,3697,2343],{"class":925},[915,3699,1782],{"class":929},[915,3701,2205],{"class":2204},[915,3703,939],{"class":938},[915,3705,2352],{"class":942},[915,3707,978],{"class":938},[915,3709,2357],{"class":925},[915,3711,3712],{"class":917,"line":2360},[915,3713,2363],{"class":925},[915,3715,3716],{"class":917,"line":2366},[915,3717,952],{"emptyLinePlaceholder":8},[915,3719,3720],{"class":917,"line":2371},[915,3721,2374],{"class":958},[915,3723,3724,3726,3728,3730,3732,3734,3736,3738,3740,3742,3744,3746,3748,3750,3752,3754,3756,3758,3760,3762,3764,3766,3768,3770,3772,3774],{"class":917,"line":2377},[915,3725,2163],{"class":992},[915,3727,2382],{"class":1025},[915,3729,1683],{"class":1056},[915,3731,2387],{"class":925},[915,3733,2310],{"class":929},[915,3735,968],{"class":925},[915,3737,1036],{"class":929},[915,3739,872],{"class":925},[915,3741,2310],{"class":929},[915,3743,968],{"class":925},[915,3745,1138],{"class":929},[915,3747,872],{"class":925},[915,3749,2310],{"class":929},[915,3751,968],{"class":925},[915,3753,1031],{"class":929},[915,3755,2412],{"class":925},[915,3757,2415],{"class":971},[915,3759,975],{"class":925},[915,3761,2420],{"class":929},[915,3763,1116],{"class":925},[915,3765,2425],{"class":971},[915,3767,975],{"class":925},[915,3769,978],{"class":938},[915,3771,872],{"class":942},[915,3773,978],{"class":938},[915,3775,1738],{"class":925},[915,3777,3778],{"class":917,"line":2438},[915,3779,952],{"emptyLinePlaceholder":8},[915,3781,3782,3784,3786,3788,3790,3792,3794],{"class":917,"line":2443},[915,3783,2446],{"class":921},[915,3785,2449],{"class":971},[915,3787,975],{"class":925},[915,3789,2454],{"class":929},[915,3791,968],{"class":925},[915,3793,2459],{"class":929},[915,3795,2462],{"class":925},[915,3797,3798,3800,3802,3804,3806,3808,3810,3812],{"class":917,"line":2465},[915,3799,2468],{"class":929},[915,3801,2205],{"class":2204},[915,3803,1060],{"class":929},[915,3805,968],{"class":925},[915,3807,2477],{"class":929},[915,3809,968],{"class":925},[915,3811,2482],{"class":929},[915,3813,1700],{"class":925},[915,3815,3816,3818],{"class":917,"line":2487},[915,3817,2490],{"class":929},[915,3819,1700],{"class":925},[915,3821,3822,3824,3826,3828,3830,3832,3834,3836,3838,3840,3842,3844,3846,3848,3850,3852],{"class":917,"line":2495},[915,3823,2248],{"class":929},[915,3825,2205],{"class":2204},[915,3827,2502],{"class":942},[915,3829,2505],{"class":992},[915,3831,2310],{"class":929},[915,3833,968],{"class":2510},[915,3835,1041],{"class":929},[915,3837,2515],{"class":992},[915,3839,2518],{"class":942},[915,3841,2505],{"class":992},[915,3843,2310],{"class":929},[915,3845,968],{"class":2510},[915,3847,1046],{"class":929},[915,3849,2515],{"class":992},[915,3851,2531],{"class":942},[915,3853,1700],{"class":925},[915,3855,3856],{"class":917,"line":2536},[915,3857,2539],{"class":925},[915,3859,3860],{"class":917,"line":2542},[915,3861,2545],{"class":925},[915,3863,3864],{"class":917,"line":2548},[915,3865,1738],{"class":925},[1334,3867,3868],{},[856,3869,890,3870,2557,3872,2560,3874,2563,3876,2567,3878,2570,3880,2574],{},[860,3871,862],{},[860,3873,899],{},[860,3875,903],{},[860,3877,2566],{},[860,3879,866],{},[860,3881,2573],{},[2576,3883,2578],{},{"title":911,"searchDepth":949,"depth":949,"links":3885},[3886,3890,3891,3895,3896,3897],{"id":886,"depth":949,"text":887,"children":3887},[3888,3889],{"id":1091,"depth":955,"text":1092},{"id":1226,"depth":955,"text":1227},{"id":1358,"depth":949,"text":1359},{"id":1512,"depth":949,"text":1513,"children":3892},[3893,3894],{"id":1548,"depth":955,"text":1549},{"id":1613,"depth":955,"text":1607},{"id":1804,"depth":949,"text":1805},{"id":1990,"depth":949,"text":2592},{"id":2076,"depth":949,"text":2077},{},{"title":127,"description":2594},1780436282235]