[{"data":1,"prerenderedAt":10623},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fiam":64,"navigation":257,"navLinks_footer":837,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links_page":850,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links_surround":6241,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links":6244},{"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":115,"body":852,"description":6233,"extension":6234,"icon":6235,"meta":6236,"module":6237,"navigation":8,"path":116,"rawbody":6238,"seo":6239,"stem":117,"__hash__":6240},"docs\u002Fdocs\u002Fiam\u002F01.essentials\u002F08.magic-links.md",{"type":853,"value":854,"toc":6187},"minimark",[855,886,911,914,919,926,945,1133,1164,1168,1258,1261,1265,1270,1281,1283,1287,1293,1298,1328,1460,1471,1481,1483,1487,1501,1514,1588,1595,1597,1601,1604,1630,1653,1661,1673,1675,1679,1682,1686,1704,1710,1715,1760,1764,1770,1926,1930,1975,1979,1986,2061,2064,2119,2123,2129,2193,2196,2251,2253,2257,2263,2267,2270,2320,2332,2336,2349,2471,2481,2485,2488,2531,2545,2547,2551,2554,2598,2656,2660,2663,2703,2706,2726,2728,2732,2736,2742,2815,2823,2840,2844,2855,3202,3218,3229,3232,3241,3509,3518,3522,3525,4542,4552,4629,4633,4644,5215,5228,5239,5241,5244,5255,5292,5301,5303,5307,5316,5319,5379,5383,5392,5418,5422,5425,5475,5479,5517,5519,5522,5525,5692,5694,5698,5713,5717,5725,5846,5850,5859,5975,5979,5988,6069,6072,6076,6081,6180,6183],[856,857,858,859,863,864,868,869,872,873,876,877,880,881,885],"p",{},"Magic links are short-lived, single-use JWTs embedded as URL query parameters in emails sent to the user. The IAM service uses them for four built-in flows: ",[860,861,862],"strong",{},"adaptive MFA"," (triggered by ",[865,866,867],"a",{"href":96},"anomaly detection","), ",[860,870,871],{},"password reset",", ",[860,874,875],{},"email update",", and ",[860,878,879],{},"custom MFA"," for app-defined sensitive actions. All four flows share the same signing, caching, and verification infrastructure, differing only in the ",[882,883,884],"code",{},"purpose"," value carried in the JWT payload and the route that processes them.",[856,887,888,889,872,892,895,896,899,900,903,904,910],{},"Each magic link carries four query parameters: ",[882,890,891],{},"visitor",[882,893,894],{},"token"," (the signed JWT), ",[882,897,898],{},"random"," (a SHA-256-hashed challenge), and ",[882,901,902],{},"reason"," (the purpose string). Links expire after a configurable window (default 15 minutes), are cached in a server-side ",[865,905,909],{"href":906,"rel":907},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCache_replacement_policies#LRU",[908],"nofollow","LRU"," store, and are consumed on use so that a verified link cannot be replayed even if the JWT itself has not expired yet.",[912,913],"hr",{},[915,916,918],"h2",{"id":917},"signing","Signing",[920,921,923],"h3",{"id":922},"tempjwtlink",[882,924,925],{},"tempJwtLink",[856,927,928,930,931,936,937,940,941,944],{},[882,929,925],{}," is the low-level function that creates a magic link JWT. It accepts a typed payload, signs it with ",[865,932,935],{"href":933,"rel":934},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHMAC",[908],"HMAC-SHA512"," using a dedicated secret key (",[882,938,939],{},"magic_links.jwt_secret_key","), and stores the resulting token in the magic links cache with ",[882,942,943],{},"valid: true",".",[946,947,952],"pre",{"className":948,"code":949,"language":950,"meta":951,"style":951},"language-ts shiki shiki-themes light-plus light-plus dracula","import { tempJwtLink } from '@riavzon\u002Fauth'\n\nconst token = tempJwtLink\u003C{ customField: string }>({\n  visitor: user.visitor_id,\n  subject: String(user.id),\n  purpose: 'PASSWORD_RESET',\n  jti: crypto.randomUUID(),\n  customField: 'value',\n})\n","ts","",[882,953,954,987,993,1028,1048,1073,1091,1110,1127],{"__ignoreMap":951},[955,956,959,963,967,970,973,976,980,984],"span",{"class":957,"line":958},"line",1,[955,960,962],{"class":961},"sZ328","import",[955,964,966],{"class":965},"sDd4n"," { ",[955,968,925],{"class":969},"sjsA6",[955,971,972],{"class":965}," } ",[955,974,975],{"class":961},"from",[955,977,979],{"class":978},"sFkSl"," '",[955,981,983],{"class":982},"sFB1V","@riavzon\u002Fauth",[955,985,986],{"class":978},"'\n",[955,988,990],{"class":957,"line":989},2,[955,991,992],{"emptyLinePlaceholder":8},"\n",[955,994,996,1000,1004,1008,1012,1015,1018,1021,1025],{"class":957,"line":995},3,[955,997,999],{"class":998},"sl46w","const",[955,1001,1003],{"class":1002},"s3JHE"," token",[955,1005,1007],{"class":1006},"saOXh"," =",[955,1009,1011],{"class":1010},"sHOzp"," tempJwtLink",[955,1013,1014],{"class":965},"\u003C{ ",[955,1016,1017],{"class":969},"customField",[955,1019,1020],{"class":1006},":",[955,1022,1024],{"class":1023},"sFs1U"," string",[955,1026,1027],{"class":965}," }>({\n",[955,1029,1031,1034,1037,1040,1042,1045],{"class":957,"line":1030},4,[955,1032,1033],{"class":969},"  visitor",[955,1035,1020],{"class":1036},"s34zl",[955,1038,1039],{"class":969}," user",[955,1041,944],{"class":965},[955,1043,1044],{"class":969},"visitor_id",[955,1046,1047],{"class":965},",\n",[955,1049,1051,1054,1056,1059,1062,1065,1067,1070],{"class":957,"line":1050},5,[955,1052,1053],{"class":969},"  subject",[955,1055,1020],{"class":1036},[955,1057,1058],{"class":1010}," String",[955,1060,1061],{"class":965},"(",[955,1063,1064],{"class":969},"user",[955,1066,944],{"class":965},[955,1068,1069],{"class":969},"id",[955,1071,1072],{"class":965},"),\n",[955,1074,1076,1079,1081,1083,1086,1089],{"class":957,"line":1075},6,[955,1077,1078],{"class":969},"  purpose",[955,1080,1020],{"class":1036},[955,1082,979],{"class":978},[955,1084,1085],{"class":982},"PASSWORD_RESET",[955,1087,1088],{"class":978},"'",[955,1090,1047],{"class":965},[955,1092,1094,1097,1099,1102,1104,1107],{"class":957,"line":1093},7,[955,1095,1096],{"class":969},"  jti",[955,1098,1020],{"class":1036},[955,1100,1101],{"class":969}," crypto",[955,1103,944],{"class":965},[955,1105,1106],{"class":1010},"randomUUID",[955,1108,1109],{"class":965},"(),\n",[955,1111,1113,1116,1118,1120,1123,1125],{"class":957,"line":1112},8,[955,1114,1115],{"class":969},"  customField",[955,1117,1020],{"class":1036},[955,1119,979],{"class":978},[955,1121,1122],{"class":982},"value",[955,1124,1088],{"class":978},[955,1126,1047],{"class":965},[955,1128,1130],{"class":957,"line":1129},9,[955,1131,1132],{"class":965},"})\n",[856,1134,1135,1136,1139,1140,872,1143,872,1146,872,1149,872,1152,1155,1156,1159,1160,1163],{},"The JWT is signed with ",[882,1137,1138],{},"HS512"," and includes standard claims (",[882,1141,1142],{},"iss",[882,1144,1145],{},"sub",[882,1147,1148],{},"aud",[882,1150,1151],{},"jti",[882,1153,1154],{},"exp","). The expiry is pulled from ",[882,1157,1158],{},"magic_links.expiresIn"," (default ",[882,1161,1162],{},"'15m'",").",[920,1165,1167],{"id":1166},"payload-fields","Payload fields",[1169,1170,1171,1187],"table",{},[1172,1173,1174],"thead",{},[1175,1176,1177,1181,1184],"tr",{},[1178,1179,1180],"th",{},"Field",[1178,1182,1183],{},"Type",[1178,1185,1186],{},"Description",[1188,1189,1190,1208,1222,1245],"tbody",{},[1175,1191,1192,1197,1202],{},[1193,1194,1195],"td",{},[882,1196,891],{},[1193,1198,1199],{},[882,1200,1201],{},"string",[1193,1203,1204,1205,1207],{},"The ",[882,1206,1044],{}," foreign key on the user record, used to bind the link to a specific device context",[1175,1209,1210,1215,1219],{},[1193,1211,1212],{},[882,1213,1214],{},"subject",[1193,1216,1217],{},[882,1218,1201],{},[1193,1220,1221],{},"User ID that identifies the recipient",[1175,1223,1224,1228,1232],{},[1193,1225,1226],{},[882,1227,884],{},[1193,1229,1230],{},[882,1231,1201],{},[1193,1233,1234,1235,872,1238,872,1241,1244],{},"Identifies the flow: ",[882,1236,1237],{},"'PASSWORD_RESET'",[882,1239,1240],{},"'MAGIC_LINK_MFA_CHECKS'",[882,1242,1243],{},"'change_email'",", or a custom string",[1175,1246,1247,1251,1255],{},[1193,1248,1249],{},[882,1250,1151],{},[1193,1252,1253],{},[882,1254,1201],{},[1193,1256,1257],{},"Unique JWT ID, used for replay prevention",[856,1259,1260],{},"The type parameter lets you extend the payload with additional fields. Any extra properties are encoded into the JWT and available after verification.",[920,1262,1264],{"id":1263},"jti-generation","JTI generation",[856,1266,1204,1267,1269],{},[882,1268,1151],{}," is constructed differently depending on the flow:",[856,1271,1272,1273,1276,1277,1280],{},"All flows use the same construction: ",[882,1274,1275],{},"crypto.randomUUID()"," concatenated with ",[882,1278,1279],{},"crypto.randomBytes(64).toString('hex')",", producing a 164-character string (36-char UUID + 128 hex chars from 64 bytes).",[912,1282],{},[915,1284,1286],{"id":1285},"verification","Verification",[920,1288,1290],{"id":1289},"verifytempjwtlink",[882,1291,1292],{},"verifyTempJwtLink",[856,1294,1295,1297],{},[882,1296,1292],{}," validates a magic link token through three checks:",[1299,1300,1301,1310,1319],"ol",{},[1302,1303,1304,1307,1308],"li",{},[860,1305,1306],{},"Cache lookup"," - the token must exist in the LRU cache and have ",[882,1309,943],{},[1302,1311,1312,1315,1316,1318],{},[860,1313,1314],{},"JWT verification"," - signature, expiry, issuer, subject, audience, and ",[882,1317,1151],{}," are all validated against the magic link secret",[1302,1320,1321,1324,1325,1327],{},[860,1322,1323],{},"Visitor match"," - the ",[882,1326,891],{}," claim in the JWT is compared against the visitor context of the current request",[946,1329,1331],{"className":948,"code":1330,"language":950,"meta":951,"style":951},"import { verifyTempJwtLink } from '@riavzon\u002Fauth'\n\nconst result = verifyTempJwtLink\u003C{ customField?: string }>(rawToken)\n\nif (!result.valid) {\n  \u002F\u002F result includes the failure reason\n  return\n}\n\nconst { subject, purpose, customField } = result.payload\n",[882,1332,1333,1351,1355,1385,1389,1411,1417,1422,1427,1431],{"__ignoreMap":951},[955,1334,1335,1337,1339,1341,1343,1345,1347,1349],{"class":957,"line":958},[955,1336,962],{"class":961},[955,1338,966],{"class":965},[955,1340,1292],{"class":969},[955,1342,972],{"class":965},[955,1344,975],{"class":961},[955,1346,979],{"class":978},[955,1348,983],{"class":982},[955,1350,986],{"class":978},[955,1352,1353],{"class":957,"line":989},[955,1354,992],{"emptyLinePlaceholder":8},[955,1356,1357,1359,1362,1364,1367,1369,1371,1374,1376,1379,1382],{"class":957,"line":995},[955,1358,999],{"class":998},[955,1360,1361],{"class":1002}," result",[955,1363,1007],{"class":1006},[955,1365,1366],{"class":1010}," verifyTempJwtLink",[955,1368,1014],{"class":965},[955,1370,1017],{"class":969},[955,1372,1373],{"class":1006},"?:",[955,1375,1024],{"class":1023},[955,1377,1378],{"class":965}," }>(",[955,1380,1381],{"class":969},"rawToken",[955,1383,1384],{"class":965},")\n",[955,1386,1387],{"class":957,"line":1030},[955,1388,992],{"emptyLinePlaceholder":8},[955,1390,1391,1394,1397,1400,1403,1405,1408],{"class":957,"line":1050},[955,1392,1393],{"class":961},"if",[955,1395,1396],{"class":965}," (",[955,1398,1399],{"class":1006},"!",[955,1401,1402],{"class":969},"result",[955,1404,944],{"class":965},[955,1406,1407],{"class":969},"valid",[955,1409,1410],{"class":965},") {\n",[955,1412,1413],{"class":957,"line":1075},[955,1414,1416],{"class":1415},"sghk6","  \u002F\u002F result includes the failure reason\n",[955,1418,1419],{"class":957,"line":1093},[955,1420,1421],{"class":961},"  return\n",[955,1423,1424],{"class":957,"line":1112},[955,1425,1426],{"class":965},"}\n",[955,1428,1429],{"class":957,"line":1129},[955,1430,992],{"emptyLinePlaceholder":8},[955,1432,1434,1436,1438,1440,1442,1444,1446,1448,1450,1453,1455,1457],{"class":957,"line":1433},10,[955,1435,999],{"class":998},[955,1437,966],{"class":965},[955,1439,1214],{"class":1002},[955,1441,872],{"class":965},[955,1443,884],{"class":1002},[955,1445,872],{"class":965},[955,1447,1017],{"class":1002},[955,1449,972],{"class":965},[955,1451,1452],{"class":1006},"=",[955,1454,1361],{"class":969},[955,1456,944],{"class":965},[955,1458,1459],{"class":969},"payload\n",[856,1461,1462,1463,1466,1467,1163],{},"If any check fails, the function returns ",[882,1464,1465],{},"{ valid: false }"," with a descriptive error. The cache entry is not removed on a failed verification attempt, allowing the user to retry (subject to ",[865,1468,1470],{"href":1469},"#usage-limits","usage limits",[1472,1473,1474],"warning",{},[856,1475,1476,1477,1480],{},"Use a separate ",[882,1478,1479],{},"jwt_secret_key"," for magic links. Do not reuse the access-token or refresh-token secret. This limits the risk if one secret is compromised.",[912,1482],{},[915,1484,1486],{"id":1485},"cache","Cache",[856,1488,1489,1490,1493,1494,1496,1497,1500],{},"The cache is a singleton ",[865,1491,909],{"href":906,"rel":1492},[908]," store keyed by the raw JWT string. Each entry stores a ",[882,1495,1407],{}," boolean and is automatically evicted after the configured TTL (",[882,1498,1499],{},"magic_links.expiresInMs",", default 15 minutes).",[856,1502,1503,1504,1506,1507,1509,1510,1513],{},"When a magic link is created, the token is inserted with ",[882,1505,943],{},". During verification, the cache is checked first. If the entry is missing (expired or evicted) or ",[882,1508,1407],{}," is ",[882,1511,1512],{},"false",", verification fails immediately without attempting JWT decoding.",[946,1515,1517],{"className":948,"code":1516,"language":950,"meta":951,"style":951},"import { magicLinksCache } from '@riavzon\u002Fauth'\n\nconst cache = magicLinksCache()\ncache.size        \u002F\u002F Current number of cached tokens\ncache.delete(jti) \u002F\u002F Force-expire a specific link (testing only)\n",[882,1518,1519,1538,1542,1557,1569],{"__ignoreMap":951},[955,1520,1521,1523,1525,1528,1530,1532,1534,1536],{"class":957,"line":958},[955,1522,962],{"class":961},[955,1524,966],{"class":965},[955,1526,1527],{"class":969},"magicLinksCache",[955,1529,972],{"class":965},[955,1531,975],{"class":961},[955,1533,979],{"class":978},[955,1535,983],{"class":982},[955,1537,986],{"class":978},[955,1539,1540],{"class":957,"line":989},[955,1541,992],{"emptyLinePlaceholder":8},[955,1543,1544,1546,1549,1551,1554],{"class":957,"line":995},[955,1545,999],{"class":998},[955,1547,1548],{"class":1002}," cache",[955,1550,1007],{"class":1006},[955,1552,1553],{"class":1010}," magicLinksCache",[955,1555,1556],{"class":965},"()\n",[955,1558,1559,1561,1563,1566],{"class":957,"line":1030},[955,1560,1485],{"class":969},[955,1562,944],{"class":965},[955,1564,1565],{"class":969},"size",[955,1567,1568],{"class":1415},"        \u002F\u002F Current number of cached tokens\n",[955,1570,1571,1573,1575,1578,1580,1582,1585],{"class":957,"line":1050},[955,1572,1485],{"class":969},[955,1574,944],{"class":965},[955,1576,1577],{"class":1010},"delete",[955,1579,1061],{"class":965},[955,1581,1151],{"class":969},[955,1583,1584],{"class":965},") ",[955,1586,1587],{"class":1415},"\u002F\u002F Force-expire a specific link (testing only)\n",[856,1589,1590,1591,1594],{},"Configure the cache size with ",[882,1592,1593],{},"magic_links.maxCacheEntries"," (default 500). The LRU eviction policy ensures the oldest unused entries are dropped first when the cache is full.",[912,1596],{},[915,1598,1600],{"id":1599},"url-construction","URL construction",[856,1602,1603],{},"All magic link emails build the destination URL from three config values:",[1605,1606,1607,1613,1619],"ul",{},[1302,1608,1609,1612],{},[882,1610,1611],{},"magic_links.domain",", the base URL",[1302,1614,1615,1618],{},[882,1616,1617],{},"magic_links.paths.pathFor*",", the path segment",[1302,1620,1621,1622,872,1624,872,1626,872,1628],{},"Query parameters: ",[882,1623,891],{},[882,1625,894],{},[882,1627,898],{},[882,1629,902],{},[856,1631,1204,1632,1634,1635,1638,1639,1642,1643,1645,1646,1648,1649,1652],{},[882,1633,898],{}," parameter is ",[882,1636,1637],{},"crypto.randomBytes(128).toString('hex')",", a 256-character hex string. The SHA-256 digest of that string is embedded in the signed JWT payload as ",[882,1640,1641],{},"randomHashed",". On verification the middleware hashes the incoming ",[882,1644,898],{}," and compares it against ",[882,1647,1641],{}," with ",[882,1650,1651],{},"crypto.timingSafeEqual"," to prevent timing attacks.",[946,1654,1659],{"className":1655,"code":1657,"language":1658},[1656],"language-text","https:\u002F\u002Fexample.com\u002Fauth\u002Fbounce?visitor=42&token=eyJ...&random=a3f8...&reason=MAGIC_LINK_MFA_CHECKS\n","text",[882,1660,1657],{"__ignoreMap":951},[856,1662,1663,1664,1667,1668,1672],{},"The bounce path is a thin redirect handler that forwards all query parameters to the application's verification page (default ",[882,1665,1666],{},"\u002Fauth\u002Fverify","). This indirection lets you host the auth service on a different domain from your application. See ",[865,1669,1671],{"href":1670},"\u002Fdocs\u002Fiam\u002Fauth-h3client","auth-h3client"," for how the client handles the redirect.",[912,1674],{},[915,1676,1678],{"id":1677},"built-in-flows","Built-in flows",[856,1680,1681],{},"The service ships with three built-in flows. Each one uses the same signing, caching, and verification process described above.",[920,1683,1685],{"id":1684},"adaptive-mfa","Adaptive MFA",[856,1687,1688,1689,1691,1692,1695,1696,1699,1700,1703],{},"Adaptive MFA is triggered automatically when ",[865,1690,867],{"href":96}," returns ",[882,1693,1694],{},"reqMFA: true",". This happens inside ",[882,1697,1698],{},"protectRoute"," (during access-token verification) and during token rotation (inside ",[882,1701,1702],{},"rotateOnEveryUse","). The user is not required to initiate this flow; the service detects the anomaly and sends the email automatically.",[856,1705,1706,1707,1709],{},"See ",[865,1708,123],{"href":124}," for the full adaptive MFA lifecycle, OTP code generation and verification, and the post-MFA bypass window.",[1711,1712,1714],"h4",{"id":1713},"trigger-points","Trigger points",[1169,1716,1717,1730],{},[1172,1718,1719],{},[1175,1720,1721,1724,1727],{},[1178,1722,1723],{},"Caller",[1178,1725,1726],{},"When",[1178,1728,1729],{},"Purpose",[1188,1731,1732,1744],{},[1175,1733,1734,1738,1741],{},[1193,1735,1736],{},[882,1737,1698],{},[1193,1739,1740],{},"Every authenticated request",[1193,1742,1743],{},"Anomaly detected during routine access-token check",[1175,1745,1746,1750,1757],{},[1193,1747,1748],{},[882,1749,1702],{},[1193,1751,1752,1753,1756],{},"Token rotation (",[882,1754,1755],{},"POST \u002Fauth\u002Fuser\u002Frefresh-session",")",[1193,1758,1759],{},"Anomaly detected during rotation",[1711,1761,1763],{"id":1762},"sending-the-link","Sending the link",[856,1765,1766,1769],{},[882,1767,1768],{},"sendTempMfaLink"," handles the full email delivery pipeline:",[1771,1772,1774,1778,1781,1858,1861,1865,1879,1883,1892,1896,1907,1911],"steps",{"level":1773},"4",[1711,1775,1777],{"id":1776},"rate-limiting","Rate limiting",[856,1779,1780],{},"Three rate limiters run in sequence before the email is sent:",[1169,1782,1783,1802],{},[1172,1784,1785],{},[1175,1786,1787,1790,1793,1796,1799],{},[1178,1788,1789],{},"Limiter",[1178,1791,1792],{},"Key",[1178,1794,1795],{},"Points",[1178,1797,1798],{},"Window",[1178,1800,1801],{},"Block",[1188,1803,1804,1822,1840],{},[1175,1805,1806,1809,1814,1817,1820],{},[1193,1807,1808],{},"Global",[1193,1810,1811],{},[882,1812,1813],{},"'global_emails'",[1193,1815,1816],{},"800",[1193,1818,1819],{},"24 h",[1193,1821,1819],{},[1175,1823,1824,1827,1832,1835,1837],{},[1193,1825,1826],{},"Per-user",[1193,1828,1829],{},[882,1830,1831],{},"'user_{userId}'",[1193,1833,1834],{},"8",[1193,1836,1819],{},[1193,1838,1839],{},"12 h",[1175,1841,1842,1845,1850,1853,1855],{},[1193,1843,1844],{},"Per-IP",[1193,1846,1847],{},[882,1848,1849],{},"{ip}",[1193,1851,1852],{},"5",[1193,1854,1819],{},[1193,1856,1857],{},"4 h",[856,1859,1860],{},"If any limiter rejects the request, the function returns early and the email is not sent.",[1711,1862,1864],{"id":1863},"jti-and-random-generation","JTI and random generation",[856,1866,1867,1868,1870,1871,1874,1875,1878],{},"The function generates a JTI (",[882,1869,1275],{}," + ",[882,1872,1873],{},"crypto.randomBytes(64)",") and a random challenge (",[882,1876,1877],{},"crypto.randomBytes(128)","), then hashes the random with SHA-256.",[1711,1880,1882],{"id":1881},"jwt-signing","JWT signing",[856,1884,1885,1887,1888,1891],{},[882,1886,925],{}," is called with ",[882,1889,1890],{},"purpose: 'MAGIC_LINK_MFA_CHECKS'"," and the generated JTI.",[1711,1893,1895],{"id":1894},"otp-code-generation","OTP code generation",[856,1897,1898,1901,1902,1906],{},[882,1899,1900],{},"generateMfaCode"," creates a 7-digit numeric code, hashes it with SHA-256, and stores the hash in the database. See ",[865,1903,1905],{"href":1904},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#otp-generation-and-verification","MFA - OTP generation"," for the full details.",[1711,1908,1910],{"id":1909},"email-delivery","Email delivery",[856,1912,1913,1914,1919,1920,1925],{},"The link URL and OTP code are rendered into an ",[865,1915,1918],{"href":1916,"rel":1917},"https:\u002F\u002Fejs.co\u002F",[908],"EJS"," template and sent via the ",[865,1921,1924],{"href":1922,"rel":1923},"https:\u002F\u002Fresend.com\u002F",[908],"Resend"," SDK. The email includes device metadata (browser, OS, location) so the user can recognize unexpected requests.",[1711,1927,1929],{"id":1928},"routes","Routes",[1169,1931,1932,1944],{},[1172,1933,1934],{},[1175,1935,1936,1939,1942],{},[1178,1937,1938],{},"Method",[1178,1940,1941],{},"Path",[1178,1943,1186],{},[1188,1945,1946,1961],{},[1175,1947,1948,1953,1958],{},[1193,1949,1950],{},[882,1951,1952],{},"GET",[1193,1954,1955],{},[882,1956,1957],{},"\u002Fauth\u002Fverify-mfa",[1193,1959,1960],{},"Validates the signed link token from the email",[1175,1962,1963,1968,1972],{},[1193,1964,1965],{},[882,1966,1967],{},"POST",[1193,1969,1970],{},[882,1971,1957],{},[1193,1973,1974],{},"Submits the 7-digit OTP code and completes verification",[920,1976,1978],{"id":1977},"password-reset","Password reset",[856,1980,1981,1982,1985],{},"The password reset flow is the only built-in flow that does ",[860,1983,1984],{},"not"," generate an OTP code. The user proves identity by clicking the email link alone, then submits a new password directly.",[1771,1987,1988,1992,2010,2026,2030,2041,2045],{"level":1773},[1711,1989,1991],{"id":1990},"user-requests-a-reset","User requests a reset",[856,1993,1994,1995,1998,1999,2001,2002,2005,2006,2009],{},"The user submits their email to ",[882,1996,1997],{},"POST \u002Fauth\u002Fforgot-password",". The controller looks up the account and rejects ",[865,2000,111],{"href":112},"-only users (those with ",[882,2003,2004],{},"password_hash = 'no_password'","). A signed link is generated with ",[882,2007,2008],{},"purpose: 'PASSWORD_RESET'"," and sent via email.",[856,2011,2012,2013,2016,2017,2020,2021,944],{},"The controller enforces a ",[860,2014,2015],{},"minimum 3-second response time"," regardless of whether the email exists. Combined with always returning ",[882,2018,2019],{},"200",", this prevents ",[865,2022,2025],{"href":2023,"rel":2024},"https:\u002F\u002Fowasp.org\u002Fwww-project-web-security-testing-guide\u002Flatest\u002F4-Web_Application_Security_Testing\u002F03-Identity_Management_Testing\u002F04-Testing_for_Account_Enumeration_and_Guessable_User_Account",[908],"timing-based user enumeration",[1711,2027,2029],{"id":2028},"user-clicks-the-link","User clicks the link",[856,2031,2032,2033,2036,2037,2040],{},"The email link targets the bounce handler, which redirects to the application's reset-password page. The page calls ",[882,2034,2035],{},"GET \u002Fauth\u002Freset-password"," with the link's query parameters. The ",[882,2038,2039],{},"linkPasswordVerification"," middleware validates the token, visitor match, and random challenge.",[1711,2042,2044],{"id":2043},"user-submits-a-new-password","User submits a new password",[856,2046,2047,2050,2051,2054,2055,2060],{},[882,2048,2049],{},"POST \u002Fauth\u002Freset-password"," receives ",[882,2052,2053],{},"{ password, confirmedPassword }",", verifies the magic link token again, hashes the new password with ",[865,2056,2059],{"href":2057,"rel":2058},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FArgon2",[908],"Argon2id",", and updates the user record. The link is consumed and cannot be reused.",[1711,2062,1929],{"id":2063},"routes-1",[1169,2065,2066,2076],{},[1172,2067,2068],{},[1175,2069,2070,2072,2074],{},[1178,2071,1938],{},[1178,2073,1941],{},[1178,2075,1186],{},[1188,2077,2078,2092,2106],{},[1175,2079,2080,2084,2089],{},[1193,2081,2082],{},[882,2083,1967],{},[1193,2085,2086],{},[882,2087,2088],{},"\u002Fauth\u002Fforgot-password",[1193,2090,2091],{},"Initiates the reset by sending the email",[1175,2093,2094,2098,2103],{},[1193,2095,2096],{},[882,2097,1952],{},[1193,2099,2100],{},[882,2101,2102],{},"\u002Fauth\u002Freset-password",[1193,2104,2105],{},"Validates the magic link token",[1175,2107,2108,2112,2116],{},[1193,2109,2110],{},[882,2111,1967],{},[1193,2113,2114],{},[882,2115,2102],{},[1193,2117,2118],{},"Submits the new password",[920,2120,2122],{"id":2121},"email-update","Email update",[856,2124,2125,2126,2128],{},"The email update flow uses the custom MFA process with the reserved purpose ",[882,2127,1243],{},". It requires the user to be authenticated, pass a custom MFA challenge, and confirm their current password before the email is changed.",[1771,2130,2131,2135,2153,2156,2166,2170],{"level":1773},[1711,2132,2134],{"id":2133},"user-initiates-the-change","User initiates the change",[856,2136,2137,2138,2141,2142,1648,2145,2148,2149,2152],{},"The authenticated user calls ",[882,2139,2140],{},"POST \u002Fupdate\u002Femail",". The controller generates a random challenge and calls ",[882,2143,2144],{},"generateCustomMfaFlow",[882,2146,2147],{},"reason: 'change_email'",". A magic link and OTP code are sent to the user's ",[860,2150,2151],{},"current"," email address.",[1711,2154,2029],{"id":2155},"user-clicks-the-link-1",[856,2157,2158,2159,2036,2162,2165],{},"The email link targets the bounce handler, which redirects to the application's email-update page. The page calls ",[882,2160,2161],{},"GET \u002Fauth\u002Fverify-custom-mfa",[882,2163,2164],{},"customMfaFlowsVerification"," middleware validates the token.",[1711,2167,2169],{"id":2168},"user-submits-the-new-email-and-code","User submits the new email and code",[856,2171,2172,2050,2174,2177,2178,2180,2181,2185,2186,1648,2189,2192],{},[882,2173,2140],{},[882,2175,2176],{},"{ code, email, newEmail, password }",". The controller verifies that the magic link purpose is ",[882,2179,1243],{},", confirms the current password with ",[865,2182,2184],{"href":2057,"rel":2183},[908],"Argon2",", and delegates to ",[882,2187,2188],{},"verifyMfaCode",[882,2190,2191],{},"revokeAllTokensOnSuccess: true",". On success, the email is updated and all existing sessions are revoked, forcing the user to log in again with the new email.",[1711,2194,1929],{"id":2195},"routes-2",[1169,2197,2198,2208],{},[1172,2199,2200],{},[1175,2201,2202,2204,2206],{},[1178,2203,1938],{},[1178,2205,1941],{},[1178,2207,1186],{},[1188,2209,2210,2224,2237],{},[1175,2211,2212,2216,2221],{},[1193,2213,2214],{},[882,2215,1967],{},[1193,2217,2218],{},[882,2219,2220],{},"\u002Fcustom\u002Fmfa\u002Fchange_email",[1193,2222,2223],{},"Initiates the flow (sends magic link + OTP)",[1175,2225,2226,2230,2235],{},[1193,2227,2228],{},[882,2229,1952],{},[1193,2231,2232],{},[882,2233,2234],{},"\u002Fauth\u002Fverify-custom-mfa",[1193,2236,2105],{},[1175,2238,2239,2243,2248],{},[1193,2240,2241],{},[882,2242,1967],{},[1193,2244,2245],{},[882,2246,2247],{},"\u002Fupdate\u002Femail",[1193,2249,2250],{},"Submits the new email, OTP code, and current password",[912,2252],{},[915,2254,2256],{"id":2255},"custom-mfa-flows","Custom MFA flows",[856,2258,2259,2260,2262],{},"Custom MFA flows let you protect any sensitive action behind a second-factor email check. The user must be authenticated to initiate a custom flow. You define the ",[882,2261,902],{}," string, and the service handles link generation, OTP creation, email delivery, and verification.",[920,2264,2266],{"id":2265},"reserved-reasons","Reserved reasons",[856,2268,2269],{},"The following reason strings are reserved and cannot be used for custom flows:",[1169,2271,2272,2282],{},[1172,2273,2274],{},[1175,2275,2276,2279],{},[1178,2277,2278],{},"Reserved string",[1178,2280,2281],{},"Used by",[1188,2283,2284,2293,2301,2311],{},[1175,2285,2286,2291],{},[1193,2287,2288],{},[882,2289,2290],{},"MAGIC_LINK_MFA_CHECKS",[1193,2292,1685],{},[1175,2294,2295,2299],{},[1193,2296,2297],{},[882,2298,1085],{},[1193,2300,1978],{},[1175,2302,2303,2308],{},[1193,2304,2305],{},[882,2306,2307],{},"PASSWORD_RESET_FLOW",[1193,2309,2310],{},"Internal",[1175,2312,2313,2318],{},[1193,2314,2315],{},[882,2316,2317],{},"EMAIL_MFA_FLOW",[1193,2319,2310],{},[856,2321,2322,2323,872,2326,872,2329,1163],{},"Any other string is valid as a custom reason (e.g. ",[882,2324,2325],{},"'payment'",[882,2327,2328],{},"'account-delete'",[882,2330,2331],{},"'transfer-funds'",[920,2333,2335],{"id":2334},"initiating-a-custom-flow","Initiating a custom flow",[856,2337,2338,2341,2342,2345,2346,2348],{},[882,2339,2340],{},"POST \u002Fcustom\u002Fmfa\u002F:reason"," initiates the flow. The controller requires the request to originate from a trusted IP (either the client IP or the proxy's ",[882,2343,2344],{},"ipToTrust","). The ",[882,2347,898],{}," query parameter must be a high-entropy string between 254 and 500 characters.",[946,2350,2352],{"className":948,"code":2351,"language":950,"meta":951,"style":951},"import { generateCustomMfaFlow } from '@riavzon\u002Fauth'\n\nawait generateCustomMfaFlow(random, 'payment', user, sessionToken, ip, res, {\n  device: 'Chrome on Windows',\n  browser: 'Chrome 125',\n  location: 'Berlin, DE',\n})\n",[882,2353,2354,2372,2376,2419,2435,2451,2467],{"__ignoreMap":951},[955,2355,2356,2358,2360,2362,2364,2366,2368,2370],{"class":957,"line":958},[955,2357,962],{"class":961},[955,2359,966],{"class":965},[955,2361,2144],{"class":969},[955,2363,972],{"class":965},[955,2365,975],{"class":961},[955,2367,979],{"class":978},[955,2369,983],{"class":982},[955,2371,986],{"class":978},[955,2373,2374],{"class":957,"line":989},[955,2375,992],{"emptyLinePlaceholder":8},[955,2377,2378,2381,2384,2386,2388,2390,2392,2395,2397,2399,2401,2403,2406,2408,2411,2413,2416],{"class":957,"line":995},[955,2379,2380],{"class":961},"await",[955,2382,2383],{"class":1010}," generateCustomMfaFlow",[955,2385,1061],{"class":965},[955,2387,898],{"class":969},[955,2389,872],{"class":965},[955,2391,1088],{"class":978},[955,2393,2394],{"class":982},"payment",[955,2396,1088],{"class":978},[955,2398,872],{"class":965},[955,2400,1064],{"class":969},[955,2402,872],{"class":965},[955,2404,2405],{"class":969},"sessionToken",[955,2407,872],{"class":965},[955,2409,2410],{"class":969},"ip",[955,2412,872],{"class":965},[955,2414,2415],{"class":969},"res",[955,2417,2418],{"class":965},", {\n",[955,2420,2421,2424,2426,2428,2431,2433],{"class":957,"line":1030},[955,2422,2423],{"class":969},"  device",[955,2425,1020],{"class":1036},[955,2427,979],{"class":978},[955,2429,2430],{"class":982},"Chrome on Windows",[955,2432,1088],{"class":978},[955,2434,1047],{"class":965},[955,2436,2437,2440,2442,2444,2447,2449],{"class":957,"line":1050},[955,2438,2439],{"class":969},"  browser",[955,2441,1020],{"class":1036},[955,2443,979],{"class":978},[955,2445,2446],{"class":982},"Chrome 125",[955,2448,1088],{"class":978},[955,2450,1047],{"class":965},[955,2452,2453,2456,2458,2460,2463,2465],{"class":957,"line":1075},[955,2454,2455],{"class":969},"  location",[955,2457,1020],{"class":1036},[955,2459,979],{"class":978},[955,2461,2462],{"class":982},"Berlin, DE",[955,2464,1088],{"class":978},[955,2466,1047],{"class":965},[955,2468,2469],{"class":957,"line":1093},[955,2470,1132],{"class":965},[856,2472,2473,2474,2476,2477,2480],{},"The function generates a JTI, signs a temporary link with ",[882,2475,884],{}," set to the provided reason, generates a 7-digit OTP code, and sends the email. The same triple rate limiting described in ",[865,2478,1685],{"href":2479},"#adaptive-mfa"," applies.",[920,2482,2484],{"id":2483},"verifying-a-custom-flow","Verifying a custom flow",[856,2486,2487],{},"After the user clicks the email link:",[1169,2489,2490,2500],{},[1172,2491,2492],{},[1175,2493,2494,2496,2498],{},[1178,2495,1938],{},[1178,2497,1941],{},[1178,2499,1186],{},[1188,2501,2502,2518],{},[1175,2503,2504,2508,2512],{},[1193,2505,2506],{},[882,2507,1952],{},[1193,2509,2510],{},[882,2511,2234],{},[1193,2513,2514,2515,2517],{},"Validates the link token via ",[882,2516,2164],{}," middleware",[1175,2519,2520,2524,2528],{},[1193,2521,2522],{},[882,2523,1967],{},[1193,2525,2526],{},[882,2527,2234],{},[1193,2529,2530],{},"Submits the OTP code and completes verification",[856,2532,2533,2534,2536,2537,2540,2541,2544],{},"On successful POST verification, ",[882,2535,2188],{}," runs with ",[882,2538,2539],{},"returnMetaData: true",", making limited user metadata available to the calling controller via the response. See ",[865,2542,2543],{"href":1904},"MFA - OTP verification"," for the full verification sequence.",[912,2546],{},[915,2548,2550],{"id":2549},"verification-middleware","Verification middleware",[856,2552,2553],{},"Three middleware functions handle magic link verification on the server side. Each one follows the same pattern:",[1299,2555,2556,2570,2575,2582,2589],{},[1302,2557,2558,2561,2562,872,2564,872,2566,872,2568,1756],{},[860,2559,2560],{},"Zod validation"," of the query parameters (",[882,2563,891],{},[882,2565,894],{},[882,2567,898],{},[882,2569,902],{},[1302,2571,2572,2574],{},[860,2573,1777],{}," keyed on the hashed token",[1302,2576,2577,2579,2580],{},[860,2578,1314],{}," via ",[882,2581,1292],{},[1302,2583,2584,1324,2586,2588],{},[860,2585,1323],{},[882,2587,891],{}," claim is compared with the current request context",[1302,2590,2591,2594,2595,2597],{},[860,2592,2593],{},"Timing-safe hash comparison"," of the ",[882,2596,898],{}," parameter against the stored challenge",[1169,2599,2600,2611],{},[1172,2601,2602],{},[1175,2603,2604,2607,2609],{},[1178,2605,2606],{},"Middleware",[1178,2608,1729],{},[1178,2610,2281],{},[1188,2612,2613,2628,2642],{},[1175,2614,2615,2620,2623],{},[1193,2616,2617],{},[882,2618,2619],{},"linkMfaVerification",[1193,2621,2622],{},"Adaptive MFA links",[1193,2624,2625],{},[882,2626,2627],{},"GET\u002FPOST \u002Fauth\u002Fverify-mfa",[1175,2629,2630,2634,2637],{},[1193,2631,2632],{},[882,2633,2039],{},[1193,2635,2636],{},"Password reset links",[1193,2638,2639],{},[882,2640,2641],{},"GET\u002FPOST \u002Fauth\u002Freset-password",[1175,2643,2644,2648,2651],{},[1193,2645,2646],{},[882,2647,2164],{},[1193,2649,2650],{},"Custom MFA and email update links",[1193,2652,2653],{},[882,2654,2655],{},"GET\u002FPOST \u002Fauth\u002Fverify-custom-mfa",[920,2657,2659],{"id":2658},"usage-limits","Usage limits",[856,2661,2662],{},"Each middleware tracks how many times a link has been used for GET and POST requests separately. If the limit is exceeded, the link is invalidated regardless of its expiry.",[1169,2664,2665,2678],{},[1172,2666,2667],{},[1175,2668,2669,2672,2675],{},[1178,2670,2671],{},"Request type",[1178,2673,2674],{},"Default limit",[1178,2676,2677],{},"Config path",[1188,2679,2680,2691],{},[1175,2681,2682,2684,2686],{},[1193,2683,1952],{},[1193,2685,1852],{},[1193,2687,2688],{},[882,2689,2690],{},"magic_links.thresholds.*.allowedPerSuccessfulGet",[1175,2692,2693,2695,2698],{},[1193,2694,1967],{},[1193,2696,2697],{},"3",[1193,2699,2700],{},[882,2701,2702],{},"magic_links.thresholds.*.allowedPerSuccessfulPost",[856,2704,2705],{},"The threshold config keys correspond to each flow:",[1605,2707,2708,2714,2720],{},[1302,2709,2710,2713],{},[882,2711,2712],{},"magic_links.thresholds.adaptiveMfa"," - adaptive MFA links",[1302,2715,2716,2719],{},[882,2717,2718],{},"magic_links.thresholds.linkPasswordVerification"," - password reset links",[1302,2721,2722,2725],{},[882,2723,2724],{},"magic_links.thresholds.customMfaFlowsAndEmailChanges"," - custom MFA and email update links",[912,2727],{},[915,2729,2731],{"id":2730},"examples","Examples",[920,2733,2735],{"id":2734},"mounting-the-router","Mounting the router",[856,2737,1204,2738,2741],{},[882,2739,2740],{},"magicLinks"," export is a pre-built router with all nine routes already wired. Mount it on your app with:",[946,2743,2745],{"className":948,"code":2744,"language":950,"meta":951,"style":951},"import express from 'express'\nimport { magicLinks } from '@riavzon\u002Fauth'\n\nconst app = express()\napp.use(magicLinks)\n",[882,2746,2747,2764,2782,2786,2799],{"__ignoreMap":951},[955,2748,2749,2751,2754,2757,2759,2762],{"class":957,"line":958},[955,2750,962],{"class":961},[955,2752,2753],{"class":969}," express",[955,2755,2756],{"class":961}," from",[955,2758,979],{"class":978},[955,2760,2761],{"class":982},"express",[955,2763,986],{"class":978},[955,2765,2766,2768,2770,2772,2774,2776,2778,2780],{"class":957,"line":989},[955,2767,962],{"class":961},[955,2769,966],{"class":965},[955,2771,2740],{"class":969},[955,2773,972],{"class":965},[955,2775,975],{"class":961},[955,2777,979],{"class":978},[955,2779,983],{"class":982},[955,2781,986],{"class":978},[955,2783,2784],{"class":957,"line":995},[955,2785,992],{"emptyLinePlaceholder":8},[955,2787,2788,2790,2793,2795,2797],{"class":957,"line":1030},[955,2789,999],{"class":998},[955,2791,2792],{"class":1002}," app",[955,2794,1007],{"class":1006},[955,2796,2753],{"class":1010},[955,2798,1556],{"class":965},[955,2800,2801,2804,2806,2809,2811,2813],{"class":957,"line":1050},[955,2802,2803],{"class":969},"app",[955,2805,944],{"class":965},[955,2807,2808],{"class":1010},"use",[955,2810,1061],{"class":965},[955,2812,2740],{"class":969},[955,2814,1384],{"class":965},[856,2816,2817,2818,2822],{},"This registers every route listed in ",[865,2819,2821],{"href":2820},"#routes-reference","Routes reference",", including the full middleware chains for rate limiting, Zod validation, link verification, and bot detection. No additional setup is required.",[2824,2825,2826],"tip",{},[856,2827,2828,2829,2832,2833,2836,2837,944],{},"If you use ",[882,2830,2831],{},"bootstrapApp()"," from ",[882,2834,2835],{},"@riavzon\u002Fauth\u002Fservice",", the magic links router is mounted automatically and you do not need to call ",[882,2838,2839],{},"app.use(magicLinks)",[920,2841,2843],{"id":2842},"triggering-adaptive-mfa-from-a-custom-handler","Triggering adaptive MFA from a custom handler",[856,2845,2846,2848,2849,2851,2852,2854],{},[882,2847,1768],{}," is the function that ",[882,2850,1698],{}," calls internally when anomaly detection returns ",[882,2853,1694],{},". You can call it directly from any handler to force an MFA challenge:",[946,2856,2858],{"className":948,"code":2857,"language":950,"meta":951,"style":951},"import { sendTempMfaLink } from '@riavzon\u002Fauth'\n\nasync function myProtectedHandler(req, res) {\n  const result = await sendTempMfaLink(\n    { userId: Number(req.user.userId), visitor: req.user.visitor_id },\n    req.cookies.session,       \u002F\u002F raw refresh token\n    req.ip!,\n    res,\n    {\n      device: 'Chrome on macOS',\n      browser: 'Chrome 125',\n      location: 'Paris, FR',\n    }\n  )\n\n  if (result === 'rate_limited') {\n    return res.status(429).json({ error: 'Too many MFA requests. Try again later.' })\n  }\n\n  \u002F\u002F The email has been sent. Respond with 202 so the client knows to prompt for MFA.\n  return res.status(202).json({ message: 'Please check your email for a verification link.' })\n}\n",[882,2859,2860,2878,2882,2905,2923,2968,2989,3001,3008,3013,3029,3045,3062,3068,3074,3079,3101,3143,3149,3154,3160,3197],{"__ignoreMap":951},[955,2861,2862,2864,2866,2868,2870,2872,2874,2876],{"class":957,"line":958},[955,2863,962],{"class":961},[955,2865,966],{"class":965},[955,2867,1768],{"class":969},[955,2869,972],{"class":965},[955,2871,975],{"class":961},[955,2873,979],{"class":978},[955,2875,983],{"class":982},[955,2877,986],{"class":978},[955,2879,2880],{"class":957,"line":989},[955,2881,992],{"emptyLinePlaceholder":8},[955,2883,2884,2887,2890,2893,2895,2899,2901,2903],{"class":957,"line":995},[955,2885,2886],{"class":998},"async",[955,2888,2889],{"class":998}," function",[955,2891,2892],{"class":1010}," myProtectedHandler",[955,2894,1061],{"class":965},[955,2896,2898],{"class":2897},"sygFZ","req",[955,2900,872],{"class":965},[955,2902,2415],{"class":2897},[955,2904,1410],{"class":965},[955,2906,2907,2910,2912,2914,2917,2920],{"class":957,"line":1030},[955,2908,2909],{"class":998},"  const",[955,2911,1361],{"class":1002},[955,2913,1007],{"class":1006},[955,2915,2916],{"class":961}," await",[955,2918,2919],{"class":1010}," sendTempMfaLink",[955,2921,2922],{"class":965},"(\n",[955,2924,2925,2928,2931,2933,2936,2938,2940,2942,2944,2946,2948,2950,2952,2954,2957,2959,2961,2963,2965],{"class":957,"line":1050},[955,2926,2927],{"class":965},"    { ",[955,2929,2930],{"class":969},"userId",[955,2932,1020],{"class":1036},[955,2934,2935],{"class":1010}," Number",[955,2937,1061],{"class":965},[955,2939,2898],{"class":969},[955,2941,944],{"class":965},[955,2943,1064],{"class":969},[955,2945,944],{"class":965},[955,2947,2930],{"class":969},[955,2949,868],{"class":965},[955,2951,891],{"class":969},[955,2953,1020],{"class":1036},[955,2955,2956],{"class":969}," req",[955,2958,944],{"class":965},[955,2960,1064],{"class":969},[955,2962,944],{"class":965},[955,2964,1044],{"class":969},[955,2966,2967],{"class":965}," },\n",[955,2969,2970,2973,2975,2978,2980,2983,2986],{"class":957,"line":1075},[955,2971,2972],{"class":969},"    req",[955,2974,944],{"class":965},[955,2976,2977],{"class":969},"cookies",[955,2979,944],{"class":965},[955,2981,2982],{"class":969},"session",[955,2984,2985],{"class":965},",       ",[955,2987,2988],{"class":1415},"\u002F\u002F raw refresh token\n",[955,2990,2991,2993,2995,2997,2999],{"class":957,"line":1093},[955,2992,2972],{"class":969},[955,2994,944],{"class":965},[955,2996,2410],{"class":969},[955,2998,1399],{"class":1006},[955,3000,1047],{"class":965},[955,3002,3003,3006],{"class":957,"line":1112},[955,3004,3005],{"class":969},"    res",[955,3007,1047],{"class":965},[955,3009,3010],{"class":957,"line":1129},[955,3011,3012],{"class":965},"    {\n",[955,3014,3015,3018,3020,3022,3025,3027],{"class":957,"line":1433},[955,3016,3017],{"class":969},"      device",[955,3019,1020],{"class":1036},[955,3021,979],{"class":978},[955,3023,3024],{"class":982},"Chrome on macOS",[955,3026,1088],{"class":978},[955,3028,1047],{"class":965},[955,3030,3032,3035,3037,3039,3041,3043],{"class":957,"line":3031},11,[955,3033,3034],{"class":969},"      browser",[955,3036,1020],{"class":1036},[955,3038,979],{"class":978},[955,3040,2446],{"class":982},[955,3042,1088],{"class":978},[955,3044,1047],{"class":965},[955,3046,3048,3051,3053,3055,3058,3060],{"class":957,"line":3047},12,[955,3049,3050],{"class":969},"      location",[955,3052,1020],{"class":1036},[955,3054,979],{"class":978},[955,3056,3057],{"class":982},"Paris, FR",[955,3059,1088],{"class":978},[955,3061,1047],{"class":965},[955,3063,3065],{"class":957,"line":3064},13,[955,3066,3067],{"class":965},"    }\n",[955,3069,3071],{"class":957,"line":3070},14,[955,3072,3073],{"class":965},"  )\n",[955,3075,3077],{"class":957,"line":3076},15,[955,3078,992],{"emptyLinePlaceholder":8},[955,3080,3082,3085,3087,3089,3092,3094,3097,3099],{"class":957,"line":3081},16,[955,3083,3084],{"class":961},"  if",[955,3086,1396],{"class":965},[955,3088,1402],{"class":969},[955,3090,3091],{"class":1006}," ===",[955,3093,979],{"class":978},[955,3095,3096],{"class":982},"rate_limited",[955,3098,1088],{"class":978},[955,3100,1410],{"class":965},[955,3102,3104,3107,3110,3112,3115,3117,3121,3123,3125,3128,3131,3133,3135,3138,3140],{"class":957,"line":3103},17,[955,3105,3106],{"class":961},"    return",[955,3108,3109],{"class":969}," res",[955,3111,944],{"class":965},[955,3113,3114],{"class":1010},"status",[955,3116,1061],{"class":965},[955,3118,3120],{"class":3119},"spgvN","429",[955,3122,1163],{"class":965},[955,3124,5],{"class":1010},[955,3126,3127],{"class":965},"({ ",[955,3129,3130],{"class":969},"error",[955,3132,1020],{"class":1036},[955,3134,979],{"class":978},[955,3136,3137],{"class":982},"Too many MFA requests. Try again later.",[955,3139,1088],{"class":978},[955,3141,3142],{"class":965}," })\n",[955,3144,3146],{"class":957,"line":3145},18,[955,3147,3148],{"class":965},"  }\n",[955,3150,3152],{"class":957,"line":3151},19,[955,3153,992],{"emptyLinePlaceholder":8},[955,3155,3157],{"class":957,"line":3156},20,[955,3158,3159],{"class":1415},"  \u002F\u002F The email has been sent. Respond with 202 so the client knows to prompt for MFA.\n",[955,3161,3163,3166,3168,3170,3172,3174,3177,3179,3181,3183,3186,3188,3190,3193,3195],{"class":957,"line":3162},21,[955,3164,3165],{"class":961},"  return",[955,3167,3109],{"class":969},[955,3169,944],{"class":965},[955,3171,3114],{"class":1010},[955,3173,1061],{"class":965},[955,3175,3176],{"class":3119},"202",[955,3178,1163],{"class":965},[955,3180,5],{"class":1010},[955,3182,3127],{"class":965},[955,3184,3185],{"class":969},"message",[955,3187,1020],{"class":1036},[955,3189,979],{"class":978},[955,3191,3192],{"class":982},"Please check your email for a verification link.",[955,3194,1088],{"class":978},[955,3196,3142],{"class":965},[955,3198,3200],{"class":957,"line":3199},22,[955,3201,1426],{"class":965},[856,3203,1204,3204,3206,3207,3210,3211,3206,3214,3217],{},[882,3205,1064],{}," parameter expects ",[882,3208,3209],{},"{ userId: number, visitor: string }",". The ",[882,3212,3213],{},"meta",[882,3215,3216],{},"{ device: string, browser: string, location: string }",", which is rendered into the email template so the user can recognize the request.",[856,3219,3220,3221,3224,3225,3228],{},"The function applies three rate limiters (global, per-user, per-IP), generates the JTI and random challenge, signs the JWT, creates the 7-digit OTP code, and sends the email. It returns ",[882,3222,3223],{},"true"," on success or ",[882,3226,3227],{},"'rate_limited'"," if any limiter rejects.",[920,3230,1978],{"id":3231},"password-reset-1",[856,3233,3234,3237,3238,3240],{},[882,3235,3236],{},"sendTempPasswordResetLink"," handles the full password-reset email pipeline. The  ",[882,3239,1997],{}," route, calls this internally, but you can use it from a custom controller:",[946,3242,3244],{"className":948,"code":3243,"language":950,"meta":951,"style":951},"import { sendTempPasswordResetLink } from '@riavzon\u002Fauth'\n\nasync function customForgotPassword(req, res) {\n  const startTime = Date.now()\n\n  const { valid, error } = await sendTempPasswordResetLink(req.body.email)\n\n  \u002F\u002F Enforce a minimum 3-second response time to prevent timing-based enumeration.\n  const elapsed = Date.now() - startTime\n  if (elapsed \u003C 3000) {\n    await new Promise(resolve => setTimeout(resolve, 3000 - elapsed))\n  }\n\n  \u002F\u002F Always return success regardless of whether the email exists.\n  return res.status(200).json({\n    ok: true,\n    message: 'If that email is registered, a reset link has been sent.',\n  })\n}\n",[882,3245,3246,3264,3268,3287,3306,3310,3347,3351,3356,3380,3397,3437,3441,3445,3450,3471,3484,3500,3505],{"__ignoreMap":951},[955,3247,3248,3250,3252,3254,3256,3258,3260,3262],{"class":957,"line":958},[955,3249,962],{"class":961},[955,3251,966],{"class":965},[955,3253,3236],{"class":969},[955,3255,972],{"class":965},[955,3257,975],{"class":961},[955,3259,979],{"class":978},[955,3261,983],{"class":982},[955,3263,986],{"class":978},[955,3265,3266],{"class":957,"line":989},[955,3267,992],{"emptyLinePlaceholder":8},[955,3269,3270,3272,3274,3277,3279,3281,3283,3285],{"class":957,"line":995},[955,3271,2886],{"class":998},[955,3273,2889],{"class":998},[955,3275,3276],{"class":1010}," customForgotPassword",[955,3278,1061],{"class":965},[955,3280,2898],{"class":2897},[955,3282,872],{"class":965},[955,3284,2415],{"class":2897},[955,3286,1410],{"class":965},[955,3288,3289,3291,3294,3296,3299,3301,3304],{"class":957,"line":1030},[955,3290,2909],{"class":998},[955,3292,3293],{"class":1002}," startTime",[955,3295,1007],{"class":1006},[955,3297,3298],{"class":969}," Date",[955,3300,944],{"class":965},[955,3302,3303],{"class":1010},"now",[955,3305,1556],{"class":965},[955,3307,3308],{"class":957,"line":1050},[955,3309,992],{"emptyLinePlaceholder":8},[955,3311,3312,3314,3316,3318,3320,3322,3324,3326,3328,3331,3333,3335,3337,3340,3342,3345],{"class":957,"line":1075},[955,3313,2909],{"class":998},[955,3315,966],{"class":965},[955,3317,1407],{"class":1002},[955,3319,872],{"class":965},[955,3321,3130],{"class":1002},[955,3323,972],{"class":965},[955,3325,1452],{"class":1006},[955,3327,2916],{"class":961},[955,3329,3330],{"class":1010}," sendTempPasswordResetLink",[955,3332,1061],{"class":965},[955,3334,2898],{"class":969},[955,3336,944],{"class":965},[955,3338,3339],{"class":969},"body",[955,3341,944],{"class":965},[955,3343,3344],{"class":969},"email",[955,3346,1384],{"class":965},[955,3348,3349],{"class":957,"line":1093},[955,3350,992],{"emptyLinePlaceholder":8},[955,3352,3353],{"class":957,"line":1112},[955,3354,3355],{"class":1415},"  \u002F\u002F Enforce a minimum 3-second response time to prevent timing-based enumeration.\n",[955,3357,3358,3360,3363,3365,3367,3369,3371,3374,3377],{"class":957,"line":1129},[955,3359,2909],{"class":998},[955,3361,3362],{"class":1002}," elapsed",[955,3364,1007],{"class":1006},[955,3366,3298],{"class":969},[955,3368,944],{"class":965},[955,3370,3303],{"class":1010},[955,3372,3373],{"class":965},"() ",[955,3375,3376],{"class":1006},"-",[955,3378,3379],{"class":969}," startTime\n",[955,3381,3382,3384,3386,3389,3392,3395],{"class":957,"line":1433},[955,3383,3084],{"class":961},[955,3385,1396],{"class":965},[955,3387,3388],{"class":969},"elapsed",[955,3390,3391],{"class":1006}," \u003C",[955,3393,3394],{"class":3119}," 3000",[955,3396,1410],{"class":965},[955,3398,3399,3402,3406,3409,3411,3414,3417,3420,3422,3424,3426,3429,3432,3434],{"class":957,"line":3031},[955,3400,3401],{"class":961},"    await",[955,3403,3405],{"class":3404},"sakC6"," new",[955,3407,3408],{"class":1023}," Promise",[955,3410,1061],{"class":965},[955,3412,3413],{"class":2897},"resolve",[955,3415,3416],{"class":998}," =>",[955,3418,3419],{"class":1010}," setTimeout",[955,3421,1061],{"class":965},[955,3423,3413],{"class":969},[955,3425,872],{"class":965},[955,3427,3428],{"class":3119},"3000",[955,3430,3431],{"class":1006}," -",[955,3433,3362],{"class":969},[955,3435,3436],{"class":965},"))\n",[955,3438,3439],{"class":957,"line":3047},[955,3440,3148],{"class":965},[955,3442,3443],{"class":957,"line":3064},[955,3444,992],{"emptyLinePlaceholder":8},[955,3446,3447],{"class":957,"line":3070},[955,3448,3449],{"class":1415},"  \u002F\u002F Always return success regardless of whether the email exists.\n",[955,3451,3452,3454,3456,3458,3460,3462,3464,3466,3468],{"class":957,"line":3076},[955,3453,3165],{"class":961},[955,3455,3109],{"class":969},[955,3457,944],{"class":965},[955,3459,3114],{"class":1010},[955,3461,1061],{"class":965},[955,3463,2019],{"class":3119},[955,3465,1163],{"class":965},[955,3467,5],{"class":1010},[955,3469,3470],{"class":965},"({\n",[955,3472,3473,3476,3478,3482],{"class":957,"line":3081},[955,3474,3475],{"class":969},"    ok",[955,3477,1020],{"class":1036},[955,3479,3481],{"class":3480},"sjR7W"," true",[955,3483,1047],{"class":965},[955,3485,3486,3489,3491,3493,3496,3498],{"class":957,"line":3103},[955,3487,3488],{"class":969},"    message",[955,3490,1020],{"class":1036},[955,3492,979],{"class":978},[955,3494,3495],{"class":982},"If that email is registered, a reset link has been sent.",[955,3497,1088],{"class":978},[955,3499,1047],{"class":965},[955,3501,3502],{"class":957,"line":3145},[955,3503,3504],{"class":965},"  })\n",[955,3506,3507],{"class":957,"line":3151},[955,3508,1426],{"class":965},[856,3510,3511,3512,3514,3515,3517],{},"The function looks up the user, rejects OAuth accounts (no password to reset), generates a signed link with ",[882,3513,2008],{},", and sends the email. It does ",[860,3516,1984],{}," generate an OTP code.",[920,3519,3521],{"id":3520},"building-a-custom-mfa-protected-action","Building a custom MFA-protected action",[856,3523,3524],{},"To protect a sensitive action behind a custom MFA flow, wire three routes: one to initiate the flow, one to verify the magic link (GET), and one to verify the OTP code and execute the action (POST).",[946,3526,3528],{"className":948,"code":3527,"language":950,"meta":951,"style":951},"import express from 'express'\nimport crypto from 'crypto'\nimport {\n  validateContentType,\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  customMfaFlowsVerification,\n  generateCustomMfaFlow,\n  verifyCustomMfa,\n} from '@riavzon\u002Fauth'\nimport { detectBots } from '@riavzon\u002Fbot-detector'\n\nconst router = express.Router()\n\n\u002F\u002F Step 1: Initiate the custom MFA flow\nrouter.post('\u002Fdanger-zone\u002Finitiate',\n  validateContentType('application\u002Fjson'),\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  express.json({ limit: '1kb' }),\n  async (req, res) => {\n    const random = crypto.randomBytes(128).toString('hex')\n\n    const result = await generateCustomMfaFlow(\n      random,\n      'account-delete',       \u002F\u002F your custom reason string\n      { userId: Number(req.user!.userId), visitor: req.user!.visitor_id },\n      req.cookies.session,    \u002F\u002F raw refresh token\n      req.ip!,\n      res,\n      {\n        device: req.headers['user-agent'] ?? 'Unknown Device',\n        browser: 'Unknown Browser',\n        location: 'Unknown Location',\n      }\n    )\n\n    if (!result.ok && result.data === 'rate_limited') return\n    if (!result.ok && result.data === 'exists') {\n      return res.status(400).json({ error: 'This reason is already reserved.' })\n    }\n\n    return res.status(200).json(result)\n  }\n)\n\n\u002F\u002F Step 2: Verify the magic link (GET)\n\u002F\u002F customMfaFlowsVerification sends the response on GET automatically.\nrouter.get('\u002Fdanger-zone\u002Fverify',\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  customMfaFlowsVerification\n)\n\n\u002F\u002F Step 3: Verify the OTP code and execute the action (POST)\nrouter.post('\u002Fdanger-zone\u002Fverify',\n  validateContentType('application\u002Fjson'),\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  express.json({ limit: '1kb' }),\n  customMfaFlowsVerification,\n  detectBots,\n  async (req, res) => {\n    \u002F\u002F MFA verified. req.link is populated with the decoded payload.\n    const { purpose, subject } = req.link\n\n    \u002F\u002F Execute the protected action\n    await deleteUserAccount(Number(req.user!.userId))\n\n    return res.json({ ok: true, message: 'Account deleted.' })\n  }\n)\n",[882,3529,3530,3544,3559,3566,3573,3580,3587,3594,3601,3608,3615,3622,3635,3655,3659,3677,3681,3686,3707,3722,3728,3734,3740,3747,3774,3795,3834,3839,3854,3862,3878,3924,3943,3956,3964,3970,4010,4027,4044,4050,4056,4061,4101,4135,4171,4176,4181,4206,4211,4216,4221,4227,4233,4254,4261,4268,4275,4282,4288,4293,4298,4304,4323,4338,4345,4352,4359,4366,4389,4396,4404,4423,4429,4453,4458,4464,4493,4498,4532,4537],{"__ignoreMap":951},[955,3531,3532,3534,3536,3538,3540,3542],{"class":957,"line":958},[955,3533,962],{"class":961},[955,3535,2753],{"class":969},[955,3537,2756],{"class":961},[955,3539,979],{"class":978},[955,3541,2761],{"class":982},[955,3543,986],{"class":978},[955,3545,3546,3548,3550,3552,3554,3557],{"class":957,"line":989},[955,3547,962],{"class":961},[955,3549,1101],{"class":969},[955,3551,2756],{"class":961},[955,3553,979],{"class":978},[955,3555,3556],{"class":982},"crypto",[955,3558,986],{"class":978},[955,3560,3561,3563],{"class":957,"line":995},[955,3562,962],{"class":961},[955,3564,3565],{"class":965}," {\n",[955,3567,3568,3571],{"class":957,"line":1030},[955,3569,3570],{"class":969},"  validateContentType",[955,3572,1047],{"class":965},[955,3574,3575,3578],{"class":957,"line":1050},[955,3576,3577],{"class":969},"  requireAccessToken",[955,3579,1047],{"class":965},[955,3581,3582,3585],{"class":957,"line":1075},[955,3583,3584],{"class":969},"  requireRefreshToken",[955,3586,1047],{"class":965},[955,3588,3589,3592],{"class":957,"line":1093},[955,3590,3591],{"class":969},"  getFingerPrint",[955,3593,1047],{"class":965},[955,3595,3596,3599],{"class":957,"line":1112},[955,3597,3598],{"class":969},"  protectRoute",[955,3600,1047],{"class":965},[955,3602,3603,3606],{"class":957,"line":1129},[955,3604,3605],{"class":969},"  customMfaFlowsVerification",[955,3607,1047],{"class":965},[955,3609,3610,3613],{"class":957,"line":1433},[955,3611,3612],{"class":969},"  generateCustomMfaFlow",[955,3614,1047],{"class":965},[955,3616,3617,3620],{"class":957,"line":3031},[955,3618,3619],{"class":969},"  verifyCustomMfa",[955,3621,1047],{"class":965},[955,3623,3624,3627,3629,3631,3633],{"class":957,"line":3047},[955,3625,3626],{"class":965},"} ",[955,3628,975],{"class":961},[955,3630,979],{"class":978},[955,3632,983],{"class":982},[955,3634,986],{"class":978},[955,3636,3637,3639,3641,3644,3646,3648,3650,3653],{"class":957,"line":3064},[955,3638,962],{"class":961},[955,3640,966],{"class":965},[955,3642,3643],{"class":969},"detectBots",[955,3645,972],{"class":965},[955,3647,975],{"class":961},[955,3649,979],{"class":978},[955,3651,3652],{"class":982},"@riavzon\u002Fbot-detector",[955,3654,986],{"class":978},[955,3656,3657],{"class":957,"line":3070},[955,3658,992],{"emptyLinePlaceholder":8},[955,3660,3661,3663,3666,3668,3670,3672,3675],{"class":957,"line":3076},[955,3662,999],{"class":998},[955,3664,3665],{"class":1002}," router",[955,3667,1007],{"class":1006},[955,3669,2753],{"class":969},[955,3671,944],{"class":965},[955,3673,3674],{"class":1010},"Router",[955,3676,1556],{"class":965},[955,3678,3679],{"class":957,"line":3081},[955,3680,992],{"emptyLinePlaceholder":8},[955,3682,3683],{"class":957,"line":3103},[955,3684,3685],{"class":1415},"\u002F\u002F Step 1: Initiate the custom MFA flow\n",[955,3687,3688,3691,3693,3696,3698,3700,3703,3705],{"class":957,"line":3145},[955,3689,3690],{"class":969},"router",[955,3692,944],{"class":965},[955,3694,3695],{"class":1010},"post",[955,3697,1061],{"class":965},[955,3699,1088],{"class":978},[955,3701,3702],{"class":982},"\u002Fdanger-zone\u002Finitiate",[955,3704,1088],{"class":978},[955,3706,1047],{"class":965},[955,3708,3709,3711,3713,3715,3718,3720],{"class":957,"line":3151},[955,3710,3570],{"class":1010},[955,3712,1061],{"class":965},[955,3714,1088],{"class":978},[955,3716,3717],{"class":982},"application\u002Fjson",[955,3719,1088],{"class":978},[955,3721,1072],{"class":965},[955,3723,3724,3726],{"class":957,"line":3156},[955,3725,3577],{"class":969},[955,3727,1047],{"class":965},[955,3729,3730,3732],{"class":957,"line":3162},[955,3731,3584],{"class":969},[955,3733,1047],{"class":965},[955,3735,3736,3738],{"class":957,"line":3199},[955,3737,3591],{"class":969},[955,3739,1047],{"class":965},[955,3741,3743,3745],{"class":957,"line":3742},23,[955,3744,3598],{"class":969},[955,3746,1047],{"class":965},[955,3748,3750,3753,3755,3757,3759,3762,3764,3766,3769,3771],{"class":957,"line":3749},24,[955,3751,3752],{"class":969},"  express",[955,3754,944],{"class":965},[955,3756,5],{"class":1010},[955,3758,3127],{"class":965},[955,3760,3761],{"class":969},"limit",[955,3763,1020],{"class":1036},[955,3765,979],{"class":978},[955,3767,3768],{"class":982},"1kb",[955,3770,1088],{"class":978},[955,3772,3773],{"class":965}," }),\n",[955,3775,3777,3780,3782,3784,3786,3788,3790,3793],{"class":957,"line":3776},25,[955,3778,3779],{"class":998},"  async",[955,3781,1396],{"class":965},[955,3783,2898],{"class":2897},[955,3785,872],{"class":965},[955,3787,2415],{"class":2897},[955,3789,1584],{"class":965},[955,3791,3792],{"class":998},"=>",[955,3794,3565],{"class":965},[955,3796,3798,3801,3804,3806,3808,3810,3813,3815,3818,3820,3823,3825,3827,3830,3832],{"class":957,"line":3797},26,[955,3799,3800],{"class":998},"    const",[955,3802,3803],{"class":1002}," random",[955,3805,1007],{"class":1006},[955,3807,1101],{"class":969},[955,3809,944],{"class":965},[955,3811,3812],{"class":1010},"randomBytes",[955,3814,1061],{"class":965},[955,3816,3817],{"class":3119},"128",[955,3819,1163],{"class":965},[955,3821,3822],{"class":1010},"toString",[955,3824,1061],{"class":965},[955,3826,1088],{"class":978},[955,3828,3829],{"class":982},"hex",[955,3831,1088],{"class":978},[955,3833,1384],{"class":965},[955,3835,3837],{"class":957,"line":3836},27,[955,3838,992],{"emptyLinePlaceholder":8},[955,3840,3842,3844,3846,3848,3850,3852],{"class":957,"line":3841},28,[955,3843,3800],{"class":998},[955,3845,1361],{"class":1002},[955,3847,1007],{"class":1006},[955,3849,2916],{"class":961},[955,3851,2383],{"class":1010},[955,3853,2922],{"class":965},[955,3855,3857,3860],{"class":957,"line":3856},29,[955,3858,3859],{"class":969},"      random",[955,3861,1047],{"class":965},[955,3863,3865,3868,3871,3873,3875],{"class":957,"line":3864},30,[955,3866,3867],{"class":978},"      '",[955,3869,3870],{"class":982},"account-delete",[955,3872,1088],{"class":978},[955,3874,2985],{"class":965},[955,3876,3877],{"class":1415},"\u002F\u002F your custom reason string\n",[955,3879,3881,3884,3886,3888,3890,3892,3894,3896,3898,3900,3902,3904,3906,3908,3910,3912,3914,3916,3918,3920,3922],{"class":957,"line":3880},31,[955,3882,3883],{"class":965},"      { ",[955,3885,2930],{"class":969},[955,3887,1020],{"class":1036},[955,3889,2935],{"class":1010},[955,3891,1061],{"class":965},[955,3893,2898],{"class":969},[955,3895,944],{"class":965},[955,3897,1064],{"class":969},[955,3899,1399],{"class":1006},[955,3901,944],{"class":965},[955,3903,2930],{"class":969},[955,3905,868],{"class":965},[955,3907,891],{"class":969},[955,3909,1020],{"class":1036},[955,3911,2956],{"class":969},[955,3913,944],{"class":965},[955,3915,1064],{"class":969},[955,3917,1399],{"class":1006},[955,3919,944],{"class":965},[955,3921,1044],{"class":969},[955,3923,2967],{"class":965},[955,3925,3927,3930,3932,3934,3936,3938,3941],{"class":957,"line":3926},32,[955,3928,3929],{"class":969},"      req",[955,3931,944],{"class":965},[955,3933,2977],{"class":969},[955,3935,944],{"class":965},[955,3937,2982],{"class":969},[955,3939,3940],{"class":965},",    ",[955,3942,2988],{"class":1415},[955,3944,3946,3948,3950,3952,3954],{"class":957,"line":3945},33,[955,3947,3929],{"class":969},[955,3949,944],{"class":965},[955,3951,2410],{"class":969},[955,3953,1399],{"class":1006},[955,3955,1047],{"class":965},[955,3957,3959,3962],{"class":957,"line":3958},34,[955,3960,3961],{"class":969},"      res",[955,3963,1047],{"class":965},[955,3965,3967],{"class":957,"line":3966},35,[955,3968,3969],{"class":965},"      {\n",[955,3971,3973,3976,3978,3980,3982,3985,3988,3990,3993,3995,3998,4001,4003,4006,4008],{"class":957,"line":3972},36,[955,3974,3975],{"class":969},"        device",[955,3977,1020],{"class":1036},[955,3979,2956],{"class":969},[955,3981,944],{"class":965},[955,3983,3984],{"class":969},"headers",[955,3986,3987],{"class":965},"[",[955,3989,1088],{"class":978},[955,3991,3992],{"class":982},"user-agent",[955,3994,1088],{"class":978},[955,3996,3997],{"class":965},"] ",[955,3999,4000],{"class":1006},"??",[955,4002,979],{"class":978},[955,4004,4005],{"class":982},"Unknown Device",[955,4007,1088],{"class":978},[955,4009,1047],{"class":965},[955,4011,4013,4016,4018,4020,4023,4025],{"class":957,"line":4012},37,[955,4014,4015],{"class":969},"        browser",[955,4017,1020],{"class":1036},[955,4019,979],{"class":978},[955,4021,4022],{"class":982},"Unknown Browser",[955,4024,1088],{"class":978},[955,4026,1047],{"class":965},[955,4028,4030,4033,4035,4037,4040,4042],{"class":957,"line":4029},38,[955,4031,4032],{"class":969},"        location",[955,4034,1020],{"class":1036},[955,4036,979],{"class":978},[955,4038,4039],{"class":982},"Unknown Location",[955,4041,1088],{"class":978},[955,4043,1047],{"class":965},[955,4045,4047],{"class":957,"line":4046},39,[955,4048,4049],{"class":965},"      }\n",[955,4051,4053],{"class":957,"line":4052},40,[955,4054,4055],{"class":965},"    )\n",[955,4057,4059],{"class":957,"line":4058},41,[955,4060,992],{"emptyLinePlaceholder":8},[955,4062,4064,4067,4069,4071,4073,4075,4078,4081,4083,4085,4088,4090,4092,4094,4096,4098],{"class":957,"line":4063},42,[955,4065,4066],{"class":961},"    if",[955,4068,1396],{"class":965},[955,4070,1399],{"class":1006},[955,4072,1402],{"class":969},[955,4074,944],{"class":965},[955,4076,4077],{"class":969},"ok",[955,4079,4080],{"class":1006}," &&",[955,4082,1361],{"class":969},[955,4084,944],{"class":965},[955,4086,4087],{"class":969},"data",[955,4089,3091],{"class":1006},[955,4091,979],{"class":978},[955,4093,3096],{"class":982},[955,4095,1088],{"class":978},[955,4097,1584],{"class":965},[955,4099,4100],{"class":961},"return\n",[955,4102,4104,4106,4108,4110,4112,4114,4116,4118,4120,4122,4124,4126,4128,4131,4133],{"class":957,"line":4103},43,[955,4105,4066],{"class":961},[955,4107,1396],{"class":965},[955,4109,1399],{"class":1006},[955,4111,1402],{"class":969},[955,4113,944],{"class":965},[955,4115,4077],{"class":969},[955,4117,4080],{"class":1006},[955,4119,1361],{"class":969},[955,4121,944],{"class":965},[955,4123,4087],{"class":969},[955,4125,3091],{"class":1006},[955,4127,979],{"class":978},[955,4129,4130],{"class":982},"exists",[955,4132,1088],{"class":978},[955,4134,1410],{"class":965},[955,4136,4138,4141,4143,4145,4147,4149,4152,4154,4156,4158,4160,4162,4164,4167,4169],{"class":957,"line":4137},44,[955,4139,4140],{"class":961},"      return",[955,4142,3109],{"class":969},[955,4144,944],{"class":965},[955,4146,3114],{"class":1010},[955,4148,1061],{"class":965},[955,4150,4151],{"class":3119},"400",[955,4153,1163],{"class":965},[955,4155,5],{"class":1010},[955,4157,3127],{"class":965},[955,4159,3130],{"class":969},[955,4161,1020],{"class":1036},[955,4163,979],{"class":978},[955,4165,4166],{"class":982},"This reason is already reserved.",[955,4168,1088],{"class":978},[955,4170,3142],{"class":965},[955,4172,4174],{"class":957,"line":4173},45,[955,4175,3067],{"class":965},[955,4177,4179],{"class":957,"line":4178},46,[955,4180,992],{"emptyLinePlaceholder":8},[955,4182,4184,4186,4188,4190,4192,4194,4196,4198,4200,4202,4204],{"class":957,"line":4183},47,[955,4185,3106],{"class":961},[955,4187,3109],{"class":969},[955,4189,944],{"class":965},[955,4191,3114],{"class":1010},[955,4193,1061],{"class":965},[955,4195,2019],{"class":3119},[955,4197,1163],{"class":965},[955,4199,5],{"class":1010},[955,4201,1061],{"class":965},[955,4203,1402],{"class":969},[955,4205,1384],{"class":965},[955,4207,4209],{"class":957,"line":4208},48,[955,4210,3148],{"class":965},[955,4212,4214],{"class":957,"line":4213},49,[955,4215,1384],{"class":965},[955,4217,4219],{"class":957,"line":4218},50,[955,4220,992],{"emptyLinePlaceholder":8},[955,4222,4224],{"class":957,"line":4223},51,[955,4225,4226],{"class":1415},"\u002F\u002F Step 2: Verify the magic link (GET)\n",[955,4228,4230],{"class":957,"line":4229},52,[955,4231,4232],{"class":1415},"\u002F\u002F customMfaFlowsVerification sends the response on GET automatically.\n",[955,4234,4236,4238,4240,4243,4245,4247,4250,4252],{"class":957,"line":4235},53,[955,4237,3690],{"class":969},[955,4239,944],{"class":965},[955,4241,4242],{"class":1010},"get",[955,4244,1061],{"class":965},[955,4246,1088],{"class":978},[955,4248,4249],{"class":982},"\u002Fdanger-zone\u002Fverify",[955,4251,1088],{"class":978},[955,4253,1047],{"class":965},[955,4255,4257,4259],{"class":957,"line":4256},54,[955,4258,3577],{"class":969},[955,4260,1047],{"class":965},[955,4262,4264,4266],{"class":957,"line":4263},55,[955,4265,3584],{"class":969},[955,4267,1047],{"class":965},[955,4269,4271,4273],{"class":957,"line":4270},56,[955,4272,3591],{"class":969},[955,4274,1047],{"class":965},[955,4276,4278,4280],{"class":957,"line":4277},57,[955,4279,3598],{"class":969},[955,4281,1047],{"class":965},[955,4283,4285],{"class":957,"line":4284},58,[955,4286,4287],{"class":969},"  customMfaFlowsVerification\n",[955,4289,4291],{"class":957,"line":4290},59,[955,4292,1384],{"class":965},[955,4294,4296],{"class":957,"line":4295},60,[955,4297,992],{"emptyLinePlaceholder":8},[955,4299,4301],{"class":957,"line":4300},61,[955,4302,4303],{"class":1415},"\u002F\u002F Step 3: Verify the OTP code and execute the action (POST)\n",[955,4305,4307,4309,4311,4313,4315,4317,4319,4321],{"class":957,"line":4306},62,[955,4308,3690],{"class":969},[955,4310,944],{"class":965},[955,4312,3695],{"class":1010},[955,4314,1061],{"class":965},[955,4316,1088],{"class":978},[955,4318,4249],{"class":982},[955,4320,1088],{"class":978},[955,4322,1047],{"class":965},[955,4324,4326,4328,4330,4332,4334,4336],{"class":957,"line":4325},63,[955,4327,3570],{"class":1010},[955,4329,1061],{"class":965},[955,4331,1088],{"class":978},[955,4333,3717],{"class":982},[955,4335,1088],{"class":978},[955,4337,1072],{"class":965},[955,4339,4341,4343],{"class":957,"line":4340},64,[955,4342,3577],{"class":969},[955,4344,1047],{"class":965},[955,4346,4348,4350],{"class":957,"line":4347},65,[955,4349,3584],{"class":969},[955,4351,1047],{"class":965},[955,4353,4355,4357],{"class":957,"line":4354},66,[955,4356,3591],{"class":969},[955,4358,1047],{"class":965},[955,4360,4362,4364],{"class":957,"line":4361},67,[955,4363,3598],{"class":969},[955,4365,1047],{"class":965},[955,4367,4369,4371,4373,4375,4377,4379,4381,4383,4385,4387],{"class":957,"line":4368},68,[955,4370,3752],{"class":969},[955,4372,944],{"class":965},[955,4374,5],{"class":1010},[955,4376,3127],{"class":965},[955,4378,3761],{"class":969},[955,4380,1020],{"class":1036},[955,4382,979],{"class":978},[955,4384,3768],{"class":982},[955,4386,1088],{"class":978},[955,4388,3773],{"class":965},[955,4390,4392,4394],{"class":957,"line":4391},69,[955,4393,3605],{"class":969},[955,4395,1047],{"class":965},[955,4397,4399,4402],{"class":957,"line":4398},70,[955,4400,4401],{"class":969},"  detectBots",[955,4403,1047],{"class":965},[955,4405,4407,4409,4411,4413,4415,4417,4419,4421],{"class":957,"line":4406},71,[955,4408,3779],{"class":998},[955,4410,1396],{"class":965},[955,4412,2898],{"class":2897},[955,4414,872],{"class":965},[955,4416,2415],{"class":2897},[955,4418,1584],{"class":965},[955,4420,3792],{"class":998},[955,4422,3565],{"class":965},[955,4424,4426],{"class":957,"line":4425},72,[955,4427,4428],{"class":1415},"    \u002F\u002F MFA verified. req.link is populated with the decoded payload.\n",[955,4430,4432,4434,4436,4438,4440,4442,4444,4446,4448,4450],{"class":957,"line":4431},73,[955,4433,3800],{"class":998},[955,4435,966],{"class":965},[955,4437,884],{"class":1002},[955,4439,872],{"class":965},[955,4441,1214],{"class":1002},[955,4443,972],{"class":965},[955,4445,1452],{"class":1006},[955,4447,2956],{"class":969},[955,4449,944],{"class":965},[955,4451,4452],{"class":969},"link\n",[955,4454,4456],{"class":957,"line":4455},74,[955,4457,992],{"emptyLinePlaceholder":8},[955,4459,4461],{"class":957,"line":4460},75,[955,4462,4463],{"class":1415},"    \u002F\u002F Execute the protected action\n",[955,4465,4467,4469,4472,4474,4477,4479,4481,4483,4485,4487,4489,4491],{"class":957,"line":4466},76,[955,4468,3401],{"class":961},[955,4470,4471],{"class":1010}," deleteUserAccount",[955,4473,1061],{"class":965},[955,4475,4476],{"class":1010},"Number",[955,4478,1061],{"class":965},[955,4480,2898],{"class":969},[955,4482,944],{"class":965},[955,4484,1064],{"class":969},[955,4486,1399],{"class":1006},[955,4488,944],{"class":965},[955,4490,2930],{"class":969},[955,4492,3436],{"class":965},[955,4494,4496],{"class":957,"line":4495},77,[955,4497,992],{"emptyLinePlaceholder":8},[955,4499,4501,4503,4505,4507,4509,4511,4513,4515,4517,4519,4521,4523,4525,4528,4530],{"class":957,"line":4500},78,[955,4502,3106],{"class":961},[955,4504,3109],{"class":969},[955,4506,944],{"class":965},[955,4508,5],{"class":1010},[955,4510,3127],{"class":965},[955,4512,4077],{"class":969},[955,4514,1020],{"class":1036},[955,4516,3481],{"class":3480},[955,4518,872],{"class":965},[955,4520,3185],{"class":969},[955,4522,1020],{"class":1036},[955,4524,979],{"class":978},[955,4526,4527],{"class":982},"Account deleted.",[955,4529,1088],{"class":978},[955,4531,3142],{"class":965},[955,4533,4535],{"class":957,"line":4534},79,[955,4536,3148],{"class":965},[955,4538,4540],{"class":957,"line":4539},80,[955,4541,1384],{"class":965},[856,4543,4544,4545,4547,4548,4551],{},"After ",[882,4546,2164],{}," passes on a POST request, ",[882,4549,4550],{},"req.link"," is populated with the decoded JWT payload:",[946,4553,4555],{"className":948,"code":4554,"language":950,"meta":951,"style":951},"req.link = {\n  visitor: string,      \u002F\u002F visitor_id\n  subject: string,      \u002F\u002F `${reason}_${visitor_id}`\n  purpose: string,      \u002F\u002F the reason string ('account-delete' in this example)\n  jti?: string,         \u002F\u002F unique link identifier (optional)\n}\n",[882,4556,4557,4570,4584,4597,4610,4625],{"__ignoreMap":951},[955,4558,4559,4561,4563,4566,4568],{"class":957,"line":958},[955,4560,2898],{"class":969},[955,4562,944],{"class":965},[955,4564,4565],{"class":969},"link",[955,4567,1007],{"class":1006},[955,4569,3565],{"class":965},[955,4571,4572,4574,4576,4578,4581],{"class":957,"line":989},[955,4573,1033],{"class":969},[955,4575,1020],{"class":1036},[955,4577,1024],{"class":969},[955,4579,4580],{"class":965},",      ",[955,4582,4583],{"class":1415},"\u002F\u002F visitor_id\n",[955,4585,4586,4588,4590,4592,4594],{"class":957,"line":995},[955,4587,1053],{"class":969},[955,4589,1020],{"class":1036},[955,4591,1024],{"class":969},[955,4593,4580],{"class":965},[955,4595,4596],{"class":1415},"\u002F\u002F `${reason}_${visitor_id}`\n",[955,4598,4599,4601,4603,4605,4607],{"class":957,"line":1030},[955,4600,1078],{"class":969},[955,4602,1020],{"class":1036},[955,4604,1024],{"class":969},[955,4606,4580],{"class":965},[955,4608,4609],{"class":1415},"\u002F\u002F the reason string ('account-delete' in this example)\n",[955,4611,4612,4615,4617,4619,4622],{"class":957,"line":1050},[955,4613,4614],{"class":965},"  jti?",[955,4616,1020],{"class":1036},[955,4618,1024],{"class":969},[955,4620,4621],{"class":965},",         ",[955,4623,4624],{"class":1415},"\u002F\u002F unique link identifier (optional)\n",[955,4626,4627],{"class":957,"line":1075},[955,4628,1426],{"class":965},[920,4630,4632],{"id":4631},"manual-signing-and-verification","Manual signing and verification",[856,4634,4635,4636,4639,4640,4643],{},"For full control over the JWT lifecycle, use ",[882,4637,4638],{},"signNewTempLink"," and ",[882,4641,4642],{},"verifyTempLink"," directly. These are the low-level primitives that all built-in flows call internally.",[946,4645,4647],{"className":948,"code":4646,"language":950,"meta":951,"style":951},"import { signNewTempLink, verifyTempLink, magicLinksCache } from '@riavzon\u002Fauth'\nimport crypto from 'crypto'\n\n\u002F\u002F --- Sign ---\nconst token = signNewTempLink\u003C{ transferId: string }>({\n  visitor: user.visitor_id,        \u002F\u002F string\n  subject: String(user.userId),    \u002F\u002F string\n  purpose: 'wire-transfer',\n  jti: crypto.randomUUID(),\n  transferId: 'txn_abc123',\n})\n\n\u002F\u002F Build the email URL\nconst url = new URL('\u002Fauth\u002Fbounce', config.magic_links.domain)\nurl.searchParams.set('visitor', user.visitor_id)\nurl.searchParams.set('token', token)\nurl.searchParams.set('random', crypto.createHash('sha256')\n  .update(crypto.randomBytes(128).toString('hex'))\n  .digest('hex'))\nurl.searchParams.set('reason', 'wire-transfer')\n\n\u002F\u002F Send url.toString() via your email provider\n\n\u002F\u002F --- Verify ---\nconst result = verifyTempLink\u003C{ transferId: string }>(token)\n\nif (!result.valid) {\n  console.error('Link invalid:', result.errorType)\n  return\n}\n\nconst { subject, purpose, transferId } = result.payload!\n\u002F\u002F purpose === 'wire-transfer', transferId === 'txn_abc123'\n\n\u002F\u002F --- Invalidate ---\nconst cache = magicLinksCache()\ncache.delete(token)  \u002F\u002F Mark the link as consumed\n",[882,4648,4649,4675,4689,4693,4698,4720,4738,4759,4774,4788,4804,4808,4812,4817,4857,4890,4916,4956,4990,5007,5037,5041,5046,5050,5055,5080,5084,5100,5129,5133,5137,5141,5171,5176,5180,5185,5197],{"__ignoreMap":951},[955,4650,4651,4653,4655,4657,4659,4661,4663,4665,4667,4669,4671,4673],{"class":957,"line":958},[955,4652,962],{"class":961},[955,4654,966],{"class":965},[955,4656,4638],{"class":969},[955,4658,872],{"class":965},[955,4660,4642],{"class":969},[955,4662,872],{"class":965},[955,4664,1527],{"class":969},[955,4666,972],{"class":965},[955,4668,975],{"class":961},[955,4670,979],{"class":978},[955,4672,983],{"class":982},[955,4674,986],{"class":978},[955,4676,4677,4679,4681,4683,4685,4687],{"class":957,"line":989},[955,4678,962],{"class":961},[955,4680,1101],{"class":969},[955,4682,2756],{"class":961},[955,4684,979],{"class":978},[955,4686,3556],{"class":982},[955,4688,986],{"class":978},[955,4690,4691],{"class":957,"line":995},[955,4692,992],{"emptyLinePlaceholder":8},[955,4694,4695],{"class":957,"line":1030},[955,4696,4697],{"class":1415},"\u002F\u002F --- Sign ---\n",[955,4699,4700,4702,4704,4706,4709,4711,4714,4716,4718],{"class":957,"line":1050},[955,4701,999],{"class":998},[955,4703,1003],{"class":1002},[955,4705,1007],{"class":1006},[955,4707,4708],{"class":1010}," signNewTempLink",[955,4710,1014],{"class":965},[955,4712,4713],{"class":969},"transferId",[955,4715,1020],{"class":1006},[955,4717,1024],{"class":1023},[955,4719,1027],{"class":965},[955,4721,4722,4724,4726,4728,4730,4732,4735],{"class":957,"line":1075},[955,4723,1033],{"class":969},[955,4725,1020],{"class":1036},[955,4727,1039],{"class":969},[955,4729,944],{"class":965},[955,4731,1044],{"class":969},[955,4733,4734],{"class":965},",        ",[955,4736,4737],{"class":1415},"\u002F\u002F string\n",[955,4739,4740,4742,4744,4746,4748,4750,4752,4754,4757],{"class":957,"line":1093},[955,4741,1053],{"class":969},[955,4743,1020],{"class":1036},[955,4745,1058],{"class":1010},[955,4747,1061],{"class":965},[955,4749,1064],{"class":969},[955,4751,944],{"class":965},[955,4753,2930],{"class":969},[955,4755,4756],{"class":965},"),    ",[955,4758,4737],{"class":1415},[955,4760,4761,4763,4765,4767,4770,4772],{"class":957,"line":1112},[955,4762,1078],{"class":969},[955,4764,1020],{"class":1036},[955,4766,979],{"class":978},[955,4768,4769],{"class":982},"wire-transfer",[955,4771,1088],{"class":978},[955,4773,1047],{"class":965},[955,4775,4776,4778,4780,4782,4784,4786],{"class":957,"line":1129},[955,4777,1096],{"class":969},[955,4779,1020],{"class":1036},[955,4781,1101],{"class":969},[955,4783,944],{"class":965},[955,4785,1106],{"class":1010},[955,4787,1109],{"class":965},[955,4789,4790,4793,4795,4797,4800,4802],{"class":957,"line":1433},[955,4791,4792],{"class":969},"  transferId",[955,4794,1020],{"class":1036},[955,4796,979],{"class":978},[955,4798,4799],{"class":982},"txn_abc123",[955,4801,1088],{"class":978},[955,4803,1047],{"class":965},[955,4805,4806],{"class":957,"line":3031},[955,4807,1132],{"class":965},[955,4809,4810],{"class":957,"line":3047},[955,4811,992],{"emptyLinePlaceholder":8},[955,4813,4814],{"class":957,"line":3064},[955,4815,4816],{"class":1415},"\u002F\u002F Build the email URL\n",[955,4818,4819,4821,4824,4826,4828,4831,4833,4835,4838,4840,4842,4845,4847,4850,4852,4855],{"class":957,"line":3070},[955,4820,999],{"class":998},[955,4822,4823],{"class":1002}," url",[955,4825,1007],{"class":1006},[955,4827,3405],{"class":3404},[955,4829,4830],{"class":1010}," URL",[955,4832,1061],{"class":965},[955,4834,1088],{"class":978},[955,4836,4837],{"class":982},"\u002Fauth\u002Fbounce",[955,4839,1088],{"class":978},[955,4841,872],{"class":965},[955,4843,4844],{"class":969},"config",[955,4846,944],{"class":965},[955,4848,4849],{"class":969},"magic_links",[955,4851,944],{"class":965},[955,4853,4854],{"class":969},"domain",[955,4856,1384],{"class":965},[955,4858,4859,4862,4864,4867,4869,4872,4874,4876,4878,4880,4882,4884,4886,4888],{"class":957,"line":3076},[955,4860,4861],{"class":969},"url",[955,4863,944],{"class":965},[955,4865,4866],{"class":969},"searchParams",[955,4868,944],{"class":965},[955,4870,4871],{"class":1010},"set",[955,4873,1061],{"class":965},[955,4875,1088],{"class":978},[955,4877,891],{"class":982},[955,4879,1088],{"class":978},[955,4881,872],{"class":965},[955,4883,1064],{"class":969},[955,4885,944],{"class":965},[955,4887,1044],{"class":969},[955,4889,1384],{"class":965},[955,4891,4892,4894,4896,4898,4900,4902,4904,4906,4908,4910,4912,4914],{"class":957,"line":3081},[955,4893,4861],{"class":969},[955,4895,944],{"class":965},[955,4897,4866],{"class":969},[955,4899,944],{"class":965},[955,4901,4871],{"class":1010},[955,4903,1061],{"class":965},[955,4905,1088],{"class":978},[955,4907,894],{"class":982},[955,4909,1088],{"class":978},[955,4911,872],{"class":965},[955,4913,894],{"class":969},[955,4915,1384],{"class":965},[955,4917,4918,4920,4922,4924,4926,4928,4930,4932,4934,4936,4938,4940,4942,4945,4947,4949,4952,4954],{"class":957,"line":3103},[955,4919,4861],{"class":969},[955,4921,944],{"class":965},[955,4923,4866],{"class":969},[955,4925,944],{"class":965},[955,4927,4871],{"class":1010},[955,4929,1061],{"class":965},[955,4931,1088],{"class":978},[955,4933,898],{"class":982},[955,4935,1088],{"class":978},[955,4937,872],{"class":965},[955,4939,3556],{"class":969},[955,4941,944],{"class":965},[955,4943,4944],{"class":1010},"createHash",[955,4946,1061],{"class":965},[955,4948,1088],{"class":978},[955,4950,4951],{"class":982},"sha256",[955,4953,1088],{"class":978},[955,4955,1384],{"class":965},[955,4957,4958,4961,4964,4966,4968,4970,4972,4974,4976,4978,4980,4982,4984,4986,4988],{"class":957,"line":3145},[955,4959,4960],{"class":965},"  .",[955,4962,4963],{"class":1010},"update",[955,4965,1061],{"class":965},[955,4967,3556],{"class":969},[955,4969,944],{"class":965},[955,4971,3812],{"class":1010},[955,4973,1061],{"class":965},[955,4975,3817],{"class":3119},[955,4977,1163],{"class":965},[955,4979,3822],{"class":1010},[955,4981,1061],{"class":965},[955,4983,1088],{"class":978},[955,4985,3829],{"class":982},[955,4987,1088],{"class":978},[955,4989,3436],{"class":965},[955,4991,4992,4994,4997,4999,5001,5003,5005],{"class":957,"line":3151},[955,4993,4960],{"class":965},[955,4995,4996],{"class":1010},"digest",[955,4998,1061],{"class":965},[955,5000,1088],{"class":978},[955,5002,3829],{"class":982},[955,5004,1088],{"class":978},[955,5006,3436],{"class":965},[955,5008,5009,5011,5013,5015,5017,5019,5021,5023,5025,5027,5029,5031,5033,5035],{"class":957,"line":3156},[955,5010,4861],{"class":969},[955,5012,944],{"class":965},[955,5014,4866],{"class":969},[955,5016,944],{"class":965},[955,5018,4871],{"class":1010},[955,5020,1061],{"class":965},[955,5022,1088],{"class":978},[955,5024,902],{"class":982},[955,5026,1088],{"class":978},[955,5028,872],{"class":965},[955,5030,1088],{"class":978},[955,5032,4769],{"class":982},[955,5034,1088],{"class":978},[955,5036,1384],{"class":965},[955,5038,5039],{"class":957,"line":3162},[955,5040,992],{"emptyLinePlaceholder":8},[955,5042,5043],{"class":957,"line":3199},[955,5044,5045],{"class":1415},"\u002F\u002F Send url.toString() via your email provider\n",[955,5047,5048],{"class":957,"line":3742},[955,5049,992],{"emptyLinePlaceholder":8},[955,5051,5052],{"class":957,"line":3749},[955,5053,5054],{"class":1415},"\u002F\u002F --- Verify ---\n",[955,5056,5057,5059,5061,5063,5066,5068,5070,5072,5074,5076,5078],{"class":957,"line":3776},[955,5058,999],{"class":998},[955,5060,1361],{"class":1002},[955,5062,1007],{"class":1006},[955,5064,5065],{"class":1010}," verifyTempLink",[955,5067,1014],{"class":965},[955,5069,4713],{"class":969},[955,5071,1020],{"class":1006},[955,5073,1024],{"class":1023},[955,5075,1378],{"class":965},[955,5077,894],{"class":969},[955,5079,1384],{"class":965},[955,5081,5082],{"class":957,"line":3797},[955,5083,992],{"emptyLinePlaceholder":8},[955,5085,5086,5088,5090,5092,5094,5096,5098],{"class":957,"line":3836},[955,5087,1393],{"class":961},[955,5089,1396],{"class":965},[955,5091,1399],{"class":1006},[955,5093,1402],{"class":969},[955,5095,944],{"class":965},[955,5097,1407],{"class":969},[955,5099,1410],{"class":965},[955,5101,5102,5105,5107,5109,5111,5113,5116,5118,5120,5122,5124,5127],{"class":957,"line":3841},[955,5103,5104],{"class":969},"  console",[955,5106,944],{"class":965},[955,5108,3130],{"class":1010},[955,5110,1061],{"class":965},[955,5112,1088],{"class":978},[955,5114,5115],{"class":982},"Link invalid:",[955,5117,1088],{"class":978},[955,5119,872],{"class":965},[955,5121,1402],{"class":969},[955,5123,944],{"class":965},[955,5125,5126],{"class":969},"errorType",[955,5128,1384],{"class":965},[955,5130,5131],{"class":957,"line":3856},[955,5132,1421],{"class":961},[955,5134,5135],{"class":957,"line":3864},[955,5136,1426],{"class":965},[955,5138,5139],{"class":957,"line":3880},[955,5140,992],{"emptyLinePlaceholder":8},[955,5142,5143,5145,5147,5149,5151,5153,5155,5157,5159,5161,5163,5165,5168],{"class":957,"line":3926},[955,5144,999],{"class":998},[955,5146,966],{"class":965},[955,5148,1214],{"class":1002},[955,5150,872],{"class":965},[955,5152,884],{"class":1002},[955,5154,872],{"class":965},[955,5156,4713],{"class":1002},[955,5158,972],{"class":965},[955,5160,1452],{"class":1006},[955,5162,1361],{"class":969},[955,5164,944],{"class":965},[955,5166,5167],{"class":969},"payload",[955,5169,5170],{"class":1006},"!\n",[955,5172,5173],{"class":957,"line":3945},[955,5174,5175],{"class":1415},"\u002F\u002F purpose === 'wire-transfer', transferId === 'txn_abc123'\n",[955,5177,5178],{"class":957,"line":3958},[955,5179,992],{"emptyLinePlaceholder":8},[955,5181,5182],{"class":957,"line":3966},[955,5183,5184],{"class":1415},"\u002F\u002F --- Invalidate ---\n",[955,5186,5187,5189,5191,5193,5195],{"class":957,"line":3972},[955,5188,999],{"class":998},[955,5190,1548],{"class":1002},[955,5192,1007],{"class":1006},[955,5194,1553],{"class":1010},[955,5196,1556],{"class":965},[955,5198,5199,5201,5203,5205,5207,5209,5212],{"class":957,"line":4012},[955,5200,1485],{"class":969},[955,5202,944],{"class":965},[955,5204,1577],{"class":1010},[955,5206,1061],{"class":965},[955,5208,894],{"class":969},[955,5210,5211],{"class":965},")  ",[955,5213,5214],{"class":1415},"\u002F\u002F Mark the link as consumed\n",[1472,5216,5217],{},[856,5218,5219,5220,872,5222,5224,5225,5227],{},"When signing manually, you are responsible for rate limiting, visitor matching, and link consumption. The built-in flows and middleware handle all of this automatically. Prefer using ",[882,5221,1768],{},[882,5223,2144],{},", or the pre-built ",[882,5226,2740],{}," router unless you have a specific reason to sign tokens directly.",[2824,5229,5230],{},[856,5231,1204,5232,5234,5235,5238],{},[865,5233,1671],{"href":1670}," package provides a full client-side implementation for all magic link flows. It handles bounce redirection, route registration, composable state management, and 202 MFA response propagation. See the ",[865,5236,5237],{"href":1670},"auth-h3client documentation"," for usage details.",[912,5240],{},[915,5242,1910],{"id":5243},"email-delivery-1",[856,5245,5246,5247,5250,5251,5254],{},"Magic link emails are sent via the ",[865,5248,1924],{"href":1922,"rel":5249},[908]," SDK. The service uses ",[865,5252,1918],{"href":1916,"rel":5253},[908]," templates to render two email types:",[1169,5256,5257,5269],{},[1172,5258,5259],{},[1175,5260,5261,5264,5266],{},[1178,5262,5263],{},"Template",[1178,5265,2281],{},[1178,5267,5268],{},"Contains",[1188,5270,5271,5282],{},[1175,5272,5273,5276,5279],{},[1193,5274,5275],{},"OTP template",[1193,5277,5278],{},"Adaptive MFA, custom MFA, email update",[1193,5280,5281],{},"Magic link button, 7-digit OTP code, device\u002Fbrowser\u002Flocation metadata",[1175,5283,5284,5287,5289],{},[1193,5285,5286],{},"Password reset template",[1193,5288,1978],{},[1193,5290,5291],{},"Magic link button, device\u002Fbrowser\u002Flocation metadata (no OTP code)",[856,5293,5294,5295,4639,5298,944],{},"Both templates include the website name, privacy policy link, contact page link, and email images (OTP banner). These values are configured in ",[882,5296,5297],{},"magic_links.notificationEmail",[882,5299,5300],{},"magic_links.emailImages",[912,5302],{},[915,5304,5306],{"id":5305},"configuration-reference","Configuration reference",[856,5308,5309,5310,5312,5313,944],{},"All options live under the ",[882,5311,4849],{}," key in the auth service config. Overridable via ",[882,5314,5315],{},"config.magic_links.*",[920,5317,25],{"id":5318},"core",[5320,5321,5322,5328,5343,5355,5364,5373],"field-group",{},[5323,5324,5325],"field",{"name":1479,"type":1201,":required":3223},[856,5326,5327],{},"The HMAC-SHA512 signing key for magic link JWTs. Must be separate from the access-token and refresh-token secrets.",[5323,5329,5331],{"name":5330,"type":1201},"expiresIn",[856,5332,5333,5334,5336,5337,5342],{},"Default ",[882,5335,1162],{}," - JWT lifetime in ",[865,5338,5341],{"href":5339,"rel":5340},"https:\u002F\u002Fgithub.com\u002Fvercel\u002Fms",[908],"ms-compatible"," string format.",[5323,5344,5347],{"name":5345,"type":5346},"expiresInMs","number",[856,5348,5333,5349,5352,5353,944],{},[882,5350,5351],{},"900_000"," - Same lifetime in milliseconds, used for the LRU cache TTL. Must match ",[882,5354,5330],{},[5323,5356,5357],{"name":4854,"type":1201,":required":3223},[856,5358,5359,5360,5363],{},"Base URL for link construction (e.g. ",[882,5361,5362],{},"'https:\u002F\u002Fexample.com'","). Prepended to every path.",[5323,5365,5367],{"name":5366,"type":5346},"maxCacheEntries",[856,5368,5333,5369,5372],{},[882,5370,5371],{},"500"," - Maximum number of active magic link tokens held in the LRU cache.",[5323,5374,5376],{"name":5375,"type":1201},"linkToResetPasswordPage",[856,5377,5378],{},"The URL of your app's \"reset your password\" page, included in the password-reset email body.",[920,5380,5382],{"id":5381},"paths","Paths",[856,5384,5385,5386,5388,5389,944],{},"Path segments appended to ",[882,5387,4854],{}," when building email link URLs. All default to ",[882,5390,5391],{},"'\u002Fauth\u002Fbounce'",[5320,5393,5394,5402,5410],{},[5323,5395,5397],{"name":5396,"type":1201},"paths.pathForPasswordResetLink",[856,5398,5333,5399,5401],{},[882,5400,5391],{}," - Email link target for password reset flows.",[5323,5403,5405],{"name":5404,"type":1201},"paths.pathForAdaptiveMfaLink",[856,5406,5333,5407,5409],{},[882,5408,5391],{}," - Email link target for adaptive MFA flows.",[5323,5411,5413],{"name":5412,"type":1201},"paths.pathForCustomFlow",[856,5414,5333,5415,5417],{},[882,5416,5391],{}," - Email link target for custom MFA flows and email updates.",[920,5419,5421],{"id":5420},"thresholds","Thresholds",[856,5423,5424],{},"Usage limits per link. Each middleware tracks GET and POST uses separately. When exceeded, the link is invalidated regardless of expiry.",[5320,5426,5427,5435,5443,5451,5459,5467],{},[5323,5428,5430],{"name":5429,"type":5346},"thresholds.adaptiveMfa.allowedPerSuccessfulGet",[856,5431,5333,5432,5434],{},[882,5433,1852],{}," - Maximum GET verifications allowed per adaptive MFA link.",[5323,5436,5438],{"name":5437,"type":5346},"thresholds.adaptiveMfa.allowedPerSuccessfulPost",[856,5439,5333,5440,5442],{},[882,5441,2697],{}," - Maximum POST submissions allowed per adaptive MFA link.",[5323,5444,5446],{"name":5445,"type":5346},"thresholds.linkPasswordVerification.allowedPerSuccessfulGet",[856,5447,5333,5448,5450],{},[882,5449,1852],{}," - Maximum GET verifications allowed per password reset link.",[5323,5452,5454],{"name":5453,"type":5346},"thresholds.linkPasswordVerification.allowedPerSuccessfulPost",[856,5455,5333,5456,5458],{},[882,5457,2697],{}," - Maximum POST submissions allowed per password reset link.",[5323,5460,5462],{"name":5461,"type":5346},"thresholds.customMfaFlowsAndEmailChanges.allowedPerSuccessfulGet",[856,5463,5333,5464,5466],{},[882,5465,1852],{}," - Maximum GET verifications allowed per custom MFA or email update link.",[5323,5468,5470],{"name":5469,"type":5346},"thresholds.customMfaFlowsAndEmailChanges.allowedPerSuccessfulPost",[856,5471,5333,5472,5474],{},[882,5473,2697],{}," - Maximum POST submissions allowed per custom MFA or email update link.",[920,5476,5478],{"id":5477},"email-templates","Email templates",[5320,5480,5481,5487,5493,5499,5505,5511],{},[5323,5482,5484],{"name":5483,"type":1201},"emailImages.otpBanner",[856,5485,5486],{},"URL of the banner image displayed at the top of OTP emails.",[5323,5488,5490],{"name":5489,"type":1201},"notificationEmail.websiteName",[856,5491,5492],{},"The display name of your website, shown in all magic link emails.",[5323,5494,5496],{"name":5495,"type":1201},"notificationEmail.privacyPolicyLink",[856,5497,5498],{},"URL to your privacy policy page, linked in the email footer.",[5323,5500,5502],{"name":5501,"type":1201},"notificationEmail.contactPageLink",[856,5503,5504],{},"URL to your contact page, linked in the email footer.",[5323,5506,5508],{"name":5507,"type":1201},"notificationEmail.changePasswordPageLink",[856,5509,5510],{},"URL to your change-password page, linked in notification emails.",[5323,5512,5514],{"name":5513,"type":1201},"notificationEmail.loginPageLink",[856,5515,5516],{},"URL to your login page, linked in notification emails.",[912,5518],{},[915,5520,2821],{"id":5521},"routes-reference",[856,5523,5524],{},"All routes handled by the magic links router on the auth server:",[1169,5526,5527,5539],{},[1172,5528,5529],{},[1175,5530,5531,5533,5535,5537],{},[1178,5532,1938],{},[1178,5534,1941],{},[1178,5536,2606],{},[1178,5538,1186],{},[1188,5540,5541,5558,5575,5592,5609,5626,5643,5658,5675],{},[1175,5542,5543,5547,5551,5555],{},[1193,5544,5545],{},[882,5546,1952],{},[1193,5548,5549],{},[882,5550,1957],{},[1193,5552,5553],{},[882,5554,2619],{},[1193,5556,5557],{},"Validate adaptive MFA link",[1175,5559,5560,5564,5568,5572],{},[1193,5561,5562],{},[882,5563,1967],{},[1193,5565,5566],{},[882,5567,1957],{},[1193,5569,5570],{},[882,5571,2619],{},[1193,5573,5574],{},"Submit OTP code for adaptive MFA",[1175,5576,5577,5581,5586,5589],{},[1193,5578,5579],{},[882,5580,1967],{},[1193,5582,5583],{},[882,5584,5585],{},"\u002Fcustom\u002Fmfa\u002F:reason",[1193,5587,5588],{},"Auth required, IP restriction",[1193,5590,5591],{},"Initiate a custom MFA flow",[1175,5593,5594,5598,5602,5606],{},[1193,5595,5596],{},[882,5597,1952],{},[1193,5599,5600],{},[882,5601,2234],{},[1193,5603,5604],{},[882,5605,2164],{},[1193,5607,5608],{},"Validate custom MFA link",[1175,5610,5611,5615,5619,5623],{},[1193,5612,5613],{},[882,5614,1967],{},[1193,5616,5617],{},[882,5618,2234],{},[1193,5620,5621],{},[882,5622,2164],{},[1193,5624,5625],{},"Submit OTP code for custom MFA",[1175,5627,5628,5632,5636,5640],{},[1193,5629,5630],{},[882,5631,1967],{},[1193,5633,5634],{},[882,5635,2247],{},[1193,5637,5638],{},[882,5639,2164],{},[1193,5641,5642],{},"Submit new email with OTP + password",[1175,5644,5645,5649,5653,5655],{},[1193,5646,5647],{},[882,5648,1967],{},[1193,5650,5651],{},[882,5652,2088],{},[1193,5654,1777],{},[1193,5656,5657],{},"Initiate password reset",[1175,5659,5660,5664,5668,5672],{},[1193,5661,5662],{},[882,5663,1952],{},[1193,5665,5666],{},[882,5667,2102],{},[1193,5669,5670],{},[882,5671,2039],{},[1193,5673,5674],{},"Validate password reset link",[1175,5676,5677,5681,5685,5689],{},[1193,5678,5679],{},[882,5680,1967],{},[1193,5682,5683],{},[882,5684,2102],{},[1193,5686,5687],{},[882,5688,2039],{},[1193,5690,5691],{},"Submit new password",[912,5693],{},[915,5695,5697],{"id":5696},"rate-limiter-reference","Rate limiter reference",[856,5699,5700,5701,5704,5705,5708,5709,5712],{},"Magic link flows apply multiple layers of rate limiting. All values below are defaults, overridable via ",[882,5702,5703],{},"config.rate_limiters.*",". See ",[865,5706,147],{"href":5707},"\u002Fdocs\u002Fiam\u002Frate-limiting"," for the ",[882,5710,5711],{},"guard()"," architecture and the consecutive-violation escalation system.",[920,5714,5716],{"id":5715},"email-sending-mfa-and-custom-flows","Email sending (MFA and custom flows)",[856,5718,5719,5720,4639,5722,5724],{},"Applied by ",[882,5721,1768],{},[882,5723,2144],{}," before sending any email.",[1169,5726,5727,5743],{},[1172,5728,5729],{},[1175,5730,5731,5733,5735,5737,5739,5741],{},[1178,5732,1789],{},[1178,5734,1792],{},[1178,5736,1795],{},[1178,5738,1798],{},[1178,5740,1801],{},[1178,5742,2677],{},[1188,5744,5745,5764,5783,5802,5826],{},[1175,5746,5747,5749,5753,5755,5757,5759],{},[1193,5748,1808],{},[1193,5750,5751],{},[882,5752,1813],{},[1193,5754,1816],{},[1193,5756,1819],{},[1193,5758,1819],{},[1193,5760,5761],{},[882,5762,5763],{},"rate_limiters.emailMfaLimiters.globalEmailLimiter",[1175,5765,5766,5768,5772,5774,5776,5778],{},[1193,5767,1826],{},[1193,5769,5770],{},[882,5771,1831],{},[1193,5773,1834],{},[1193,5775,1819],{},[1193,5777,1839],{},[1193,5779,5780],{},[882,5781,5782],{},"rate_limiters.emailMfaLimiters.userIdLimiter",[1175,5784,5785,5787,5791,5793,5795,5797],{},[1193,5786,1844],{},[1193,5788,5789],{},[882,5790,1849],{},[1193,5792,1852],{},[1193,5794,1819],{},[1193,5796,1857],{},[1193,5798,5799],{},[882,5800,5801],{},"rate_limiters.emailMfaLimiters.ipLimiter",[1175,5803,5804,5807,5812,5815,5818,5821],{},[1193,5805,5806],{},"Burst",[1193,5808,5809],{},[882,5810,5811],{},"{ip}_{random}_{reason}",[1193,5813,5814],{},"1",[1193,5816,5817],{},"1 s",[1193,5819,5820],{},"30 min",[1193,5822,5823],{},[882,5824,5825],{},"rate_limiters.emailMfaLimiters.unionLimiter",[1175,5827,5828,5831,5835,5837,5839,5842],{},[1193,5829,5830],{},"Slow",[1193,5832,5833],{},[882,5834,5811],{},[1193,5836,1773],{},[1193,5838,5820],{},[1193,5840,5841],{},"15 min",[1193,5843,5844],{},[882,5845,5825],{},[920,5847,5849],{"id":5848},"password-reset-initiation","Password reset initiation",[856,5851,5852,5853,5856,5857,944],{},"Applied by the ",[882,5854,5855],{},"initPasswordReset"," controller on ",[882,5858,1997],{},[1169,5860,5861,5877],{},[1172,5862,5863],{},[1175,5864,5865,5867,5869,5871,5873,5875],{},[1178,5866,1789],{},[1178,5868,1792],{},[1178,5870,1795],{},[1178,5872,1798],{},[1178,5874,1801],{},[1178,5876,2677],{},[1188,5878,5879,5897,5916,5937,5957],{},[1175,5880,5881,5883,5887,5889,5891,5893],{},[1193,5882,1808],{},[1193,5884,5885],{},[882,5886,1813],{},[1193,5888,1816],{},[1193,5890,1819],{},[1193,5892,1819],{},[1193,5894,5895],{},[882,5896,5763],{},[1175,5898,5899,5901,5905,5907,5909,5911],{},[1193,5900,1844],{},[1193,5902,5903],{},[882,5904,1849],{},[1193,5906,1852],{},[1193,5908,1819],{},[1193,5910,1857],{},[1193,5912,5913],{},[882,5914,5915],{},"rate_limiters.initPasswordResetLimiters.ipLimiter",[1175,5917,5918,5921,5926,5928,5930,5932],{},[1193,5919,5920],{},"Per-email",[1193,5922,5923],{},[882,5924,5925],{},"{email}",[1193,5927,1852],{},[1193,5929,1819],{},[1193,5931,1857],{},[1193,5933,5934],{},[882,5935,5936],{},"rate_limiters.initPasswordResetLimiters.emailLimiter",[1175,5938,5939,5941,5946,5948,5950,5952],{},[1193,5940,5806],{},[1193,5942,5943],{},[882,5944,5945],{},"{ip}_{email}",[1193,5947,5814],{},[1193,5949,5817],{},[1193,5951,5820],{},[1193,5953,5954],{},[882,5955,5956],{},"rate_limiters.initPasswordResetLimiters.unionLimiter",[1175,5958,5959,5961,5965,5967,5969,5971],{},[1193,5960,5830],{},[1193,5962,5963],{},[882,5964,5945],{},[1193,5966,1773],{},[1193,5968,5820],{},[1193,5970,5841],{},[1193,5972,5973],{},[882,5974,5956],{},[920,5976,5978],{"id":5977},"link-verification-all-three-middleware-functions","Link verification (all three middleware functions)",[856,5980,5719,5981,872,5983,876,5985,5987],{},[882,5982,2619],{},[882,5984,2039],{},[882,5986,2164],{}," on every GET and POST.",[1169,5989,5990,6006],{},[1172,5991,5992],{},[1175,5993,5994,5996,5998,6000,6002,6004],{},[1178,5995,1789],{},[1178,5997,1792],{},[1178,5999,1795],{},[1178,6001,1798],{},[1178,6003,1801],{},[1178,6005,2677],{},[1188,6007,6008,6028,6047],{},[1175,6009,6010,6012,6016,6019,6021,6023],{},[1193,6011,5806],{},[1193,6013,6014],{},[882,6015,1849],{},[1193,6017,6018],{},"2",[1193,6020,5817],{},[1193,6022,5841],{},[1193,6024,6025],{},[882,6026,6027],{},"rate_limiters.linkVerificationLimiter.unionLimiter",[1175,6029,6030,6032,6036,6039,6041,6043],{},[1193,6031,5830],{},[1193,6033,6034],{},[882,6035,1849],{},[1193,6037,6038],{},"30",[1193,6040,5820],{},[1193,6042,5820],{},[1193,6044,6045],{},[882,6046,6027],{},[1175,6048,6049,6052,6057,6060,6063,6066],{},[1193,6050,6051],{},"JTI single-use",[1193,6053,6054],{},[882,6055,6056],{},"{jti}",[1193,6058,6059],{},"0",[1193,6061,6062],{},"0 s",[1193,6064,6065],{},"20 min",[1193,6067,6068],{},"Internal (not configurable)",[856,6070,6071],{},"The JTI limiter is a block-only limiter. After a link is consumed, its JTI is blocked for 20 minutes to prevent replay.",[920,6073,6075],{"id":6074},"otp-code-submission","OTP code submission",[856,6077,5719,6078,6080],{},[882,6079,2188],{}," when processing a POST with a 7-digit code (adaptive MFA, custom MFA, and email update).",[1169,6082,6083,6099],{},[1172,6084,6085],{},[1175,6086,6087,6089,6091,6093,6095,6097],{},[1178,6088,1789],{},[1178,6090,1792],{},[1178,6092,1795],{},[1178,6094,1798],{},[1178,6096,1801],{},[1178,6098,2677],{},[1188,6100,6101,6120,6139,6158],{},[1175,6102,6103,6105,6109,6111,6113,6115],{},[1193,6104,5806],{},[1193,6106,6107],{},[882,6108,1849],{},[1193,6110,5814],{},[1193,6112,5817],{},[1193,6114,5820],{},[1193,6116,6117],{},[882,6118,6119],{},"rate_limiters.tempPostRoutesLimiter.unionLimiter",[1175,6121,6122,6124,6128,6130,6133,6135],{},[1193,6123,5830],{},[1193,6125,6126],{},[882,6127,1849],{},[1193,6129,1852],{},[1193,6131,6132],{},"10 min",[1193,6134,6132],{},[1193,6136,6137],{},[882,6138,6119],{},[1175,6140,6141,6144,6148,6150,6152,6154],{},[1193,6142,6143],{},"Per-JTI",[1193,6145,6146],{},[882,6147,6056],{},[1193,6149,5814],{},[1193,6151,5817],{},[1193,6153,5820],{},[1193,6155,6156],{},[882,6157,6119],{},[1175,6159,6160,6163,6168,6171,6173,6175],{},[1193,6161,6162],{},"Per-IP (code hash)",[1193,6164,6165],{},[882,6166,6167],{},"{codeHash}",[1193,6169,6170],{},"6",[1193,6172,6132],{},[1193,6174,6132],{},[1193,6176,6177],{},[882,6178,6179],{},"rate_limiters.tempPostRoutesLimiter.ipLimit",[856,6181,6182],{},"After successful verification, the submitted code hash is blocked for 10 minutes and the JTI is blocked for 20 minutes. This prevents reuse of the same code or link.",[6184,6185,6186],"style",{},"html pre.shiki code .sZ328, html code.shiki .sZ328{--shiki-light:#AF00DB;--shiki-default:#AF00DB;--shiki-dark:#FF79C6}html pre.shiki code .sDd4n, html code.shiki .sDd4n{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#F8F8F2}html pre.shiki code .sjsA6, html code.shiki .sjsA6{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#F8F8F2}html pre.shiki code .sFkSl, html code.shiki .sFkSl{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#E9F284}html pre.shiki code .sFB1V, html code.shiki .sFB1V{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#F1FA8C}html pre.shiki code .sl46w, html code.shiki .sl46w{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#FF79C6}html pre.shiki code .s3JHE, html code.shiki .s3JHE{--shiki-light:#0070C1;--shiki-default:#0070C1;--shiki-dark:#F8F8F2}html pre.shiki code .saOXh, html code.shiki .saOXh{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#FF79C6}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}html pre.shiki code .sFs1U, html code.shiki .sFs1U{--shiki-light:#267F99;--shiki-light-font-style:inherit;--shiki-default:#267F99;--shiki-default-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic}html pre.shiki code .s34zl, html code.shiki .s34zl{--shiki-light:#001080;--shiki-default:#001080;--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 .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}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 .spgvN, html code.shiki .spgvN{--shiki-light:#098658;--shiki-default:#098658;--shiki-dark:#BD93F9}html pre.shiki code .sakC6, html code.shiki .sakC6{--shiki-light:#0000FF;--shiki-light-font-weight:inherit;--shiki-default:#0000FF;--shiki-default-font-weight:inherit;--shiki-dark:#FF79C6;--shiki-dark-font-weight:bold}html pre.shiki code .sjR7W, html code.shiki .sjR7W{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#BD93F9}",{"title":951,"searchDepth":989,"depth":989,"links":6188},[6189,6194,6197,6198,6199,6204,6209,6212,6219,6220,6226,6227],{"id":917,"depth":989,"text":918,"children":6190},[6191,6192,6193],{"id":922,"depth":995,"text":925},{"id":1166,"depth":995,"text":1167},{"id":1263,"depth":995,"text":1264},{"id":1285,"depth":989,"text":1286,"children":6195},[6196],{"id":1289,"depth":995,"text":1292},{"id":1485,"depth":989,"text":1486},{"id":1599,"depth":989,"text":1600},{"id":1677,"depth":989,"text":1678,"children":6200},[6201,6202,6203],{"id":1684,"depth":995,"text":1685},{"id":1977,"depth":995,"text":1978},{"id":2121,"depth":995,"text":2122},{"id":2255,"depth":989,"text":2256,"children":6205},[6206,6207,6208],{"id":2265,"depth":995,"text":2266},{"id":2334,"depth":995,"text":2335},{"id":2483,"depth":995,"text":2484},{"id":2549,"depth":989,"text":2550,"children":6210},[6211],{"id":2658,"depth":995,"text":2659},{"id":2730,"depth":989,"text":2731,"children":6213},[6214,6215,6216,6217,6218],{"id":2734,"depth":995,"text":2735},{"id":2842,"depth":995,"text":2843},{"id":3231,"depth":995,"text":1978},{"id":3520,"depth":995,"text":3521},{"id":4631,"depth":995,"text":4632},{"id":5243,"depth":989,"text":1910},{"id":5305,"depth":989,"text":5306,"children":6221},[6222,6223,6224,6225],{"id":5318,"depth":995,"text":25},{"id":5381,"depth":995,"text":5382},{"id":5420,"depth":995,"text":5421},{"id":5477,"depth":995,"text":5478},{"id":5521,"depth":989,"text":2821},{"id":5696,"depth":989,"text":5697,"children":6228},[6229,6230,6231,6232],{"id":5715,"depth":995,"text":5716},{"id":5848,"depth":995,"text":5849},{"id":5977,"depth":995,"text":5978},{"id":6074,"depth":995,"text":6075},"Signed temporary JWT links for adaptive MFA, password reset, email update, and custom flows -- how they are created, verified, rate-limited, and consumed in the IAM service.","md","i-lucide-link",{},null,"---\ntitle: Magic Links\ndescription: Signed temporary JWT links for adaptive MFA, password reset, email update, and custom flows -- how they are created, verified, rate-limited, and consumed in the IAM service.\nicon: i-lucide-link\n---\n\nMagic links are short-lived, single-use JWTs embedded as URL query parameters in emails sent to the user. The IAM service uses them for four built-in flows: **adaptive MFA** (triggered by [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies)), **password reset**, **email update**, and **custom MFA** for app-defined sensitive actions. All four flows share the same signing, caching, and verification infrastructure, differing only in the `purpose` value carried in the JWT payload and the route that processes them.\n\nEach magic link carries four query parameters: `visitor`, `token` (the signed JWT), `random` (a SHA-256-hashed challenge), and `reason` (the purpose string). Links expire after a configurable window (default 15 minutes), are cached in a server-side [LRU](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCache_replacement_policies#LRU) store, and are consumed on use so that a verified link cannot be replayed even if the JWT itself has not expired yet.\n\n---\n\n## Signing\n\n### `tempJwtLink`\n\n`tempJwtLink` is the low-level function that creates a magic link JWT. It accepts a typed payload, signs it with [HMAC-SHA512](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHMAC) using a dedicated secret key (`magic_links.jwt_secret_key`), and stores the resulting token in the magic links cache with `valid: true`.\n\n```ts\nimport { tempJwtLink } from '@riavzon\u002Fauth'\n\nconst token = tempJwtLink\u003C{ customField: string }>({\n  visitor: user.visitor_id,\n  subject: String(user.id),\n  purpose: 'PASSWORD_RESET',\n  jti: crypto.randomUUID(),\n  customField: 'value',\n})\n```\n\nThe JWT is signed with `HS512` and includes standard claims (`iss`, `sub`, `aud`, `jti`, `exp`). The expiry is pulled from `magic_links.expiresIn` (default `'15m'`).\n\n### Payload fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `visitor` | `string` | The `visitor_id` foreign key on the user record, used to bind the link to a specific device context |\n| `subject` | `string` | User ID that identifies the recipient |\n| `purpose` | `string` | Identifies the flow: `'PASSWORD_RESET'`, `'MAGIC_LINK_MFA_CHECKS'`, `'change_email'`, or a custom string |\n| `jti` | `string` | Unique JWT ID, used for replay prevention |\n\nThe type parameter lets you extend the payload with additional fields. Any extra properties are encoded into the JWT and available after verification.\n\n### JTI generation\n\nThe `jti` is constructed differently depending on the flow:\n\nAll flows use the same construction: `crypto.randomUUID()` concatenated with `crypto.randomBytes(64).toString('hex')`, producing a 164-character string (36-char UUID + 128 hex chars from 64 bytes).\n\n---\n\n## Verification\n\n### `verifyTempJwtLink`\n\n`verifyTempJwtLink` validates a magic link token through three checks:\n\n1. **Cache lookup** - the token must exist in the LRU cache and have `valid: true`\n2. **JWT verification** - signature, expiry, issuer, subject, audience, and `jti` are all validated against the magic link secret\n3. **Visitor match** - the `visitor` claim in the JWT is compared against the visitor context of the current request\n\n```ts\nimport { verifyTempJwtLink } from '@riavzon\u002Fauth'\n\nconst result = verifyTempJwtLink\u003C{ customField?: string }>(rawToken)\n\nif (!result.valid) {\n  \u002F\u002F result includes the failure reason\n  return\n}\n\nconst { subject, purpose, customField } = result.payload\n```\n\nIf any check fails, the function returns `{ valid: false }` with a descriptive error. The cache entry is not removed on a failed verification attempt, allowing the user to retry (subject to [usage limits](#usage-limits)).\n\n::warning\nUse a separate `jwt_secret_key` for magic links. Do not reuse the access-token or refresh-token secret. This limits the risk if one secret is compromised.\n::\n\n---\n\n## Cache\n\nThe cache is a singleton [LRU](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCache_replacement_policies#LRU) store keyed by the raw JWT string. Each entry stores a `valid` boolean and is automatically evicted after the configured TTL (`magic_links.expiresInMs`, default 15 minutes).\n\nWhen a magic link is created, the token is inserted with `valid: true`. During verification, the cache is checked first. If the entry is missing (expired or evicted) or `valid` is `false`, verification fails immediately without attempting JWT decoding.\n\n```ts\nimport { magicLinksCache } from '@riavzon\u002Fauth'\n\nconst cache = magicLinksCache()\ncache.size        \u002F\u002F Current number of cached tokens\ncache.delete(jti) \u002F\u002F Force-expire a specific link (testing only)\n```\n\nConfigure the cache size with `magic_links.maxCacheEntries` (default 500). The LRU eviction policy ensures the oldest unused entries are dropped first when the cache is full.\n\n---\n\n## URL construction\n\nAll magic link emails build the destination URL from three config values:\n\n- `magic_links.domain`, the base URL\n- `magic_links.paths.pathFor*`, the path segment\n- Query parameters: `visitor`, `token`, `random`, `reason`\n\nThe `random` parameter is `crypto.randomBytes(128).toString('hex')`, a 256-character hex string. The SHA-256 digest of that string is embedded in the signed JWT payload as `randomHashed`. On verification the middleware hashes the incoming `random` and compares it against `randomHashed` with `crypto.timingSafeEqual` to prevent timing attacks.\n\n```\nhttps:\u002F\u002Fexample.com\u002Fauth\u002Fbounce?visitor=42&token=eyJ...&random=a3f8...&reason=MAGIC_LINK_MFA_CHECKS\n```\n\nThe bounce path is a thin redirect handler that forwards all query parameters to the application's verification page (default `\u002Fauth\u002Fverify`). This indirection lets you host the auth service on a different domain from your application. See [auth-h3client](\u002Fdocs\u002Fiam\u002Fauth-h3client) for how the client handles the redirect.\n\n---\n\n## Built-in flows\n\nThe service ships with three built-in flows. Each one uses the same signing, caching, and verification process described above.\n\n### Adaptive MFA\n\nAdaptive MFA is triggered automatically when [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies) returns `reqMFA: true`. This happens inside `protectRoute` (during access-token verification) and during token rotation (inside `rotateOnEveryUse`). The user is not required to initiate this flow; the service detects the anomaly and sends the email automatically.\n\nSee [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) for the full adaptive MFA lifecycle, OTP code generation and verification, and the post-MFA bypass window.\n\n#### Trigger points\n\n| Caller | When | Purpose |\n|---|---|---|\n| `protectRoute` | Every authenticated request | Anomaly detected during routine access-token check |\n| `rotateOnEveryUse` | Token rotation (`POST \u002Fauth\u002Fuser\u002Frefresh-session`) | Anomaly detected during rotation |\n\n#### Sending the link\n\n`sendTempMfaLink` handles the full email delivery pipeline:\n\n::steps{level=\"4\"}\n#### Rate limiting\n\nThree rate limiters run in sequence before the email is sent:\n\n| Limiter | Key | Points | Window | Block |\n|---|---|---|---|---|\n| Global | `'global_emails'` | 800 | 24 h | 24 h |\n| Per-user | `'user_{userId}'` | 8 | 24 h | 12 h |\n| Per-IP | `{ip}` | 5 | 24 h | 4 h |\n\nIf any limiter rejects the request, the function returns early and the email is not sent.\n\n#### JTI and random generation\n\nThe function generates a JTI (`crypto.randomUUID()` + `crypto.randomBytes(64)`) and a random challenge (`crypto.randomBytes(128)`), then hashes the random with SHA-256.\n\n#### JWT signing\n\n`tempJwtLink` is called with `purpose: 'MAGIC_LINK_MFA_CHECKS'` and the generated JTI.\n\n#### OTP code generation\n\n`generateMfaCode` creates a 7-digit numeric code, hashes it with SHA-256, and stores the hash in the database. See [MFA - OTP generation](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#otp-generation-and-verification) for the full details.\n\n#### Email delivery\n\nThe link URL and OTP code are rendered into an [EJS](https:\u002F\u002Fejs.co\u002F) template and sent via the [Resend](https:\u002F\u002Fresend.com\u002F) SDK. The email includes device metadata (browser, OS, location) so the user can recognize unexpected requests.\n::\n\n#### Routes\n\n| Method | Path | Description |\n|---|---|---|\n| `GET` | `\u002Fauth\u002Fverify-mfa` | Validates the signed link token from the email |\n| `POST` | `\u002Fauth\u002Fverify-mfa` | Submits the 7-digit OTP code and completes verification |\n\n### Password reset\n\nThe password reset flow is the only built-in flow that does **not** generate an OTP code. The user proves identity by clicking the email link alone, then submits a new password directly.\n\n::steps{level=\"4\"}\n#### User requests a reset\n\nThe user submits their email to `POST \u002Fauth\u002Fforgot-password`. The controller looks up the account and rejects [OAuth](\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth)-only users (those with `password_hash = 'no_password'`). A signed link is generated with `purpose: 'PASSWORD_RESET'` and sent via email.\n\nThe controller enforces a **minimum 3-second response time** regardless of whether the email exists. Combined with always returning `200`, this prevents [timing-based user enumeration](https:\u002F\u002Fowasp.org\u002Fwww-project-web-security-testing-guide\u002Flatest\u002F4-Web_Application_Security_Testing\u002F03-Identity_Management_Testing\u002F04-Testing_for_Account_Enumeration_and_Guessable_User_Account).\n\n#### User clicks the link\n\nThe email link targets the bounce handler, which redirects to the application's reset-password page. The page calls `GET \u002Fauth\u002Freset-password` with the link's query parameters. The `linkPasswordVerification` middleware validates the token, visitor match, and random challenge.\n\n#### User submits a new password\n\n`POST \u002Fauth\u002Freset-password` receives `{ password, confirmedPassword }`, verifies the magic link token again, hashes the new password with [Argon2id](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FArgon2), and updates the user record. The link is consumed and cannot be reused.\n::\n\n#### Routes\n\n| Method | Path | Description |\n|---|---|---|\n| `POST` | `\u002Fauth\u002Fforgot-password` | Initiates the reset by sending the email |\n| `GET` | `\u002Fauth\u002Freset-password` | Validates the magic link token |\n| `POST` | `\u002Fauth\u002Freset-password` | Submits the new password |\n\n### Email update\n\nThe email update flow uses the custom MFA process with the reserved purpose `'change_email'`. It requires the user to be authenticated, pass a custom MFA challenge, and confirm their current password before the email is changed.\n\n::steps{level=\"4\"}\n#### User initiates the change\n\nThe authenticated user calls `POST \u002Fupdate\u002Femail`. The controller generates a random challenge and calls `generateCustomMfaFlow` with `reason: 'change_email'`. A magic link and OTP code are sent to the user's **current** email address.\n\n#### User clicks the link\n\nThe email link targets the bounce handler, which redirects to the application's email-update page. The page calls `GET \u002Fauth\u002Fverify-custom-mfa` with the link's query parameters. The `customMfaFlowsVerification` middleware validates the token.\n\n#### User submits the new email and code\n\n`POST \u002Fupdate\u002Femail` receives `{ code, email, newEmail, password }`. The controller verifies that the magic link purpose is `'change_email'`, confirms the current password with [Argon2](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FArgon2), and delegates to `verifyMfaCode` with `revokeAllTokensOnSuccess: true`. On success, the email is updated and all existing sessions are revoked, forcing the user to log in again with the new email.\n::\n\n#### Routes\n\n| Method | Path | Description |\n|---|---|---|\n| `POST` | `\u002Fcustom\u002Fmfa\u002Fchange_email` | Initiates the flow (sends magic link + OTP) |\n| `GET` | `\u002Fauth\u002Fverify-custom-mfa` | Validates the magic link token |\n| `POST` | `\u002Fupdate\u002Femail` | Submits the new email, OTP code, and current password |\n\n---\n\n## Custom MFA flows\n\nCustom MFA flows let you protect any sensitive action behind a second-factor email check. The user must be authenticated to initiate a custom flow. You define the `reason` string, and the service handles link generation, OTP creation, email delivery, and verification.\n\n### Reserved reasons\n\nThe following reason strings are reserved and cannot be used for custom flows:\n\n| Reserved string | Used by |\n|---|---|\n| `MAGIC_LINK_MFA_CHECKS` | Adaptive MFA |\n| `PASSWORD_RESET` | Password reset |\n| `PASSWORD_RESET_FLOW` | Internal |\n| `EMAIL_MFA_FLOW` | Internal |\n\nAny other string is valid as a custom reason (e.g. `'payment'`, `'account-delete'`, `'transfer-funds'`).\n\n### Initiating a custom flow\n\n`POST \u002Fcustom\u002Fmfa\u002F:reason` initiates the flow. The controller requires the request to originate from a trusted IP (either the client IP or the proxy's `ipToTrust`). The `random` query parameter must be a high-entropy string between 254 and 500 characters.\n\n```ts\nimport { generateCustomMfaFlow } from '@riavzon\u002Fauth'\n\nawait generateCustomMfaFlow(random, 'payment', user, sessionToken, ip, res, {\n  device: 'Chrome on Windows',\n  browser: 'Chrome 125',\n  location: 'Berlin, DE',\n})\n```\n\nThe function generates a JTI, signs a temporary link with `purpose` set to the provided reason, generates a 7-digit OTP code, and sends the email. The same triple rate limiting described in [Adaptive MFA](#adaptive-mfa) applies.\n\n### Verifying a custom flow\n\nAfter the user clicks the email link:\n\n| Method | Path | Description |\n|---|---|---|\n| `GET` | `\u002Fauth\u002Fverify-custom-mfa` | Validates the link token via `customMfaFlowsVerification` middleware |\n| `POST` | `\u002Fauth\u002Fverify-custom-mfa` | Submits the OTP code and completes verification |\n\nOn successful POST verification, `verifyMfaCode` runs with `returnMetaData: true`, making limited user metadata available to the calling controller via the response. See [MFA - OTP verification](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#otp-generation-and-verification) for the full verification sequence.\n\n---\n\n## Verification middleware\n\nThree middleware functions handle magic link verification on the server side. Each one follows the same pattern:\n\n1. **Zod validation** of the query parameters (`visitor`, `token`, `random`, `reason`)\n2. **Rate limiting** keyed on the hashed token\n3. **JWT verification** via `verifyTempJwtLink`\n4. **Visitor match** - the `visitor` claim is compared with the current request context\n5. **Timing-safe hash comparison** of the `random` parameter against the stored challenge\n\n| Middleware | Purpose | Used by |\n|---|---|---|\n| `linkMfaVerification` | Adaptive MFA links | `GET\u002FPOST \u002Fauth\u002Fverify-mfa` |\n| `linkPasswordVerification` | Password reset links | `GET\u002FPOST \u002Fauth\u002Freset-password` |\n| `customMfaFlowsVerification` | Custom MFA and email update links | `GET\u002FPOST \u002Fauth\u002Fverify-custom-mfa` |\n\n### Usage limits\n\nEach middleware tracks how many times a link has been used for GET and POST requests separately. If the limit is exceeded, the link is invalidated regardless of its expiry.\n\n| Request type | Default limit | Config path |\n|---|---|---|\n| GET | 5 | `magic_links.thresholds.*.allowedPerSuccessfulGet` |\n| POST | 3 | `magic_links.thresholds.*.allowedPerSuccessfulPost` |\n\nThe threshold config keys correspond to each flow:\n\n- `magic_links.thresholds.adaptiveMfa` - adaptive MFA links\n- `magic_links.thresholds.linkPasswordVerification` - password reset links\n- `magic_links.thresholds.customMfaFlowsAndEmailChanges` - custom MFA and email update links\n\n---\n\n## Examples\n\n### Mounting the router\n\nThe `magicLinks` export is a pre-built router with all nine routes already wired. Mount it on your app with:\n\n```ts\nimport express from 'express'\nimport { magicLinks } from '@riavzon\u002Fauth'\n\nconst app = express()\napp.use(magicLinks)\n```\n\nThis registers every route listed in [Routes reference](#routes-reference), including the full middleware chains for rate limiting, Zod validation, link verification, and bot detection. No additional setup is required.\n\n::tip\nIf you use `bootstrapApp()` from `@riavzon\u002Fauth\u002Fservice`, the magic links router is mounted automatically and you do not need to call `app.use(magicLinks)`.\n::\n\n### Triggering adaptive MFA from a custom handler\n\n`sendTempMfaLink` is the function that `protectRoute` calls internally when anomaly detection returns `reqMFA: true`. You can call it directly from any handler to force an MFA challenge:\n\n```ts\nimport { sendTempMfaLink } from '@riavzon\u002Fauth'\n\nasync function myProtectedHandler(req, res) {\n  const result = await sendTempMfaLink(\n    { userId: Number(req.user.userId), visitor: req.user.visitor_id },\n    req.cookies.session,       \u002F\u002F raw refresh token\n    req.ip!,\n    res,\n    {\n      device: 'Chrome on macOS',\n      browser: 'Chrome 125',\n      location: 'Paris, FR',\n    }\n  )\n\n  if (result === 'rate_limited') {\n    return res.status(429).json({ error: 'Too many MFA requests. Try again later.' })\n  }\n\n  \u002F\u002F The email has been sent. Respond with 202 so the client knows to prompt for MFA.\n  return res.status(202).json({ message: 'Please check your email for a verification link.' })\n}\n```\n\nThe `user` parameter expects `{ userId: number, visitor: string }`. The `meta` parameter expects `{ device: string, browser: string, location: string }`, which is rendered into the email template so the user can recognize the request.\n\nThe function applies three rate limiters (global, per-user, per-IP), generates the JTI and random challenge, signs the JWT, creates the 7-digit OTP code, and sends the email. It returns `true` on success or `'rate_limited'` if any limiter rejects.\n\n### Password reset\n\n`sendTempPasswordResetLink` handles the full password-reset email pipeline. The  `POST \u002Fauth\u002Fforgot-password` route, calls this internally, but you can use it from a custom controller:\n\n```ts\nimport { sendTempPasswordResetLink } from '@riavzon\u002Fauth'\n\nasync function customForgotPassword(req, res) {\n  const startTime = Date.now()\n\n  const { valid, error } = await sendTempPasswordResetLink(req.body.email)\n\n  \u002F\u002F Enforce a minimum 3-second response time to prevent timing-based enumeration.\n  const elapsed = Date.now() - startTime\n  if (elapsed \u003C 3000) {\n    await new Promise(resolve => setTimeout(resolve, 3000 - elapsed))\n  }\n\n  \u002F\u002F Always return success regardless of whether the email exists.\n  return res.status(200).json({\n    ok: true,\n    message: 'If that email is registered, a reset link has been sent.',\n  })\n}\n```\n\nThe function looks up the user, rejects OAuth accounts (no password to reset), generates a signed link with `purpose: 'PASSWORD_RESET'`, and sends the email. It does **not** generate an OTP code.\n\n### Building a custom MFA-protected action\n\nTo protect a sensitive action behind a custom MFA flow, wire three routes: one to initiate the flow, one to verify the magic link (GET), and one to verify the OTP code and execute the action (POST).\n\n```ts\nimport express from 'express'\nimport crypto from 'crypto'\nimport {\n  validateContentType,\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  customMfaFlowsVerification,\n  generateCustomMfaFlow,\n  verifyCustomMfa,\n} from '@riavzon\u002Fauth'\nimport { detectBots } from '@riavzon\u002Fbot-detector'\n\nconst router = express.Router()\n\n\u002F\u002F Step 1: Initiate the custom MFA flow\nrouter.post('\u002Fdanger-zone\u002Finitiate',\n  validateContentType('application\u002Fjson'),\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  express.json({ limit: '1kb' }),\n  async (req, res) => {\n    const random = crypto.randomBytes(128).toString('hex')\n\n    const result = await generateCustomMfaFlow(\n      random,\n      'account-delete',       \u002F\u002F your custom reason string\n      { userId: Number(req.user!.userId), visitor: req.user!.visitor_id },\n      req.cookies.session,    \u002F\u002F raw refresh token\n      req.ip!,\n      res,\n      {\n        device: req.headers['user-agent'] ?? 'Unknown Device',\n        browser: 'Unknown Browser',\n        location: 'Unknown Location',\n      }\n    )\n\n    if (!result.ok && result.data === 'rate_limited') return\n    if (!result.ok && result.data === 'exists') {\n      return res.status(400).json({ error: 'This reason is already reserved.' })\n    }\n\n    return res.status(200).json(result)\n  }\n)\n\n\u002F\u002F Step 2: Verify the magic link (GET)\n\u002F\u002F customMfaFlowsVerification sends the response on GET automatically.\nrouter.get('\u002Fdanger-zone\u002Fverify',\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  customMfaFlowsVerification\n)\n\n\u002F\u002F Step 3: Verify the OTP code and execute the action (POST)\nrouter.post('\u002Fdanger-zone\u002Fverify',\n  validateContentType('application\u002Fjson'),\n  requireAccessToken,\n  requireRefreshToken,\n  getFingerPrint,\n  protectRoute,\n  express.json({ limit: '1kb' }),\n  customMfaFlowsVerification,\n  detectBots,\n  async (req, res) => {\n    \u002F\u002F MFA verified. req.link is populated with the decoded payload.\n    const { purpose, subject } = req.link\n\n    \u002F\u002F Execute the protected action\n    await deleteUserAccount(Number(req.user!.userId))\n\n    return res.json({ ok: true, message: 'Account deleted.' })\n  }\n)\n```\n\nAfter `customMfaFlowsVerification` passes on a POST request, `req.link` is populated with the decoded JWT payload:\n\n```ts\nreq.link = {\n  visitor: string,      \u002F\u002F visitor_id\n  subject: string,      \u002F\u002F `${reason}_${visitor_id}`\n  purpose: string,      \u002F\u002F the reason string ('account-delete' in this example)\n  jti?: string,         \u002F\u002F unique link identifier (optional)\n}\n```\n\n### Manual signing and verification\n\nFor full control over the JWT lifecycle, use `signNewTempLink` and `verifyTempLink` directly. These are the low-level primitives that all built-in flows call internally.\n\n```ts\nimport { signNewTempLink, verifyTempLink, magicLinksCache } from '@riavzon\u002Fauth'\nimport crypto from 'crypto'\n\n\u002F\u002F --- Sign ---\nconst token = signNewTempLink\u003C{ transferId: string }>({\n  visitor: user.visitor_id,        \u002F\u002F string\n  subject: String(user.userId),    \u002F\u002F string\n  purpose: 'wire-transfer',\n  jti: crypto.randomUUID(),\n  transferId: 'txn_abc123',\n})\n\n\u002F\u002F Build the email URL\nconst url = new URL('\u002Fauth\u002Fbounce', config.magic_links.domain)\nurl.searchParams.set('visitor', user.visitor_id)\nurl.searchParams.set('token', token)\nurl.searchParams.set('random', crypto.createHash('sha256')\n  .update(crypto.randomBytes(128).toString('hex'))\n  .digest('hex'))\nurl.searchParams.set('reason', 'wire-transfer')\n\n\u002F\u002F Send url.toString() via your email provider\n\n\u002F\u002F --- Verify ---\nconst result = verifyTempLink\u003C{ transferId: string }>(token)\n\nif (!result.valid) {\n  console.error('Link invalid:', result.errorType)\n  return\n}\n\nconst { subject, purpose, transferId } = result.payload!\n\u002F\u002F purpose === 'wire-transfer', transferId === 'txn_abc123'\n\n\u002F\u002F --- Invalidate ---\nconst cache = magicLinksCache()\ncache.delete(token)  \u002F\u002F Mark the link as consumed\n```\n\n::warning\nWhen signing manually, you are responsible for rate limiting, visitor matching, and link consumption. The built-in flows and middleware handle all of this automatically. Prefer using `sendTempMfaLink`, `generateCustomMfaFlow`, or the pre-built `magicLinks` router unless you have a specific reason to sign tokens directly.\n::\n\n::tip\nThe [auth-h3client](\u002Fdocs\u002Fiam\u002Fauth-h3client) package provides a full client-side implementation for all magic link flows. It handles bounce redirection, route registration, composable state management, and 202 MFA response propagation. See the [auth-h3client documentation](\u002Fdocs\u002Fiam\u002Fauth-h3client) for usage details.\n::\n\n---\n\n## Email delivery\n\nMagic link emails are sent via the [Resend](https:\u002F\u002Fresend.com\u002F) SDK. The service uses [EJS](https:\u002F\u002Fejs.co\u002F) templates to render two email types:\n\n| Template | Used by | Contains |\n|---|---|---|\n| OTP template | Adaptive MFA, custom MFA, email update | Magic link button, 7-digit OTP code, device\u002Fbrowser\u002Flocation metadata |\n| Password reset template | Password reset | Magic link button, device\u002Fbrowser\u002Flocation metadata (no OTP code) |\n\nBoth templates include the website name, privacy policy link, contact page link, and email images (OTP banner). These values are configured in `magic_links.notificationEmail` and `magic_links.emailImages`.\n\n---\n\n## Configuration reference\n\nAll options live under the `magic_links` key in the auth service config. Overridable via `config.magic_links.*`.\n\n### Core\n\n::field-group\n::field{name=\"jwt_secret_key\" type=\"string\" required}\n  The HMAC-SHA512 signing key for magic link JWTs. Must be separate from the access-token and refresh-token secrets.\n::\n\n::field{name=\"expiresIn\" type=\"string\"}\n  Default `'15m'` - JWT lifetime in [ms-compatible](https:\u002F\u002Fgithub.com\u002Fvercel\u002Fms) string format.\n::\n\n::field{name=\"expiresInMs\" type=\"number\"}\n  Default `900_000` - Same lifetime in milliseconds, used for the LRU cache TTL. Must match `expiresIn`.\n::\n\n::field{name=\"domain\" type=\"string\" required}\n  Base URL for link construction (e.g. `'https:\u002F\u002Fexample.com'`). Prepended to every path.\n::\n\n::field{name=\"maxCacheEntries\" type=\"number\"}\n  Default `500` - Maximum number of active magic link tokens held in the LRU cache.\n::\n\n::field{name=\"linkToResetPasswordPage\" type=\"string\"}\n  The URL of your app's \"reset your password\" page, included in the password-reset email body.\n::\n::\n\n### Paths\n\nPath segments appended to `domain` when building email link URLs. All default to `'\u002Fauth\u002Fbounce'`.\n\n::field-group\n::field{name=\"paths.pathForPasswordResetLink\" type=\"string\"}\n  Default `'\u002Fauth\u002Fbounce'` - Email link target for password reset flows.\n::\n\n::field{name=\"paths.pathForAdaptiveMfaLink\" type=\"string\"}\n  Default `'\u002Fauth\u002Fbounce'` - Email link target for adaptive MFA flows.\n::\n\n::field{name=\"paths.pathForCustomFlow\" type=\"string\"}\n  Default `'\u002Fauth\u002Fbounce'` - Email link target for custom MFA flows and email updates.\n::\n::\n\n### Thresholds\n\nUsage limits per link. Each middleware tracks GET and POST uses separately. When exceeded, the link is invalidated regardless of expiry.\n\n::field-group\n::field{name=\"thresholds.adaptiveMfa.allowedPerSuccessfulGet\" type=\"number\"}\n  Default `5` - Maximum GET verifications allowed per adaptive MFA link.\n::\n\n::field{name=\"thresholds.adaptiveMfa.allowedPerSuccessfulPost\" type=\"number\"}\n  Default `3` - Maximum POST submissions allowed per adaptive MFA link.\n::\n\n::field{name=\"thresholds.linkPasswordVerification.allowedPerSuccessfulGet\" type=\"number\"}\n  Default `5` - Maximum GET verifications allowed per password reset link.\n::\n\n::field{name=\"thresholds.linkPasswordVerification.allowedPerSuccessfulPost\" type=\"number\"}\n  Default `3` - Maximum POST submissions allowed per password reset link.\n::\n\n::field{name=\"thresholds.customMfaFlowsAndEmailChanges.allowedPerSuccessfulGet\" type=\"number\"}\n  Default `5` - Maximum GET verifications allowed per custom MFA or email update link.\n::\n\n::field{name=\"thresholds.customMfaFlowsAndEmailChanges.allowedPerSuccessfulPost\" type=\"number\"}\n  Default `3` - Maximum POST submissions allowed per custom MFA or email update link.\n::\n::\n\n### Email templates\n\n::field-group\n::field{name=\"emailImages.otpBanner\" type=\"string\"}\n  URL of the banner image displayed at the top of OTP emails.\n::\n\n::field{name=\"notificationEmail.websiteName\" type=\"string\"}\n  The display name of your website, shown in all magic link emails.\n::\n\n::field{name=\"notificationEmail.privacyPolicyLink\" type=\"string\"}\n  URL to your privacy policy page, linked in the email footer.\n::\n\n::field{name=\"notificationEmail.contactPageLink\" type=\"string\"}\n  URL to your contact page, linked in the email footer.\n::\n\n::field{name=\"notificationEmail.changePasswordPageLink\" type=\"string\"}\n  URL to your change-password page, linked in notification emails.\n::\n\n::field{name=\"notificationEmail.loginPageLink\" type=\"string\"}\n  URL to your login page, linked in notification emails.\n::\n::\n\n---\n\n## Routes reference\n\nAll routes handled by the magic links router on the auth server:\n\n| Method | Path | Middleware | Description |\n|---|---|---|---|\n| `GET` | `\u002Fauth\u002Fverify-mfa` | `linkMfaVerification` | Validate adaptive MFA link |\n| `POST` | `\u002Fauth\u002Fverify-mfa` | `linkMfaVerification` | Submit OTP code for adaptive MFA |\n| `POST` | `\u002Fcustom\u002Fmfa\u002F:reason` | Auth required, IP restriction | Initiate a custom MFA flow |\n| `GET` | `\u002Fauth\u002Fverify-custom-mfa` | `customMfaFlowsVerification` | Validate custom MFA link |\n| `POST` | `\u002Fauth\u002Fverify-custom-mfa` | `customMfaFlowsVerification` | Submit OTP code for custom MFA |\n| `POST` | `\u002Fupdate\u002Femail` | `customMfaFlowsVerification` | Submit new email with OTP + password |\n| `POST` | `\u002Fauth\u002Fforgot-password` | Rate limiting | Initiate password reset |\n| `GET` | `\u002Fauth\u002Freset-password` | `linkPasswordVerification` | Validate password reset link |\n| `POST` | `\u002Fauth\u002Freset-password` | `linkPasswordVerification` | Submit new password |\n\n---\n\n## Rate limiter reference\n\nMagic link flows apply multiple layers of rate limiting. All values below are defaults, overridable via `config.rate_limiters.*`. See [Rate Limiting](\u002Fdocs\u002Fiam\u002Frate-limiting) for the `guard()` architecture and the consecutive-violation escalation system.\n\n### Email sending (MFA and custom flows)\n\nApplied by `sendTempMfaLink` and `generateCustomMfaFlow` before sending any email.\n\n| Limiter | Key | Points | Window | Block | Config path |\n|---|---|---|---|---|---|\n| Global | `'global_emails'` | 800 | 24 h | 24 h | `rate_limiters.emailMfaLimiters.globalEmailLimiter` |\n| Per-user | `'user_{userId}'` | 8 | 24 h | 12 h | `rate_limiters.emailMfaLimiters.userIdLimiter` |\n| Per-IP | `{ip}` | 5 | 24 h | 4 h | `rate_limiters.emailMfaLimiters.ipLimiter` |\n| Burst | `{ip}_{random}_{reason}` | 1 | 1 s | 30 min | `rate_limiters.emailMfaLimiters.unionLimiter` |\n| Slow | `{ip}_{random}_{reason}` | 4 | 30 min | 15 min | `rate_limiters.emailMfaLimiters.unionLimiter` |\n\n### Password reset initiation\n\nApplied by the `initPasswordReset` controller on `POST \u002Fauth\u002Fforgot-password`.\n\n| Limiter | Key | Points | Window | Block | Config path |\n|---|---|---|---|---|---|\n| Global | `'global_emails'` | 800 | 24 h | 24 h | `rate_limiters.emailMfaLimiters.globalEmailLimiter` |\n| Per-IP | `{ip}` | 5 | 24 h | 4 h | `rate_limiters.initPasswordResetLimiters.ipLimiter` |\n| Per-email | `{email}` | 5 | 24 h | 4 h | `rate_limiters.initPasswordResetLimiters.emailLimiter` |\n| Burst | `{ip}_{email}` | 1 | 1 s | 30 min | `rate_limiters.initPasswordResetLimiters.unionLimiter` |\n| Slow | `{ip}_{email}` | 4 | 30 min | 15 min | `rate_limiters.initPasswordResetLimiters.unionLimiter` |\n\n### Link verification (all three middleware functions)\n\nApplied by `linkMfaVerification`, `linkPasswordVerification`, and `customMfaFlowsVerification` on every GET and POST.\n\n| Limiter | Key | Points | Window | Block | Config path |\n|---|---|---|---|---|---|\n| Burst | `{ip}` | 2 | 1 s | 15 min | `rate_limiters.linkVerificationLimiter.unionLimiter` |\n| Slow | `{ip}` | 30 | 30 min | 30 min | `rate_limiters.linkVerificationLimiter.unionLimiter` |\n| JTI single-use | `{jti}` | 0 | 0 s | 20 min | Internal (not configurable) |\n\nThe JTI limiter is a block-only limiter. After a link is consumed, its JTI is blocked for 20 minutes to prevent replay.\n\n### OTP code submission\n\nApplied by `verifyMfaCode` when processing a POST with a 7-digit code (adaptive MFA, custom MFA, and email update).\n\n| Limiter | Key | Points | Window | Block | Config path |\n|---|---|---|---|---|---|\n| Burst | `{ip}` | 1 | 1 s | 30 min | `rate_limiters.tempPostRoutesLimiter.unionLimiter` |\n| Slow | `{ip}` | 5 | 10 min | 10 min | `rate_limiters.tempPostRoutesLimiter.unionLimiter` |\n| Per-JTI | `{jti}` | 1 | 1 s | 30 min | `rate_limiters.tempPostRoutesLimiter.unionLimiter` |\n| Per-IP (code hash) | `{codeHash}` | 6 | 10 min | 10 min | `rate_limiters.tempPostRoutesLimiter.ipLimit` |\n\nAfter successful verification, the submitted code hash is blocked for 10 minutes and the JTI is blocked for 20 minutes. This prevents reuse of the same code or link.\n",{"title":115,"description":6233},"5jU9HB9jtVED6Z4w_zvxbhrDugKAUUciStt3OaX6bOk",[6242,6243],{"title":111,"path":112,"stem":113,"children":-1},{"title":119,"path":120,"stem":121,"children":-1},{"id":851,"title":115,"body":6245,"description":6233,"extension":6234,"icon":6235,"meta":10621,"module":6237,"navigation":8,"path":116,"rawbody":6238,"seo":10622,"stem":117,"__hash__":6240},{"type":853,"value":6246,"toc":10575},[6247,6261,6274,6276,6278,6282,6293,6417,6435,6437,6507,6509,6511,6515,6521,6523,6525,6529,6533,6553,6665,6671,6677,6679,6681,6690,6698,6762,6766,6768,6770,6772,6792,6806,6811,6817,6819,6821,6823,6825,6835,6839,6841,6877,6879,6883,6987,6989,7027,7029,7033,7075,7077,7127,7129,7133,7172,7174,7224,7226,7228,7232,7234,7236,7280,7288,7290,7298,7406,7412,7414,7416,7456,7464,7466,7468,7470,7506,7556,7558,7560,7594,7596,7610,7612,7614,7616,7620,7686,7690,7700,7702,7710,7998,8008,8014,8016,8022,8256,8262,8264,8266,9148,9154,9222,9224,9230,9764,9774,9782,9784,9786,9794,9824,9830,9832,9834,9840,9842,9881,9883,9889,9909,9911,9913,9951,9953,9979,9981,9983,9985,10141,10143,10145,10153,10155,10161,10271,10273,10279,10389,10391,10399,10471,10473,10475,10479,10571,10573],[856,6248,858,6249,863,6251,868,6253,872,6255,876,6257,880,6259,885],{},[860,6250,862],{},[865,6252,867],{"href":96},[860,6254,871],{},[860,6256,875],{},[860,6258,879],{},[882,6260,884],{},[856,6262,888,6263,872,6265,895,6267,899,6269,903,6271,910],{},[882,6264,891],{},[882,6266,894],{},[882,6268,898],{},[882,6270,902],{},[865,6272,909],{"href":906,"rel":6273},[908],[912,6275],{},[915,6277,918],{"id":917},[920,6279,6280],{"id":922},[882,6281,925],{},[856,6283,6284,930,6286,936,6289,940,6291,944],{},[882,6285,925],{},[865,6287,935],{"href":933,"rel":6288},[908],[882,6290,939],{},[882,6292,943],{},[946,6294,6295],{"className":948,"code":949,"language":950,"meta":951,"style":951},[882,6296,6297,6315,6319,6339,6353,6371,6385,6399,6413],{"__ignoreMap":951},[955,6298,6299,6301,6303,6305,6307,6309,6311,6313],{"class":957,"line":958},[955,6300,962],{"class":961},[955,6302,966],{"class":965},[955,6304,925],{"class":969},[955,6306,972],{"class":965},[955,6308,975],{"class":961},[955,6310,979],{"class":978},[955,6312,983],{"class":982},[955,6314,986],{"class":978},[955,6316,6317],{"class":957,"line":989},[955,6318,992],{"emptyLinePlaceholder":8},[955,6320,6321,6323,6325,6327,6329,6331,6333,6335,6337],{"class":957,"line":995},[955,6322,999],{"class":998},[955,6324,1003],{"class":1002},[955,6326,1007],{"class":1006},[955,6328,1011],{"class":1010},[955,6330,1014],{"class":965},[955,6332,1017],{"class":969},[955,6334,1020],{"class":1006},[955,6336,1024],{"class":1023},[955,6338,1027],{"class":965},[955,6340,6341,6343,6345,6347,6349,6351],{"class":957,"line":1030},[955,6342,1033],{"class":969},[955,6344,1020],{"class":1036},[955,6346,1039],{"class":969},[955,6348,944],{"class":965},[955,6350,1044],{"class":969},[955,6352,1047],{"class":965},[955,6354,6355,6357,6359,6361,6363,6365,6367,6369],{"class":957,"line":1050},[955,6356,1053],{"class":969},[955,6358,1020],{"class":1036},[955,6360,1058],{"class":1010},[955,6362,1061],{"class":965},[955,6364,1064],{"class":969},[955,6366,944],{"class":965},[955,6368,1069],{"class":969},[955,6370,1072],{"class":965},[955,6372,6373,6375,6377,6379,6381,6383],{"class":957,"line":1075},[955,6374,1078],{"class":969},[955,6376,1020],{"class":1036},[955,6378,979],{"class":978},[955,6380,1085],{"class":982},[955,6382,1088],{"class":978},[955,6384,1047],{"class":965},[955,6386,6387,6389,6391,6393,6395,6397],{"class":957,"line":1093},[955,6388,1096],{"class":969},[955,6390,1020],{"class":1036},[955,6392,1101],{"class":969},[955,6394,944],{"class":965},[955,6396,1106],{"class":1010},[955,6398,1109],{"class":965},[955,6400,6401,6403,6405,6407,6409,6411],{"class":957,"line":1112},[955,6402,1115],{"class":969},[955,6404,1020],{"class":1036},[955,6406,979],{"class":978},[955,6408,1122],{"class":982},[955,6410,1088],{"class":978},[955,6412,1047],{"class":965},[955,6414,6415],{"class":957,"line":1129},[955,6416,1132],{"class":965},[856,6418,1135,6419,1139,6421,872,6423,872,6425,872,6427,872,6429,1155,6431,1159,6433,1163],{},[882,6420,1138],{},[882,6422,1142],{},[882,6424,1145],{},[882,6426,1148],{},[882,6428,1151],{},[882,6430,1154],{},[882,6432,1158],{},[882,6434,1162],{},[920,6436,1167],{"id":1166},[1169,6438,6439,6449],{},[1172,6440,6441],{},[1175,6442,6443,6445,6447],{},[1178,6444,1180],{},[1178,6446,1183],{},[1178,6448,1186],{},[1188,6450,6451,6465,6477,6495],{},[1175,6452,6453,6457,6461],{},[1193,6454,6455],{},[882,6456,891],{},[1193,6458,6459],{},[882,6460,1201],{},[1193,6462,1204,6463,1207],{},[882,6464,1044],{},[1175,6466,6467,6471,6475],{},[1193,6468,6469],{},[882,6470,1214],{},[1193,6472,6473],{},[882,6474,1201],{},[1193,6476,1221],{},[1175,6478,6479,6483,6487],{},[1193,6480,6481],{},[882,6482,884],{},[1193,6484,6485],{},[882,6486,1201],{},[1193,6488,1234,6489,872,6491,872,6493,1244],{},[882,6490,1237],{},[882,6492,1240],{},[882,6494,1243],{},[1175,6496,6497,6501,6505],{},[1193,6498,6499],{},[882,6500,1151],{},[1193,6502,6503],{},[882,6504,1201],{},[1193,6506,1257],{},[856,6508,1260],{},[920,6510,1264],{"id":1263},[856,6512,1204,6513,1269],{},[882,6514,1151],{},[856,6516,1272,6517,1276,6519,1280],{},[882,6518,1275],{},[882,6520,1279],{},[912,6522],{},[915,6524,1286],{"id":1285},[920,6526,6527],{"id":1289},[882,6528,1292],{},[856,6530,6531,1297],{},[882,6532,1292],{},[1299,6534,6535,6541,6547],{},[1302,6536,6537,1307,6539],{},[860,6538,1306],{},[882,6540,943],{},[1302,6542,6543,1315,6545,1318],{},[860,6544,1314],{},[882,6546,1151],{},[1302,6548,6549,1324,6551,1327],{},[860,6550,1323],{},[882,6552,891],{},[946,6554,6555],{"className":948,"code":1330,"language":950,"meta":951,"style":951},[882,6556,6557,6575,6579,6603,6607,6623,6627,6631,6635,6639],{"__ignoreMap":951},[955,6558,6559,6561,6563,6565,6567,6569,6571,6573],{"class":957,"line":958},[955,6560,962],{"class":961},[955,6562,966],{"class":965},[955,6564,1292],{"class":969},[955,6566,972],{"class":965},[955,6568,975],{"class":961},[955,6570,979],{"class":978},[955,6572,983],{"class":982},[955,6574,986],{"class":978},[955,6576,6577],{"class":957,"line":989},[955,6578,992],{"emptyLinePlaceholder":8},[955,6580,6581,6583,6585,6587,6589,6591,6593,6595,6597,6599,6601],{"class":957,"line":995},[955,6582,999],{"class":998},[955,6584,1361],{"class":1002},[955,6586,1007],{"class":1006},[955,6588,1366],{"class":1010},[955,6590,1014],{"class":965},[955,6592,1017],{"class":969},[955,6594,1373],{"class":1006},[955,6596,1024],{"class":1023},[955,6598,1378],{"class":965},[955,6600,1381],{"class":969},[955,6602,1384],{"class":965},[955,6604,6605],{"class":957,"line":1030},[955,6606,992],{"emptyLinePlaceholder":8},[955,6608,6609,6611,6613,6615,6617,6619,6621],{"class":957,"line":1050},[955,6610,1393],{"class":961},[955,6612,1396],{"class":965},[955,6614,1399],{"class":1006},[955,6616,1402],{"class":969},[955,6618,944],{"class":965},[955,6620,1407],{"class":969},[955,6622,1410],{"class":965},[955,6624,6625],{"class":957,"line":1075},[955,6626,1416],{"class":1415},[955,6628,6629],{"class":957,"line":1093},[955,6630,1421],{"class":961},[955,6632,6633],{"class":957,"line":1112},[955,6634,1426],{"class":965},[955,6636,6637],{"class":957,"line":1129},[955,6638,992],{"emptyLinePlaceholder":8},[955,6640,6641,6643,6645,6647,6649,6651,6653,6655,6657,6659,6661,6663],{"class":957,"line":1433},[955,6642,999],{"class":998},[955,6644,966],{"class":965},[955,6646,1214],{"class":1002},[955,6648,872],{"class":965},[955,6650,884],{"class":1002},[955,6652,872],{"class":965},[955,6654,1017],{"class":1002},[955,6656,972],{"class":965},[955,6658,1452],{"class":1006},[955,6660,1361],{"class":969},[955,6662,944],{"class":965},[955,6664,1459],{"class":969},[856,6666,1462,6667,1466,6669,1163],{},[882,6668,1465],{},[865,6670,1470],{"href":1469},[1472,6672,6673],{},[856,6674,1476,6675,1480],{},[882,6676,1479],{},[912,6678],{},[915,6680,1486],{"id":1485},[856,6682,1489,6683,1493,6686,1496,6688,1500],{},[865,6684,909],{"href":906,"rel":6685},[908],[882,6687,1407],{},[882,6689,1499],{},[856,6691,1503,6692,1506,6694,1509,6696,1513],{},[882,6693,943],{},[882,6695,1407],{},[882,6697,1512],{},[946,6699,6700],{"className":948,"code":1516,"language":950,"meta":951,"style":951},[882,6701,6702,6720,6724,6736,6746],{"__ignoreMap":951},[955,6703,6704,6706,6708,6710,6712,6714,6716,6718],{"class":957,"line":958},[955,6705,962],{"class":961},[955,6707,966],{"class":965},[955,6709,1527],{"class":969},[955,6711,972],{"class":965},[955,6713,975],{"class":961},[955,6715,979],{"class":978},[955,6717,983],{"class":982},[955,6719,986],{"class":978},[955,6721,6722],{"class":957,"line":989},[955,6723,992],{"emptyLinePlaceholder":8},[955,6725,6726,6728,6730,6732,6734],{"class":957,"line":995},[955,6727,999],{"class":998},[955,6729,1548],{"class":1002},[955,6731,1007],{"class":1006},[955,6733,1553],{"class":1010},[955,6735,1556],{"class":965},[955,6737,6738,6740,6742,6744],{"class":957,"line":1030},[955,6739,1485],{"class":969},[955,6741,944],{"class":965},[955,6743,1565],{"class":969},[955,6745,1568],{"class":1415},[955,6747,6748,6750,6752,6754,6756,6758,6760],{"class":957,"line":1050},[955,6749,1485],{"class":969},[955,6751,944],{"class":965},[955,6753,1577],{"class":1010},[955,6755,1061],{"class":965},[955,6757,1151],{"class":969},[955,6759,1584],{"class":965},[955,6761,1587],{"class":1415},[856,6763,1590,6764,1594],{},[882,6765,1593],{},[912,6767],{},[915,6769,1600],{"id":1599},[856,6771,1603],{},[1605,6773,6774,6778,6782],{},[1302,6775,6776,1612],{},[882,6777,1611],{},[1302,6779,6780,1618],{},[882,6781,1617],{},[1302,6783,1621,6784,872,6786,872,6788,872,6790],{},[882,6785,891],{},[882,6787,894],{},[882,6789,898],{},[882,6791,902],{},[856,6793,1204,6794,1634,6796,1638,6798,1642,6800,1645,6802,1648,6804,1652],{},[882,6795,898],{},[882,6797,1637],{},[882,6799,1641],{},[882,6801,898],{},[882,6803,1641],{},[882,6805,1651],{},[946,6807,6809],{"className":6808,"code":1657,"language":1658},[1656],[882,6810,1657],{"__ignoreMap":951},[856,6812,1663,6813,1667,6815,1672],{},[882,6814,1666],{},[865,6816,1671],{"href":1670},[912,6818],{},[915,6820,1678],{"id":1677},[856,6822,1681],{},[920,6824,1685],{"id":1684},[856,6826,1688,6827,1691,6829,1695,6831,1699,6833,1703],{},[865,6828,867],{"href":96},[882,6830,1694],{},[882,6832,1698],{},[882,6834,1702],{},[856,6836,1706,6837,1709],{},[865,6838,123],{"href":124},[1711,6840,1714],{"id":1713},[1169,6842,6843,6853],{},[1172,6844,6845],{},[1175,6846,6847,6849,6851],{},[1178,6848,1723],{},[1178,6850,1726],{},[1178,6852,1729],{},[1188,6854,6855,6865],{},[1175,6856,6857,6861,6863],{},[1193,6858,6859],{},[882,6860,1698],{},[1193,6862,1740],{},[1193,6864,1743],{},[1175,6866,6867,6871,6875],{},[1193,6868,6869],{},[882,6870,1702],{},[1193,6872,1752,6873,1756],{},[882,6874,1755],{},[1193,6876,1759],{},[1711,6878,1763],{"id":1762},[856,6880,6881,1769],{},[882,6882,1768],{},[1771,6884,6885,6887,6889,6949,6951,6953,6961,6963,6969,6971,6977,6979],{"level":1773},[1711,6886,1777],{"id":1776},[856,6888,1780],{},[1169,6890,6891,6905],{},[1172,6892,6893],{},[1175,6894,6895,6897,6899,6901,6903],{},[1178,6896,1789],{},[1178,6898,1792],{},[1178,6900,1795],{},[1178,6902,1798],{},[1178,6904,1801],{},[1188,6906,6907,6921,6935],{},[1175,6908,6909,6911,6915,6917,6919],{},[1193,6910,1808],{},[1193,6912,6913],{},[882,6914,1813],{},[1193,6916,1816],{},[1193,6918,1819],{},[1193,6920,1819],{},[1175,6922,6923,6925,6929,6931,6933],{},[1193,6924,1826],{},[1193,6926,6927],{},[882,6928,1831],{},[1193,6930,1834],{},[1193,6932,1819],{},[1193,6934,1839],{},[1175,6936,6937,6939,6943,6945,6947],{},[1193,6938,1844],{},[1193,6940,6941],{},[882,6942,1849],{},[1193,6944,1852],{},[1193,6946,1819],{},[1193,6948,1857],{},[856,6950,1860],{},[1711,6952,1864],{"id":1863},[856,6954,1867,6955,1870,6957,1874,6959,1878],{},[882,6956,1275],{},[882,6958,1873],{},[882,6960,1877],{},[1711,6962,1882],{"id":1881},[856,6964,6965,1887,6967,1891],{},[882,6966,925],{},[882,6968,1890],{},[1711,6970,1895],{"id":1894},[856,6972,6973,1901,6975,1906],{},[882,6974,1900],{},[865,6976,1905],{"href":1904},[1711,6978,1910],{"id":1909},[856,6980,1913,6981,1919,6984,1925],{},[865,6982,1918],{"href":1916,"rel":6983},[908],[865,6985,1924],{"href":1922,"rel":6986},[908],[1711,6988,1929],{"id":1928},[1169,6990,6991,7001],{},[1172,6992,6993],{},[1175,6994,6995,6997,6999],{},[1178,6996,1938],{},[1178,6998,1941],{},[1178,7000,1186],{},[1188,7002,7003,7015],{},[1175,7004,7005,7009,7013],{},[1193,7006,7007],{},[882,7008,1952],{},[1193,7010,7011],{},[882,7012,1957],{},[1193,7014,1960],{},[1175,7016,7017,7021,7025],{},[1193,7018,7019],{},[882,7020,1967],{},[1193,7022,7023],{},[882,7024,1957],{},[1193,7026,1974],{},[920,7028,1978],{"id":1977},[856,7030,1981,7031,1985],{},[860,7032,1984],{},[1771,7034,7035,7037,7047,7056,7058,7064,7066],{"level":1773},[1711,7036,1991],{"id":1990},[856,7038,1994,7039,1998,7041,2001,7043,2005,7045,2009],{},[882,7040,1997],{},[865,7042,111],{"href":112},[882,7044,2004],{},[882,7046,2008],{},[856,7048,2012,7049,2016,7051,2020,7053,944],{},[860,7050,2015],{},[882,7052,2019],{},[865,7054,2025],{"href":2023,"rel":7055},[908],[1711,7057,2029],{"id":2028},[856,7059,2032,7060,2036,7062,2040],{},[882,7061,2035],{},[882,7063,2039],{},[1711,7065,2044],{"id":2043},[856,7067,7068,2050,7070,2054,7072,2060],{},[882,7069,2049],{},[882,7071,2053],{},[865,7073,2059],{"href":2057,"rel":7074},[908],[1711,7076,1929],{"id":2063},[1169,7078,7079,7089],{},[1172,7080,7081],{},[1175,7082,7083,7085,7087],{},[1178,7084,1938],{},[1178,7086,1941],{},[1178,7088,1186],{},[1188,7090,7091,7103,7115],{},[1175,7092,7093,7097,7101],{},[1193,7094,7095],{},[882,7096,1967],{},[1193,7098,7099],{},[882,7100,2088],{},[1193,7102,2091],{},[1175,7104,7105,7109,7113],{},[1193,7106,7107],{},[882,7108,1952],{},[1193,7110,7111],{},[882,7112,2102],{},[1193,7114,2105],{},[1175,7116,7117,7121,7125],{},[1193,7118,7119],{},[882,7120,1967],{},[1193,7122,7123],{},[882,7124,2102],{},[1193,7126,2118],{},[920,7128,2122],{"id":2121},[856,7130,2125,7131,2128],{},[882,7132,1243],{},[1771,7134,7135,7137,7147,7149,7155,7157],{"level":1773},[1711,7136,2134],{"id":2133},[856,7138,2137,7139,2141,7141,1648,7143,2148,7145,2152],{},[882,7140,2140],{},[882,7142,2144],{},[882,7144,2147],{},[860,7146,2151],{},[1711,7148,2029],{"id":2155},[856,7150,2158,7151,2036,7153,2165],{},[882,7152,2161],{},[882,7154,2164],{},[1711,7156,2169],{"id":2168},[856,7158,7159,2050,7161,2177,7163,2180,7165,2185,7168,1648,7170,2192],{},[882,7160,2140],{},[882,7162,2176],{},[882,7164,1243],{},[865,7166,2184],{"href":2057,"rel":7167},[908],[882,7169,2188],{},[882,7171,2191],{},[1711,7173,1929],{"id":2195},[1169,7175,7176,7186],{},[1172,7177,7178],{},[1175,7179,7180,7182,7184],{},[1178,7181,1938],{},[1178,7183,1941],{},[1178,7185,1186],{},[1188,7187,7188,7200,7212],{},[1175,7189,7190,7194,7198],{},[1193,7191,7192],{},[882,7193,1967],{},[1193,7195,7196],{},[882,7197,2220],{},[1193,7199,2223],{},[1175,7201,7202,7206,7210],{},[1193,7203,7204],{},[882,7205,1952],{},[1193,7207,7208],{},[882,7209,2234],{},[1193,7211,2105],{},[1175,7213,7214,7218,7222],{},[1193,7215,7216],{},[882,7217,1967],{},[1193,7219,7220],{},[882,7221,2247],{},[1193,7223,2250],{},[912,7225],{},[915,7227,2256],{"id":2255},[856,7229,2259,7230,2262],{},[882,7231,902],{},[920,7233,2266],{"id":2265},[856,7235,2269],{},[1169,7237,7238,7246],{},[1172,7239,7240],{},[1175,7241,7242,7244],{},[1178,7243,2278],{},[1178,7245,2281],{},[1188,7247,7248,7256,7264,7272],{},[1175,7249,7250,7254],{},[1193,7251,7252],{},[882,7253,2290],{},[1193,7255,1685],{},[1175,7257,7258,7262],{},[1193,7259,7260],{},[882,7261,1085],{},[1193,7263,1978],{},[1175,7265,7266,7270],{},[1193,7267,7268],{},[882,7269,2307],{},[1193,7271,2310],{},[1175,7273,7274,7278],{},[1193,7275,7276],{},[882,7277,2317],{},[1193,7279,2310],{},[856,7281,2322,7282,872,7284,872,7286,1163],{},[882,7283,2325],{},[882,7285,2328],{},[882,7287,2331],{},[920,7289,2335],{"id":2334},[856,7291,7292,2341,7294,2345,7296,2348],{},[882,7293,2340],{},[882,7295,2344],{},[882,7297,898],{},[946,7299,7300],{"className":948,"code":2351,"language":950,"meta":951,"style":951},[882,7301,7302,7320,7324,7360,7374,7388,7402],{"__ignoreMap":951},[955,7303,7304,7306,7308,7310,7312,7314,7316,7318],{"class":957,"line":958},[955,7305,962],{"class":961},[955,7307,966],{"class":965},[955,7309,2144],{"class":969},[955,7311,972],{"class":965},[955,7313,975],{"class":961},[955,7315,979],{"class":978},[955,7317,983],{"class":982},[955,7319,986],{"class":978},[955,7321,7322],{"class":957,"line":989},[955,7323,992],{"emptyLinePlaceholder":8},[955,7325,7326,7328,7330,7332,7334,7336,7338,7340,7342,7344,7346,7348,7350,7352,7354,7356,7358],{"class":957,"line":995},[955,7327,2380],{"class":961},[955,7329,2383],{"class":1010},[955,7331,1061],{"class":965},[955,7333,898],{"class":969},[955,7335,872],{"class":965},[955,7337,1088],{"class":978},[955,7339,2394],{"class":982},[955,7341,1088],{"class":978},[955,7343,872],{"class":965},[955,7345,1064],{"class":969},[955,7347,872],{"class":965},[955,7349,2405],{"class":969},[955,7351,872],{"class":965},[955,7353,2410],{"class":969},[955,7355,872],{"class":965},[955,7357,2415],{"class":969},[955,7359,2418],{"class":965},[955,7361,7362,7364,7366,7368,7370,7372],{"class":957,"line":1030},[955,7363,2423],{"class":969},[955,7365,1020],{"class":1036},[955,7367,979],{"class":978},[955,7369,2430],{"class":982},[955,7371,1088],{"class":978},[955,7373,1047],{"class":965},[955,7375,7376,7378,7380,7382,7384,7386],{"class":957,"line":1050},[955,7377,2439],{"class":969},[955,7379,1020],{"class":1036},[955,7381,979],{"class":978},[955,7383,2446],{"class":982},[955,7385,1088],{"class":978},[955,7387,1047],{"class":965},[955,7389,7390,7392,7394,7396,7398,7400],{"class":957,"line":1075},[955,7391,2455],{"class":969},[955,7393,1020],{"class":1036},[955,7395,979],{"class":978},[955,7397,2462],{"class":982},[955,7399,1088],{"class":978},[955,7401,1047],{"class":965},[955,7403,7404],{"class":957,"line":1093},[955,7405,1132],{"class":965},[856,7407,2473,7408,2476,7410,2480],{},[882,7409,884],{},[865,7411,1685],{"href":2479},[920,7413,2484],{"id":2483},[856,7415,2487],{},[1169,7417,7418,7428],{},[1172,7419,7420],{},[1175,7421,7422,7424,7426],{},[1178,7423,1938],{},[1178,7425,1941],{},[1178,7427,1186],{},[1188,7429,7430,7444],{},[1175,7431,7432,7436,7440],{},[1193,7433,7434],{},[882,7435,1952],{},[1193,7437,7438],{},[882,7439,2234],{},[1193,7441,2514,7442,2517],{},[882,7443,2164],{},[1175,7445,7446,7450,7454],{},[1193,7447,7448],{},[882,7449,1967],{},[1193,7451,7452],{},[882,7453,2234],{},[1193,7455,2530],{},[856,7457,2533,7458,2536,7460,2540,7462,2544],{},[882,7459,2188],{},[882,7461,2539],{},[865,7463,2543],{"href":1904},[912,7465],{},[915,7467,2550],{"id":2549},[856,7469,2553],{},[1299,7471,7472,7484,7488,7494,7500],{},[1302,7473,7474,2561,7476,872,7478,872,7480,872,7482,1756],{},[860,7475,2560],{},[882,7477,891],{},[882,7479,894],{},[882,7481,898],{},[882,7483,902],{},[1302,7485,7486,2574],{},[860,7487,1777],{},[1302,7489,7490,2579,7492],{},[860,7491,1314],{},[882,7493,1292],{},[1302,7495,7496,1324,7498,2588],{},[860,7497,1323],{},[882,7499,891],{},[1302,7501,7502,2594,7504,2597],{},[860,7503,2593],{},[882,7505,898],{},[1169,7507,7508,7518],{},[1172,7509,7510],{},[1175,7511,7512,7514,7516],{},[1178,7513,2606],{},[1178,7515,1729],{},[1178,7517,2281],{},[1188,7519,7520,7532,7544],{},[1175,7521,7522,7526,7528],{},[1193,7523,7524],{},[882,7525,2619],{},[1193,7527,2622],{},[1193,7529,7530],{},[882,7531,2627],{},[1175,7533,7534,7538,7540],{},[1193,7535,7536],{},[882,7537,2039],{},[1193,7539,2636],{},[1193,7541,7542],{},[882,7543,2641],{},[1175,7545,7546,7550,7552],{},[1193,7547,7548],{},[882,7549,2164],{},[1193,7551,2650],{},[1193,7553,7554],{},[882,7555,2655],{},[920,7557,2659],{"id":2658},[856,7559,2662],{},[1169,7561,7562,7572],{},[1172,7563,7564],{},[1175,7565,7566,7568,7570],{},[1178,7567,2671],{},[1178,7569,2674],{},[1178,7571,2677],{},[1188,7573,7574,7584],{},[1175,7575,7576,7578,7580],{},[1193,7577,1952],{},[1193,7579,1852],{},[1193,7581,7582],{},[882,7583,2690],{},[1175,7585,7586,7588,7590],{},[1193,7587,1967],{},[1193,7589,2697],{},[1193,7591,7592],{},[882,7593,2702],{},[856,7595,2705],{},[1605,7597,7598,7602,7606],{},[1302,7599,7600,2713],{},[882,7601,2712],{},[1302,7603,7604,2719],{},[882,7605,2718],{},[1302,7607,7608,2725],{},[882,7609,2724],{},[912,7611],{},[915,7613,2731],{"id":2730},[920,7615,2735],{"id":2734},[856,7617,1204,7618,2741],{},[882,7619,2740],{},[946,7621,7622],{"className":948,"code":2744,"language":950,"meta":951,"style":951},[882,7623,7624,7638,7656,7660,7672],{"__ignoreMap":951},[955,7625,7626,7628,7630,7632,7634,7636],{"class":957,"line":958},[955,7627,962],{"class":961},[955,7629,2753],{"class":969},[955,7631,2756],{"class":961},[955,7633,979],{"class":978},[955,7635,2761],{"class":982},[955,7637,986],{"class":978},[955,7639,7640,7642,7644,7646,7648,7650,7652,7654],{"class":957,"line":989},[955,7641,962],{"class":961},[955,7643,966],{"class":965},[955,7645,2740],{"class":969},[955,7647,972],{"class":965},[955,7649,975],{"class":961},[955,7651,979],{"class":978},[955,7653,983],{"class":982},[955,7655,986],{"class":978},[955,7657,7658],{"class":957,"line":995},[955,7659,992],{"emptyLinePlaceholder":8},[955,7661,7662,7664,7666,7668,7670],{"class":957,"line":1030},[955,7663,999],{"class":998},[955,7665,2792],{"class":1002},[955,7667,1007],{"class":1006},[955,7669,2753],{"class":1010},[955,7671,1556],{"class":965},[955,7673,7674,7676,7678,7680,7682,7684],{"class":957,"line":1050},[955,7675,2803],{"class":969},[955,7677,944],{"class":965},[955,7679,2808],{"class":1010},[955,7681,1061],{"class":965},[955,7683,2740],{"class":969},[955,7685,1384],{"class":965},[856,7687,2817,7688,2822],{},[865,7689,2821],{"href":2820},[2824,7691,7692],{},[856,7693,2828,7694,2832,7696,2836,7698,944],{},[882,7695,2831],{},[882,7697,2835],{},[882,7699,2839],{},[920,7701,2843],{"id":2842},[856,7703,7704,2848,7706,2851,7708,2854],{},[882,7705,1768],{},[882,7707,1698],{},[882,7709,1694],{},[946,7711,7712],{"className":948,"code":2857,"language":950,"meta":951,"style":951},[882,7713,7714,7732,7736,7754,7768,7808,7824,7836,7842,7846,7860,7874,7888,7892,7896,7900,7918,7950,7954,7958,7962,7994],{"__ignoreMap":951},[955,7715,7716,7718,7720,7722,7724,7726,7728,7730],{"class":957,"line":958},[955,7717,962],{"class":961},[955,7719,966],{"class":965},[955,7721,1768],{"class":969},[955,7723,972],{"class":965},[955,7725,975],{"class":961},[955,7727,979],{"class":978},[955,7729,983],{"class":982},[955,7731,986],{"class":978},[955,7733,7734],{"class":957,"line":989},[955,7735,992],{"emptyLinePlaceholder":8},[955,7737,7738,7740,7742,7744,7746,7748,7750,7752],{"class":957,"line":995},[955,7739,2886],{"class":998},[955,7741,2889],{"class":998},[955,7743,2892],{"class":1010},[955,7745,1061],{"class":965},[955,7747,2898],{"class":2897},[955,7749,872],{"class":965},[955,7751,2415],{"class":2897},[955,7753,1410],{"class":965},[955,7755,7756,7758,7760,7762,7764,7766],{"class":957,"line":1030},[955,7757,2909],{"class":998},[955,7759,1361],{"class":1002},[955,7761,1007],{"class":1006},[955,7763,2916],{"class":961},[955,7765,2919],{"class":1010},[955,7767,2922],{"class":965},[955,7769,7770,7772,7774,7776,7778,7780,7782,7784,7786,7788,7790,7792,7794,7796,7798,7800,7802,7804,7806],{"class":957,"line":1050},[955,7771,2927],{"class":965},[955,7773,2930],{"class":969},[955,7775,1020],{"class":1036},[955,7777,2935],{"class":1010},[955,7779,1061],{"class":965},[955,7781,2898],{"class":969},[955,7783,944],{"class":965},[955,7785,1064],{"class":969},[955,7787,944],{"class":965},[955,7789,2930],{"class":969},[955,7791,868],{"class":965},[955,7793,891],{"class":969},[955,7795,1020],{"class":1036},[955,7797,2956],{"class":969},[955,7799,944],{"class":965},[955,7801,1064],{"class":969},[955,7803,944],{"class":965},[955,7805,1044],{"class":969},[955,7807,2967],{"class":965},[955,7809,7810,7812,7814,7816,7818,7820,7822],{"class":957,"line":1075},[955,7811,2972],{"class":969},[955,7813,944],{"class":965},[955,7815,2977],{"class":969},[955,7817,944],{"class":965},[955,7819,2982],{"class":969},[955,7821,2985],{"class":965},[955,7823,2988],{"class":1415},[955,7825,7826,7828,7830,7832,7834],{"class":957,"line":1093},[955,7827,2972],{"class":969},[955,7829,944],{"class":965},[955,7831,2410],{"class":969},[955,7833,1399],{"class":1006},[955,7835,1047],{"class":965},[955,7837,7838,7840],{"class":957,"line":1112},[955,7839,3005],{"class":969},[955,7841,1047],{"class":965},[955,7843,7844],{"class":957,"line":1129},[955,7845,3012],{"class":965},[955,7847,7848,7850,7852,7854,7856,7858],{"class":957,"line":1433},[955,7849,3017],{"class":969},[955,7851,1020],{"class":1036},[955,7853,979],{"class":978},[955,7855,3024],{"class":982},[955,7857,1088],{"class":978},[955,7859,1047],{"class":965},[955,7861,7862,7864,7866,7868,7870,7872],{"class":957,"line":3031},[955,7863,3034],{"class":969},[955,7865,1020],{"class":1036},[955,7867,979],{"class":978},[955,7869,2446],{"class":982},[955,7871,1088],{"class":978},[955,7873,1047],{"class":965},[955,7875,7876,7878,7880,7882,7884,7886],{"class":957,"line":3047},[955,7877,3050],{"class":969},[955,7879,1020],{"class":1036},[955,7881,979],{"class":978},[955,7883,3057],{"class":982},[955,7885,1088],{"class":978},[955,7887,1047],{"class":965},[955,7889,7890],{"class":957,"line":3064},[955,7891,3067],{"class":965},[955,7893,7894],{"class":957,"line":3070},[955,7895,3073],{"class":965},[955,7897,7898],{"class":957,"line":3076},[955,7899,992],{"emptyLinePlaceholder":8},[955,7901,7902,7904,7906,7908,7910,7912,7914,7916],{"class":957,"line":3081},[955,7903,3084],{"class":961},[955,7905,1396],{"class":965},[955,7907,1402],{"class":969},[955,7909,3091],{"class":1006},[955,7911,979],{"class":978},[955,7913,3096],{"class":982},[955,7915,1088],{"class":978},[955,7917,1410],{"class":965},[955,7919,7920,7922,7924,7926,7928,7930,7932,7934,7936,7938,7940,7942,7944,7946,7948],{"class":957,"line":3103},[955,7921,3106],{"class":961},[955,7923,3109],{"class":969},[955,7925,944],{"class":965},[955,7927,3114],{"class":1010},[955,7929,1061],{"class":965},[955,7931,3120],{"class":3119},[955,7933,1163],{"class":965},[955,7935,5],{"class":1010},[955,7937,3127],{"class":965},[955,7939,3130],{"class":969},[955,7941,1020],{"class":1036},[955,7943,979],{"class":978},[955,7945,3137],{"class":982},[955,7947,1088],{"class":978},[955,7949,3142],{"class":965},[955,7951,7952],{"class":957,"line":3145},[955,7953,3148],{"class":965},[955,7955,7956],{"class":957,"line":3151},[955,7957,992],{"emptyLinePlaceholder":8},[955,7959,7960],{"class":957,"line":3156},[955,7961,3159],{"class":1415},[955,7963,7964,7966,7968,7970,7972,7974,7976,7978,7980,7982,7984,7986,7988,7990,7992],{"class":957,"line":3162},[955,7965,3165],{"class":961},[955,7967,3109],{"class":969},[955,7969,944],{"class":965},[955,7971,3114],{"class":1010},[955,7973,1061],{"class":965},[955,7975,3176],{"class":3119},[955,7977,1163],{"class":965},[955,7979,5],{"class":1010},[955,7981,3127],{"class":965},[955,7983,3185],{"class":969},[955,7985,1020],{"class":1036},[955,7987,979],{"class":978},[955,7989,3192],{"class":982},[955,7991,1088],{"class":978},[955,7993,3142],{"class":965},[955,7995,7996],{"class":957,"line":3199},[955,7997,1426],{"class":965},[856,7999,1204,8000,3206,8002,3210,8004,3206,8006,3217],{},[882,8001,1064],{},[882,8003,3209],{},[882,8005,3213],{},[882,8007,3216],{},[856,8009,3220,8010,3224,8012,3228],{},[882,8011,3223],{},[882,8013,3227],{},[920,8015,1978],{"id":3231},[856,8017,8018,3237,8020,3240],{},[882,8019,3236],{},[882,8021,1997],{},[946,8023,8024],{"className":948,"code":3243,"language":950,"meta":951,"style":951},[882,8025,8026,8044,8048,8066,8082,8086,8120,8124,8128,8148,8162,8192,8196,8200,8204,8224,8234,8248,8252],{"__ignoreMap":951},[955,8027,8028,8030,8032,8034,8036,8038,8040,8042],{"class":957,"line":958},[955,8029,962],{"class":961},[955,8031,966],{"class":965},[955,8033,3236],{"class":969},[955,8035,972],{"class":965},[955,8037,975],{"class":961},[955,8039,979],{"class":978},[955,8041,983],{"class":982},[955,8043,986],{"class":978},[955,8045,8046],{"class":957,"line":989},[955,8047,992],{"emptyLinePlaceholder":8},[955,8049,8050,8052,8054,8056,8058,8060,8062,8064],{"class":957,"line":995},[955,8051,2886],{"class":998},[955,8053,2889],{"class":998},[955,8055,3276],{"class":1010},[955,8057,1061],{"class":965},[955,8059,2898],{"class":2897},[955,8061,872],{"class":965},[955,8063,2415],{"class":2897},[955,8065,1410],{"class":965},[955,8067,8068,8070,8072,8074,8076,8078,8080],{"class":957,"line":1030},[955,8069,2909],{"class":998},[955,8071,3293],{"class":1002},[955,8073,1007],{"class":1006},[955,8075,3298],{"class":969},[955,8077,944],{"class":965},[955,8079,3303],{"class":1010},[955,8081,1556],{"class":965},[955,8083,8084],{"class":957,"line":1050},[955,8085,992],{"emptyLinePlaceholder":8},[955,8087,8088,8090,8092,8094,8096,8098,8100,8102,8104,8106,8108,8110,8112,8114,8116,8118],{"class":957,"line":1075},[955,8089,2909],{"class":998},[955,8091,966],{"class":965},[955,8093,1407],{"class":1002},[955,8095,872],{"class":965},[955,8097,3130],{"class":1002},[955,8099,972],{"class":965},[955,8101,1452],{"class":1006},[955,8103,2916],{"class":961},[955,8105,3330],{"class":1010},[955,8107,1061],{"class":965},[955,8109,2898],{"class":969},[955,8111,944],{"class":965},[955,8113,3339],{"class":969},[955,8115,944],{"class":965},[955,8117,3344],{"class":969},[955,8119,1384],{"class":965},[955,8121,8122],{"class":957,"line":1093},[955,8123,992],{"emptyLinePlaceholder":8},[955,8125,8126],{"class":957,"line":1112},[955,8127,3355],{"class":1415},[955,8129,8130,8132,8134,8136,8138,8140,8142,8144,8146],{"class":957,"line":1129},[955,8131,2909],{"class":998},[955,8133,3362],{"class":1002},[955,8135,1007],{"class":1006},[955,8137,3298],{"class":969},[955,8139,944],{"class":965},[955,8141,3303],{"class":1010},[955,8143,3373],{"class":965},[955,8145,3376],{"class":1006},[955,8147,3379],{"class":969},[955,8149,8150,8152,8154,8156,8158,8160],{"class":957,"line":1433},[955,8151,3084],{"class":961},[955,8153,1396],{"class":965},[955,8155,3388],{"class":969},[955,8157,3391],{"class":1006},[955,8159,3394],{"class":3119},[955,8161,1410],{"class":965},[955,8163,8164,8166,8168,8170,8172,8174,8176,8178,8180,8182,8184,8186,8188,8190],{"class":957,"line":3031},[955,8165,3401],{"class":961},[955,8167,3405],{"class":3404},[955,8169,3408],{"class":1023},[955,8171,1061],{"class":965},[955,8173,3413],{"class":2897},[955,8175,3416],{"class":998},[955,8177,3419],{"class":1010},[955,8179,1061],{"class":965},[955,8181,3413],{"class":969},[955,8183,872],{"class":965},[955,8185,3428],{"class":3119},[955,8187,3431],{"class":1006},[955,8189,3362],{"class":969},[955,8191,3436],{"class":965},[955,8193,8194],{"class":957,"line":3047},[955,8195,3148],{"class":965},[955,8197,8198],{"class":957,"line":3064},[955,8199,992],{"emptyLinePlaceholder":8},[955,8201,8202],{"class":957,"line":3070},[955,8203,3449],{"class":1415},[955,8205,8206,8208,8210,8212,8214,8216,8218,8220,8222],{"class":957,"line":3076},[955,8207,3165],{"class":961},[955,8209,3109],{"class":969},[955,8211,944],{"class":965},[955,8213,3114],{"class":1010},[955,8215,1061],{"class":965},[955,8217,2019],{"class":3119},[955,8219,1163],{"class":965},[955,8221,5],{"class":1010},[955,8223,3470],{"class":965},[955,8225,8226,8228,8230,8232],{"class":957,"line":3081},[955,8227,3475],{"class":969},[955,8229,1020],{"class":1036},[955,8231,3481],{"class":3480},[955,8233,1047],{"class":965},[955,8235,8236,8238,8240,8242,8244,8246],{"class":957,"line":3103},[955,8237,3488],{"class":969},[955,8239,1020],{"class":1036},[955,8241,979],{"class":978},[955,8243,3495],{"class":982},[955,8245,1088],{"class":978},[955,8247,1047],{"class":965},[955,8249,8250],{"class":957,"line":3145},[955,8251,3504],{"class":965},[955,8253,8254],{"class":957,"line":3151},[955,8255,1426],{"class":965},[856,8257,3511,8258,3514,8260,3517],{},[882,8259,2008],{},[860,8261,1984],{},[920,8263,3521],{"id":3520},[856,8265,3524],{},[946,8267,8268],{"className":948,"code":3527,"language":950,"meta":951,"style":951},[882,8269,8270,8284,8298,8304,8310,8316,8322,8328,8334,8340,8346,8352,8364,8382,8386,8402,8406,8410,8428,8442,8448,8454,8460,8466,8488,8506,8538,8542,8556,8562,8574,8618,8634,8646,8652,8656,8688,8702,8716,8720,8724,8728,8762,8794,8826,8830,8834,8858,8862,8866,8870,8874,8878,8896,8902,8908,8914,8920,8924,8928,8932,8936,8954,8968,8974,8980,8986,8992,9014,9020,9026,9044,9048,9070,9074,9078,9104,9108,9140,9144],{"__ignoreMap":951},[955,8271,8272,8274,8276,8278,8280,8282],{"class":957,"line":958},[955,8273,962],{"class":961},[955,8275,2753],{"class":969},[955,8277,2756],{"class":961},[955,8279,979],{"class":978},[955,8281,2761],{"class":982},[955,8283,986],{"class":978},[955,8285,8286,8288,8290,8292,8294,8296],{"class":957,"line":989},[955,8287,962],{"class":961},[955,8289,1101],{"class":969},[955,8291,2756],{"class":961},[955,8293,979],{"class":978},[955,8295,3556],{"class":982},[955,8297,986],{"class":978},[955,8299,8300,8302],{"class":957,"line":995},[955,8301,962],{"class":961},[955,8303,3565],{"class":965},[955,8305,8306,8308],{"class":957,"line":1030},[955,8307,3570],{"class":969},[955,8309,1047],{"class":965},[955,8311,8312,8314],{"class":957,"line":1050},[955,8313,3577],{"class":969},[955,8315,1047],{"class":965},[955,8317,8318,8320],{"class":957,"line":1075},[955,8319,3584],{"class":969},[955,8321,1047],{"class":965},[955,8323,8324,8326],{"class":957,"line":1093},[955,8325,3591],{"class":969},[955,8327,1047],{"class":965},[955,8329,8330,8332],{"class":957,"line":1112},[955,8331,3598],{"class":969},[955,8333,1047],{"class":965},[955,8335,8336,8338],{"class":957,"line":1129},[955,8337,3605],{"class":969},[955,8339,1047],{"class":965},[955,8341,8342,8344],{"class":957,"line":1433},[955,8343,3612],{"class":969},[955,8345,1047],{"class":965},[955,8347,8348,8350],{"class":957,"line":3031},[955,8349,3619],{"class":969},[955,8351,1047],{"class":965},[955,8353,8354,8356,8358,8360,8362],{"class":957,"line":3047},[955,8355,3626],{"class":965},[955,8357,975],{"class":961},[955,8359,979],{"class":978},[955,8361,983],{"class":982},[955,8363,986],{"class":978},[955,8365,8366,8368,8370,8372,8374,8376,8378,8380],{"class":957,"line":3064},[955,8367,962],{"class":961},[955,8369,966],{"class":965},[955,8371,3643],{"class":969},[955,8373,972],{"class":965},[955,8375,975],{"class":961},[955,8377,979],{"class":978},[955,8379,3652],{"class":982},[955,8381,986],{"class":978},[955,8383,8384],{"class":957,"line":3070},[955,8385,992],{"emptyLinePlaceholder":8},[955,8387,8388,8390,8392,8394,8396,8398,8400],{"class":957,"line":3076},[955,8389,999],{"class":998},[955,8391,3665],{"class":1002},[955,8393,1007],{"class":1006},[955,8395,2753],{"class":969},[955,8397,944],{"class":965},[955,8399,3674],{"class":1010},[955,8401,1556],{"class":965},[955,8403,8404],{"class":957,"line":3081},[955,8405,992],{"emptyLinePlaceholder":8},[955,8407,8408],{"class":957,"line":3103},[955,8409,3685],{"class":1415},[955,8411,8412,8414,8416,8418,8420,8422,8424,8426],{"class":957,"line":3145},[955,8413,3690],{"class":969},[955,8415,944],{"class":965},[955,8417,3695],{"class":1010},[955,8419,1061],{"class":965},[955,8421,1088],{"class":978},[955,8423,3702],{"class":982},[955,8425,1088],{"class":978},[955,8427,1047],{"class":965},[955,8429,8430,8432,8434,8436,8438,8440],{"class":957,"line":3151},[955,8431,3570],{"class":1010},[955,8433,1061],{"class":965},[955,8435,1088],{"class":978},[955,8437,3717],{"class":982},[955,8439,1088],{"class":978},[955,8441,1072],{"class":965},[955,8443,8444,8446],{"class":957,"line":3156},[955,8445,3577],{"class":969},[955,8447,1047],{"class":965},[955,8449,8450,8452],{"class":957,"line":3162},[955,8451,3584],{"class":969},[955,8453,1047],{"class":965},[955,8455,8456,8458],{"class":957,"line":3199},[955,8457,3591],{"class":969},[955,8459,1047],{"class":965},[955,8461,8462,8464],{"class":957,"line":3742},[955,8463,3598],{"class":969},[955,8465,1047],{"class":965},[955,8467,8468,8470,8472,8474,8476,8478,8480,8482,8484,8486],{"class":957,"line":3749},[955,8469,3752],{"class":969},[955,8471,944],{"class":965},[955,8473,5],{"class":1010},[955,8475,3127],{"class":965},[955,8477,3761],{"class":969},[955,8479,1020],{"class":1036},[955,8481,979],{"class":978},[955,8483,3768],{"class":982},[955,8485,1088],{"class":978},[955,8487,3773],{"class":965},[955,8489,8490,8492,8494,8496,8498,8500,8502,8504],{"class":957,"line":3776},[955,8491,3779],{"class":998},[955,8493,1396],{"class":965},[955,8495,2898],{"class":2897},[955,8497,872],{"class":965},[955,8499,2415],{"class":2897},[955,8501,1584],{"class":965},[955,8503,3792],{"class":998},[955,8505,3565],{"class":965},[955,8507,8508,8510,8512,8514,8516,8518,8520,8522,8524,8526,8528,8530,8532,8534,8536],{"class":957,"line":3797},[955,8509,3800],{"class":998},[955,8511,3803],{"class":1002},[955,8513,1007],{"class":1006},[955,8515,1101],{"class":969},[955,8517,944],{"class":965},[955,8519,3812],{"class":1010},[955,8521,1061],{"class":965},[955,8523,3817],{"class":3119},[955,8525,1163],{"class":965},[955,8527,3822],{"class":1010},[955,8529,1061],{"class":965},[955,8531,1088],{"class":978},[955,8533,3829],{"class":982},[955,8535,1088],{"class":978},[955,8537,1384],{"class":965},[955,8539,8540],{"class":957,"line":3836},[955,8541,992],{"emptyLinePlaceholder":8},[955,8543,8544,8546,8548,8550,8552,8554],{"class":957,"line":3841},[955,8545,3800],{"class":998},[955,8547,1361],{"class":1002},[955,8549,1007],{"class":1006},[955,8551,2916],{"class":961},[955,8553,2383],{"class":1010},[955,8555,2922],{"class":965},[955,8557,8558,8560],{"class":957,"line":3856},[955,8559,3859],{"class":969},[955,8561,1047],{"class":965},[955,8563,8564,8566,8568,8570,8572],{"class":957,"line":3864},[955,8565,3867],{"class":978},[955,8567,3870],{"class":982},[955,8569,1088],{"class":978},[955,8571,2985],{"class":965},[955,8573,3877],{"class":1415},[955,8575,8576,8578,8580,8582,8584,8586,8588,8590,8592,8594,8596,8598,8600,8602,8604,8606,8608,8610,8612,8614,8616],{"class":957,"line":3880},[955,8577,3883],{"class":965},[955,8579,2930],{"class":969},[955,8581,1020],{"class":1036},[955,8583,2935],{"class":1010},[955,8585,1061],{"class":965},[955,8587,2898],{"class":969},[955,8589,944],{"class":965},[955,8591,1064],{"class":969},[955,8593,1399],{"class":1006},[955,8595,944],{"class":965},[955,8597,2930],{"class":969},[955,8599,868],{"class":965},[955,8601,891],{"class":969},[955,8603,1020],{"class":1036},[955,8605,2956],{"class":969},[955,8607,944],{"class":965},[955,8609,1064],{"class":969},[955,8611,1399],{"class":1006},[955,8613,944],{"class":965},[955,8615,1044],{"class":969},[955,8617,2967],{"class":965},[955,8619,8620,8622,8624,8626,8628,8630,8632],{"class":957,"line":3926},[955,8621,3929],{"class":969},[955,8623,944],{"class":965},[955,8625,2977],{"class":969},[955,8627,944],{"class":965},[955,8629,2982],{"class":969},[955,8631,3940],{"class":965},[955,8633,2988],{"class":1415},[955,8635,8636,8638,8640,8642,8644],{"class":957,"line":3945},[955,8637,3929],{"class":969},[955,8639,944],{"class":965},[955,8641,2410],{"class":969},[955,8643,1399],{"class":1006},[955,8645,1047],{"class":965},[955,8647,8648,8650],{"class":957,"line":3958},[955,8649,3961],{"class":969},[955,8651,1047],{"class":965},[955,8653,8654],{"class":957,"line":3966},[955,8655,3969],{"class":965},[955,8657,8658,8660,8662,8664,8666,8668,8670,8672,8674,8676,8678,8680,8682,8684,8686],{"class":957,"line":3972},[955,8659,3975],{"class":969},[955,8661,1020],{"class":1036},[955,8663,2956],{"class":969},[955,8665,944],{"class":965},[955,8667,3984],{"class":969},[955,8669,3987],{"class":965},[955,8671,1088],{"class":978},[955,8673,3992],{"class":982},[955,8675,1088],{"class":978},[955,8677,3997],{"class":965},[955,8679,4000],{"class":1006},[955,8681,979],{"class":978},[955,8683,4005],{"class":982},[955,8685,1088],{"class":978},[955,8687,1047],{"class":965},[955,8689,8690,8692,8694,8696,8698,8700],{"class":957,"line":4012},[955,8691,4015],{"class":969},[955,8693,1020],{"class":1036},[955,8695,979],{"class":978},[955,8697,4022],{"class":982},[955,8699,1088],{"class":978},[955,8701,1047],{"class":965},[955,8703,8704,8706,8708,8710,8712,8714],{"class":957,"line":4029},[955,8705,4032],{"class":969},[955,8707,1020],{"class":1036},[955,8709,979],{"class":978},[955,8711,4039],{"class":982},[955,8713,1088],{"class":978},[955,8715,1047],{"class":965},[955,8717,8718],{"class":957,"line":4046},[955,8719,4049],{"class":965},[955,8721,8722],{"class":957,"line":4052},[955,8723,4055],{"class":965},[955,8725,8726],{"class":957,"line":4058},[955,8727,992],{"emptyLinePlaceholder":8},[955,8729,8730,8732,8734,8736,8738,8740,8742,8744,8746,8748,8750,8752,8754,8756,8758,8760],{"class":957,"line":4063},[955,8731,4066],{"class":961},[955,8733,1396],{"class":965},[955,8735,1399],{"class":1006},[955,8737,1402],{"class":969},[955,8739,944],{"class":965},[955,8741,4077],{"class":969},[955,8743,4080],{"class":1006},[955,8745,1361],{"class":969},[955,8747,944],{"class":965},[955,8749,4087],{"class":969},[955,8751,3091],{"class":1006},[955,8753,979],{"class":978},[955,8755,3096],{"class":982},[955,8757,1088],{"class":978},[955,8759,1584],{"class":965},[955,8761,4100],{"class":961},[955,8763,8764,8766,8768,8770,8772,8774,8776,8778,8780,8782,8784,8786,8788,8790,8792],{"class":957,"line":4103},[955,8765,4066],{"class":961},[955,8767,1396],{"class":965},[955,8769,1399],{"class":1006},[955,8771,1402],{"class":969},[955,8773,944],{"class":965},[955,8775,4077],{"class":969},[955,8777,4080],{"class":1006},[955,8779,1361],{"class":969},[955,8781,944],{"class":965},[955,8783,4087],{"class":969},[955,8785,3091],{"class":1006},[955,8787,979],{"class":978},[955,8789,4130],{"class":982},[955,8791,1088],{"class":978},[955,8793,1410],{"class":965},[955,8795,8796,8798,8800,8802,8804,8806,8808,8810,8812,8814,8816,8818,8820,8822,8824],{"class":957,"line":4137},[955,8797,4140],{"class":961},[955,8799,3109],{"class":969},[955,8801,944],{"class":965},[955,8803,3114],{"class":1010},[955,8805,1061],{"class":965},[955,8807,4151],{"class":3119},[955,8809,1163],{"class":965},[955,8811,5],{"class":1010},[955,8813,3127],{"class":965},[955,8815,3130],{"class":969},[955,8817,1020],{"class":1036},[955,8819,979],{"class":978},[955,8821,4166],{"class":982},[955,8823,1088],{"class":978},[955,8825,3142],{"class":965},[955,8827,8828],{"class":957,"line":4173},[955,8829,3067],{"class":965},[955,8831,8832],{"class":957,"line":4178},[955,8833,992],{"emptyLinePlaceholder":8},[955,8835,8836,8838,8840,8842,8844,8846,8848,8850,8852,8854,8856],{"class":957,"line":4183},[955,8837,3106],{"class":961},[955,8839,3109],{"class":969},[955,8841,944],{"class":965},[955,8843,3114],{"class":1010},[955,8845,1061],{"class":965},[955,8847,2019],{"class":3119},[955,8849,1163],{"class":965},[955,8851,5],{"class":1010},[955,8853,1061],{"class":965},[955,8855,1402],{"class":969},[955,8857,1384],{"class":965},[955,8859,8860],{"class":957,"line":4208},[955,8861,3148],{"class":965},[955,8863,8864],{"class":957,"line":4213},[955,8865,1384],{"class":965},[955,8867,8868],{"class":957,"line":4218},[955,8869,992],{"emptyLinePlaceholder":8},[955,8871,8872],{"class":957,"line":4223},[955,8873,4226],{"class":1415},[955,8875,8876],{"class":957,"line":4229},[955,8877,4232],{"class":1415},[955,8879,8880,8882,8884,8886,8888,8890,8892,8894],{"class":957,"line":4235},[955,8881,3690],{"class":969},[955,8883,944],{"class":965},[955,8885,4242],{"class":1010},[955,8887,1061],{"class":965},[955,8889,1088],{"class":978},[955,8891,4249],{"class":982},[955,8893,1088],{"class":978},[955,8895,1047],{"class":965},[955,8897,8898,8900],{"class":957,"line":4256},[955,8899,3577],{"class":969},[955,8901,1047],{"class":965},[955,8903,8904,8906],{"class":957,"line":4263},[955,8905,3584],{"class":969},[955,8907,1047],{"class":965},[955,8909,8910,8912],{"class":957,"line":4270},[955,8911,3591],{"class":969},[955,8913,1047],{"class":965},[955,8915,8916,8918],{"class":957,"line":4277},[955,8917,3598],{"class":969},[955,8919,1047],{"class":965},[955,8921,8922],{"class":957,"line":4284},[955,8923,4287],{"class":969},[955,8925,8926],{"class":957,"line":4290},[955,8927,1384],{"class":965},[955,8929,8930],{"class":957,"line":4295},[955,8931,992],{"emptyLinePlaceholder":8},[955,8933,8934],{"class":957,"line":4300},[955,8935,4303],{"class":1415},[955,8937,8938,8940,8942,8944,8946,8948,8950,8952],{"class":957,"line":4306},[955,8939,3690],{"class":969},[955,8941,944],{"class":965},[955,8943,3695],{"class":1010},[955,8945,1061],{"class":965},[955,8947,1088],{"class":978},[955,8949,4249],{"class":982},[955,8951,1088],{"class":978},[955,8953,1047],{"class":965},[955,8955,8956,8958,8960,8962,8964,8966],{"class":957,"line":4325},[955,8957,3570],{"class":1010},[955,8959,1061],{"class":965},[955,8961,1088],{"class":978},[955,8963,3717],{"class":982},[955,8965,1088],{"class":978},[955,8967,1072],{"class":965},[955,8969,8970,8972],{"class":957,"line":4340},[955,8971,3577],{"class":969},[955,8973,1047],{"class":965},[955,8975,8976,8978],{"class":957,"line":4347},[955,8977,3584],{"class":969},[955,8979,1047],{"class":965},[955,8981,8982,8984],{"class":957,"line":4354},[955,8983,3591],{"class":969},[955,8985,1047],{"class":965},[955,8987,8988,8990],{"class":957,"line":4361},[955,8989,3598],{"class":969},[955,8991,1047],{"class":965},[955,8993,8994,8996,8998,9000,9002,9004,9006,9008,9010,9012],{"class":957,"line":4368},[955,8995,3752],{"class":969},[955,8997,944],{"class":965},[955,8999,5],{"class":1010},[955,9001,3127],{"class":965},[955,9003,3761],{"class":969},[955,9005,1020],{"class":1036},[955,9007,979],{"class":978},[955,9009,3768],{"class":982},[955,9011,1088],{"class":978},[955,9013,3773],{"class":965},[955,9015,9016,9018],{"class":957,"line":4391},[955,9017,3605],{"class":969},[955,9019,1047],{"class":965},[955,9021,9022,9024],{"class":957,"line":4398},[955,9023,4401],{"class":969},[955,9025,1047],{"class":965},[955,9027,9028,9030,9032,9034,9036,9038,9040,9042],{"class":957,"line":4406},[955,9029,3779],{"class":998},[955,9031,1396],{"class":965},[955,9033,2898],{"class":2897},[955,9035,872],{"class":965},[955,9037,2415],{"class":2897},[955,9039,1584],{"class":965},[955,9041,3792],{"class":998},[955,9043,3565],{"class":965},[955,9045,9046],{"class":957,"line":4425},[955,9047,4428],{"class":1415},[955,9049,9050,9052,9054,9056,9058,9060,9062,9064,9066,9068],{"class":957,"line":4431},[955,9051,3800],{"class":998},[955,9053,966],{"class":965},[955,9055,884],{"class":1002},[955,9057,872],{"class":965},[955,9059,1214],{"class":1002},[955,9061,972],{"class":965},[955,9063,1452],{"class":1006},[955,9065,2956],{"class":969},[955,9067,944],{"class":965},[955,9069,4452],{"class":969},[955,9071,9072],{"class":957,"line":4455},[955,9073,992],{"emptyLinePlaceholder":8},[955,9075,9076],{"class":957,"line":4460},[955,9077,4463],{"class":1415},[955,9079,9080,9082,9084,9086,9088,9090,9092,9094,9096,9098,9100,9102],{"class":957,"line":4466},[955,9081,3401],{"class":961},[955,9083,4471],{"class":1010},[955,9085,1061],{"class":965},[955,9087,4476],{"class":1010},[955,9089,1061],{"class":965},[955,9091,2898],{"class":969},[955,9093,944],{"class":965},[955,9095,1064],{"class":969},[955,9097,1399],{"class":1006},[955,9099,944],{"class":965},[955,9101,2930],{"class":969},[955,9103,3436],{"class":965},[955,9105,9106],{"class":957,"line":4495},[955,9107,992],{"emptyLinePlaceholder":8},[955,9109,9110,9112,9114,9116,9118,9120,9122,9124,9126,9128,9130,9132,9134,9136,9138],{"class":957,"line":4500},[955,9111,3106],{"class":961},[955,9113,3109],{"class":969},[955,9115,944],{"class":965},[955,9117,5],{"class":1010},[955,9119,3127],{"class":965},[955,9121,4077],{"class":969},[955,9123,1020],{"class":1036},[955,9125,3481],{"class":3480},[955,9127,872],{"class":965},[955,9129,3185],{"class":969},[955,9131,1020],{"class":1036},[955,9133,979],{"class":978},[955,9135,4527],{"class":982},[955,9137,1088],{"class":978},[955,9139,3142],{"class":965},[955,9141,9142],{"class":957,"line":4534},[955,9143,3148],{"class":965},[955,9145,9146],{"class":957,"line":4539},[955,9147,1384],{"class":965},[856,9149,4544,9150,4547,9152,4551],{},[882,9151,2164],{},[882,9153,4550],{},[946,9155,9156],{"className":948,"code":4554,"language":950,"meta":951,"style":951},[882,9157,9158,9170,9182,9194,9206,9218],{"__ignoreMap":951},[955,9159,9160,9162,9164,9166,9168],{"class":957,"line":958},[955,9161,2898],{"class":969},[955,9163,944],{"class":965},[955,9165,4565],{"class":969},[955,9167,1007],{"class":1006},[955,9169,3565],{"class":965},[955,9171,9172,9174,9176,9178,9180],{"class":957,"line":989},[955,9173,1033],{"class":969},[955,9175,1020],{"class":1036},[955,9177,1024],{"class":969},[955,9179,4580],{"class":965},[955,9181,4583],{"class":1415},[955,9183,9184,9186,9188,9190,9192],{"class":957,"line":995},[955,9185,1053],{"class":969},[955,9187,1020],{"class":1036},[955,9189,1024],{"class":969},[955,9191,4580],{"class":965},[955,9193,4596],{"class":1415},[955,9195,9196,9198,9200,9202,9204],{"class":957,"line":1030},[955,9197,1078],{"class":969},[955,9199,1020],{"class":1036},[955,9201,1024],{"class":969},[955,9203,4580],{"class":965},[955,9205,4609],{"class":1415},[955,9207,9208,9210,9212,9214,9216],{"class":957,"line":1050},[955,9209,4614],{"class":965},[955,9211,1020],{"class":1036},[955,9213,1024],{"class":969},[955,9215,4621],{"class":965},[955,9217,4624],{"class":1415},[955,9219,9220],{"class":957,"line":1075},[955,9221,1426],{"class":965},[920,9223,4632],{"id":4631},[856,9225,4635,9226,4639,9228,4643],{},[882,9227,4638],{},[882,9229,4642],{},[946,9231,9232],{"className":948,"code":4646,"language":950,"meta":951,"style":951},[882,9233,9234,9260,9274,9278,9282,9302,9318,9338,9352,9366,9380,9384,9388,9392,9426,9456,9482,9520,9552,9568,9598,9602,9606,9610,9614,9638,9642,9658,9684,9688,9692,9696,9724,9728,9732,9736,9748],{"__ignoreMap":951},[955,9235,9236,9238,9240,9242,9244,9246,9248,9250,9252,9254,9256,9258],{"class":957,"line":958},[955,9237,962],{"class":961},[955,9239,966],{"class":965},[955,9241,4638],{"class":969},[955,9243,872],{"class":965},[955,9245,4642],{"class":969},[955,9247,872],{"class":965},[955,9249,1527],{"class":969},[955,9251,972],{"class":965},[955,9253,975],{"class":961},[955,9255,979],{"class":978},[955,9257,983],{"class":982},[955,9259,986],{"class":978},[955,9261,9262,9264,9266,9268,9270,9272],{"class":957,"line":989},[955,9263,962],{"class":961},[955,9265,1101],{"class":969},[955,9267,2756],{"class":961},[955,9269,979],{"class":978},[955,9271,3556],{"class":982},[955,9273,986],{"class":978},[955,9275,9276],{"class":957,"line":995},[955,9277,992],{"emptyLinePlaceholder":8},[955,9279,9280],{"class":957,"line":1030},[955,9281,4697],{"class":1415},[955,9283,9284,9286,9288,9290,9292,9294,9296,9298,9300],{"class":957,"line":1050},[955,9285,999],{"class":998},[955,9287,1003],{"class":1002},[955,9289,1007],{"class":1006},[955,9291,4708],{"class":1010},[955,9293,1014],{"class":965},[955,9295,4713],{"class":969},[955,9297,1020],{"class":1006},[955,9299,1024],{"class":1023},[955,9301,1027],{"class":965},[955,9303,9304,9306,9308,9310,9312,9314,9316],{"class":957,"line":1075},[955,9305,1033],{"class":969},[955,9307,1020],{"class":1036},[955,9309,1039],{"class":969},[955,9311,944],{"class":965},[955,9313,1044],{"class":969},[955,9315,4734],{"class":965},[955,9317,4737],{"class":1415},[955,9319,9320,9322,9324,9326,9328,9330,9332,9334,9336],{"class":957,"line":1093},[955,9321,1053],{"class":969},[955,9323,1020],{"class":1036},[955,9325,1058],{"class":1010},[955,9327,1061],{"class":965},[955,9329,1064],{"class":969},[955,9331,944],{"class":965},[955,9333,2930],{"class":969},[955,9335,4756],{"class":965},[955,9337,4737],{"class":1415},[955,9339,9340,9342,9344,9346,9348,9350],{"class":957,"line":1112},[955,9341,1078],{"class":969},[955,9343,1020],{"class":1036},[955,9345,979],{"class":978},[955,9347,4769],{"class":982},[955,9349,1088],{"class":978},[955,9351,1047],{"class":965},[955,9353,9354,9356,9358,9360,9362,9364],{"class":957,"line":1129},[955,9355,1096],{"class":969},[955,9357,1020],{"class":1036},[955,9359,1101],{"class":969},[955,9361,944],{"class":965},[955,9363,1106],{"class":1010},[955,9365,1109],{"class":965},[955,9367,9368,9370,9372,9374,9376,9378],{"class":957,"line":1433},[955,9369,4792],{"class":969},[955,9371,1020],{"class":1036},[955,9373,979],{"class":978},[955,9375,4799],{"class":982},[955,9377,1088],{"class":978},[955,9379,1047],{"class":965},[955,9381,9382],{"class":957,"line":3031},[955,9383,1132],{"class":965},[955,9385,9386],{"class":957,"line":3047},[955,9387,992],{"emptyLinePlaceholder":8},[955,9389,9390],{"class":957,"line":3064},[955,9391,4816],{"class":1415},[955,9393,9394,9396,9398,9400,9402,9404,9406,9408,9410,9412,9414,9416,9418,9420,9422,9424],{"class":957,"line":3070},[955,9395,999],{"class":998},[955,9397,4823],{"class":1002},[955,9399,1007],{"class":1006},[955,9401,3405],{"class":3404},[955,9403,4830],{"class":1010},[955,9405,1061],{"class":965},[955,9407,1088],{"class":978},[955,9409,4837],{"class":982},[955,9411,1088],{"class":978},[955,9413,872],{"class":965},[955,9415,4844],{"class":969},[955,9417,944],{"class":965},[955,9419,4849],{"class":969},[955,9421,944],{"class":965},[955,9423,4854],{"class":969},[955,9425,1384],{"class":965},[955,9427,9428,9430,9432,9434,9436,9438,9440,9442,9444,9446,9448,9450,9452,9454],{"class":957,"line":3076},[955,9429,4861],{"class":969},[955,9431,944],{"class":965},[955,9433,4866],{"class":969},[955,9435,944],{"class":965},[955,9437,4871],{"class":1010},[955,9439,1061],{"class":965},[955,9441,1088],{"class":978},[955,9443,891],{"class":982},[955,9445,1088],{"class":978},[955,9447,872],{"class":965},[955,9449,1064],{"class":969},[955,9451,944],{"class":965},[955,9453,1044],{"class":969},[955,9455,1384],{"class":965},[955,9457,9458,9460,9462,9464,9466,9468,9470,9472,9474,9476,9478,9480],{"class":957,"line":3081},[955,9459,4861],{"class":969},[955,9461,944],{"class":965},[955,9463,4866],{"class":969},[955,9465,944],{"class":965},[955,9467,4871],{"class":1010},[955,9469,1061],{"class":965},[955,9471,1088],{"class":978},[955,9473,894],{"class":982},[955,9475,1088],{"class":978},[955,9477,872],{"class":965},[955,9479,894],{"class":969},[955,9481,1384],{"class":965},[955,9483,9484,9486,9488,9490,9492,9494,9496,9498,9500,9502,9504,9506,9508,9510,9512,9514,9516,9518],{"class":957,"line":3103},[955,9485,4861],{"class":969},[955,9487,944],{"class":965},[955,9489,4866],{"class":969},[955,9491,944],{"class":965},[955,9493,4871],{"class":1010},[955,9495,1061],{"class":965},[955,9497,1088],{"class":978},[955,9499,898],{"class":982},[955,9501,1088],{"class":978},[955,9503,872],{"class":965},[955,9505,3556],{"class":969},[955,9507,944],{"class":965},[955,9509,4944],{"class":1010},[955,9511,1061],{"class":965},[955,9513,1088],{"class":978},[955,9515,4951],{"class":982},[955,9517,1088],{"class":978},[955,9519,1384],{"class":965},[955,9521,9522,9524,9526,9528,9530,9532,9534,9536,9538,9540,9542,9544,9546,9548,9550],{"class":957,"line":3145},[955,9523,4960],{"class":965},[955,9525,4963],{"class":1010},[955,9527,1061],{"class":965},[955,9529,3556],{"class":969},[955,9531,944],{"class":965},[955,9533,3812],{"class":1010},[955,9535,1061],{"class":965},[955,9537,3817],{"class":3119},[955,9539,1163],{"class":965},[955,9541,3822],{"class":1010},[955,9543,1061],{"class":965},[955,9545,1088],{"class":978},[955,9547,3829],{"class":982},[955,9549,1088],{"class":978},[955,9551,3436],{"class":965},[955,9553,9554,9556,9558,9560,9562,9564,9566],{"class":957,"line":3151},[955,9555,4960],{"class":965},[955,9557,4996],{"class":1010},[955,9559,1061],{"class":965},[955,9561,1088],{"class":978},[955,9563,3829],{"class":982},[955,9565,1088],{"class":978},[955,9567,3436],{"class":965},[955,9569,9570,9572,9574,9576,9578,9580,9582,9584,9586,9588,9590,9592,9594,9596],{"class":957,"line":3156},[955,9571,4861],{"class":969},[955,9573,944],{"class":965},[955,9575,4866],{"class":969},[955,9577,944],{"class":965},[955,9579,4871],{"class":1010},[955,9581,1061],{"class":965},[955,9583,1088],{"class":978},[955,9585,902],{"class":982},[955,9587,1088],{"class":978},[955,9589,872],{"class":965},[955,9591,1088],{"class":978},[955,9593,4769],{"class":982},[955,9595,1088],{"class":978},[955,9597,1384],{"class":965},[955,9599,9600],{"class":957,"line":3162},[955,9601,992],{"emptyLinePlaceholder":8},[955,9603,9604],{"class":957,"line":3199},[955,9605,5045],{"class":1415},[955,9607,9608],{"class":957,"line":3742},[955,9609,992],{"emptyLinePlaceholder":8},[955,9611,9612],{"class":957,"line":3749},[955,9613,5054],{"class":1415},[955,9615,9616,9618,9620,9622,9624,9626,9628,9630,9632,9634,9636],{"class":957,"line":3776},[955,9617,999],{"class":998},[955,9619,1361],{"class":1002},[955,9621,1007],{"class":1006},[955,9623,5065],{"class":1010},[955,9625,1014],{"class":965},[955,9627,4713],{"class":969},[955,9629,1020],{"class":1006},[955,9631,1024],{"class":1023},[955,9633,1378],{"class":965},[955,9635,894],{"class":969},[955,9637,1384],{"class":965},[955,9639,9640],{"class":957,"line":3797},[955,9641,992],{"emptyLinePlaceholder":8},[955,9643,9644,9646,9648,9650,9652,9654,9656],{"class":957,"line":3836},[955,9645,1393],{"class":961},[955,9647,1396],{"class":965},[955,9649,1399],{"class":1006},[955,9651,1402],{"class":969},[955,9653,944],{"class":965},[955,9655,1407],{"class":969},[955,9657,1410],{"class":965},[955,9659,9660,9662,9664,9666,9668,9670,9672,9674,9676,9678,9680,9682],{"class":957,"line":3841},[955,9661,5104],{"class":969},[955,9663,944],{"class":965},[955,9665,3130],{"class":1010},[955,9667,1061],{"class":965},[955,9669,1088],{"class":978},[955,9671,5115],{"class":982},[955,9673,1088],{"class":978},[955,9675,872],{"class":965},[955,9677,1402],{"class":969},[955,9679,944],{"class":965},[955,9681,5126],{"class":969},[955,9683,1384],{"class":965},[955,9685,9686],{"class":957,"line":3856},[955,9687,1421],{"class":961},[955,9689,9690],{"class":957,"line":3864},[955,9691,1426],{"class":965},[955,9693,9694],{"class":957,"line":3880},[955,9695,992],{"emptyLinePlaceholder":8},[955,9697,9698,9700,9702,9704,9706,9708,9710,9712,9714,9716,9718,9720,9722],{"class":957,"line":3926},[955,9699,999],{"class":998},[955,9701,966],{"class":965},[955,9703,1214],{"class":1002},[955,9705,872],{"class":965},[955,9707,884],{"class":1002},[955,9709,872],{"class":965},[955,9711,4713],{"class":1002},[955,9713,972],{"class":965},[955,9715,1452],{"class":1006},[955,9717,1361],{"class":969},[955,9719,944],{"class":965},[955,9721,5167],{"class":969},[955,9723,5170],{"class":1006},[955,9725,9726],{"class":957,"line":3945},[955,9727,5175],{"class":1415},[955,9729,9730],{"class":957,"line":3958},[955,9731,992],{"emptyLinePlaceholder":8},[955,9733,9734],{"class":957,"line":3966},[955,9735,5184],{"class":1415},[955,9737,9738,9740,9742,9744,9746],{"class":957,"line":3972},[955,9739,999],{"class":998},[955,9741,1548],{"class":1002},[955,9743,1007],{"class":1006},[955,9745,1553],{"class":1010},[955,9747,1556],{"class":965},[955,9749,9750,9752,9754,9756,9758,9760,9762],{"class":957,"line":4012},[955,9751,1485],{"class":969},[955,9753,944],{"class":965},[955,9755,1577],{"class":1010},[955,9757,1061],{"class":965},[955,9759,894],{"class":969},[955,9761,5211],{"class":965},[955,9763,5214],{"class":1415},[1472,9765,9766],{},[856,9767,5219,9768,872,9770,5224,9772,5227],{},[882,9769,1768],{},[882,9771,2144],{},[882,9773,2740],{},[2824,9775,9776],{},[856,9777,1204,9778,5234,9780,5238],{},[865,9779,1671],{"href":1670},[865,9781,5237],{"href":1670},[912,9783],{},[915,9785,1910],{"id":5243},[856,9787,5246,9788,5250,9791,5254],{},[865,9789,1924],{"href":1922,"rel":9790},[908],[865,9792,1918],{"href":1916,"rel":9793},[908],[1169,9795,9796,9806],{},[1172,9797,9798],{},[1175,9799,9800,9802,9804],{},[1178,9801,5263],{},[1178,9803,2281],{},[1178,9805,5268],{},[1188,9807,9808,9816],{},[1175,9809,9810,9812,9814],{},[1193,9811,5275],{},[1193,9813,5278],{},[1193,9815,5281],{},[1175,9817,9818,9820,9822],{},[1193,9819,5286],{},[1193,9821,1978],{},[1193,9823,5291],{},[856,9825,5294,9826,4639,9828,944],{},[882,9827,5297],{},[882,9829,5300],{},[912,9831],{},[915,9833,5306],{"id":5305},[856,9835,5309,9836,5312,9838,944],{},[882,9837,4849],{},[882,9839,5315],{},[920,9841,25],{"id":5318},[5320,9843,9844,9848,9857,9865,9871,9877],{},[5323,9845,9846],{"name":1479,"type":1201,":required":3223},[856,9847,5327],{},[5323,9849,9850],{"name":5330,"type":1201},[856,9851,5333,9852,5336,9854,5342],{},[882,9853,1162],{},[865,9855,5341],{"href":5339,"rel":9856},[908],[5323,9858,9859],{"name":5345,"type":5346},[856,9860,5333,9861,5352,9863,944],{},[882,9862,5351],{},[882,9864,5330],{},[5323,9866,9867],{"name":4854,"type":1201,":required":3223},[856,9868,5359,9869,5363],{},[882,9870,5362],{},[5323,9872,9873],{"name":5366,"type":5346},[856,9874,5333,9875,5372],{},[882,9876,5371],{},[5323,9878,9879],{"name":5375,"type":1201},[856,9880,5378],{},[920,9882,5382],{"id":5381},[856,9884,5385,9885,5388,9887,944],{},[882,9886,4854],{},[882,9888,5391],{},[5320,9890,9891,9897,9903],{},[5323,9892,9893],{"name":5396,"type":1201},[856,9894,5333,9895,5401],{},[882,9896,5391],{},[5323,9898,9899],{"name":5404,"type":1201},[856,9900,5333,9901,5409],{},[882,9902,5391],{},[5323,9904,9905],{"name":5412,"type":1201},[856,9906,5333,9907,5417],{},[882,9908,5391],{},[920,9910,5421],{"id":5420},[856,9912,5424],{},[5320,9914,9915,9921,9927,9933,9939,9945],{},[5323,9916,9917],{"name":5429,"type":5346},[856,9918,5333,9919,5434],{},[882,9920,1852],{},[5323,9922,9923],{"name":5437,"type":5346},[856,9924,5333,9925,5442],{},[882,9926,2697],{},[5323,9928,9929],{"name":5445,"type":5346},[856,9930,5333,9931,5450],{},[882,9932,1852],{},[5323,9934,9935],{"name":5453,"type":5346},[856,9936,5333,9937,5458],{},[882,9938,2697],{},[5323,9940,9941],{"name":5461,"type":5346},[856,9942,5333,9943,5466],{},[882,9944,1852],{},[5323,9946,9947],{"name":5469,"type":5346},[856,9948,5333,9949,5474],{},[882,9950,2697],{},[920,9952,5478],{"id":5477},[5320,9954,9955,9959,9963,9967,9971,9975],{},[5323,9956,9957],{"name":5483,"type":1201},[856,9958,5486],{},[5323,9960,9961],{"name":5489,"type":1201},[856,9962,5492],{},[5323,9964,9965],{"name":5495,"type":1201},[856,9966,5498],{},[5323,9968,9969],{"name":5501,"type":1201},[856,9970,5504],{},[5323,9972,9973],{"name":5507,"type":1201},[856,9974,5510],{},[5323,9976,9977],{"name":5513,"type":1201},[856,9978,5516],{},[912,9980],{},[915,9982,2821],{"id":5521},[856,9984,5524],{},[1169,9986,9987,9999],{},[1172,9988,9989],{},[1175,9990,9991,9993,9995,9997],{},[1178,9992,1938],{},[1178,9994,1941],{},[1178,9996,2606],{},[1178,9998,1186],{},[1188,10000,10001,10017,10033,10047,10063,10079,10095,10109,10125],{},[1175,10002,10003,10007,10011,10015],{},[1193,10004,10005],{},[882,10006,1952],{},[1193,10008,10009],{},[882,10010,1957],{},[1193,10012,10013],{},[882,10014,2619],{},[1193,10016,5557],{},[1175,10018,10019,10023,10027,10031],{},[1193,10020,10021],{},[882,10022,1967],{},[1193,10024,10025],{},[882,10026,1957],{},[1193,10028,10029],{},[882,10030,2619],{},[1193,10032,5574],{},[1175,10034,10035,10039,10043,10045],{},[1193,10036,10037],{},[882,10038,1967],{},[1193,10040,10041],{},[882,10042,5585],{},[1193,10044,5588],{},[1193,10046,5591],{},[1175,10048,10049,10053,10057,10061],{},[1193,10050,10051],{},[882,10052,1952],{},[1193,10054,10055],{},[882,10056,2234],{},[1193,10058,10059],{},[882,10060,2164],{},[1193,10062,5608],{},[1175,10064,10065,10069,10073,10077],{},[1193,10066,10067],{},[882,10068,1967],{},[1193,10070,10071],{},[882,10072,2234],{},[1193,10074,10075],{},[882,10076,2164],{},[1193,10078,5625],{},[1175,10080,10081,10085,10089,10093],{},[1193,10082,10083],{},[882,10084,1967],{},[1193,10086,10087],{},[882,10088,2247],{},[1193,10090,10091],{},[882,10092,2164],{},[1193,10094,5642],{},[1175,10096,10097,10101,10105,10107],{},[1193,10098,10099],{},[882,10100,1967],{},[1193,10102,10103],{},[882,10104,2088],{},[1193,10106,1777],{},[1193,10108,5657],{},[1175,10110,10111,10115,10119,10123],{},[1193,10112,10113],{},[882,10114,1952],{},[1193,10116,10117],{},[882,10118,2102],{},[1193,10120,10121],{},[882,10122,2039],{},[1193,10124,5674],{},[1175,10126,10127,10131,10135,10139],{},[1193,10128,10129],{},[882,10130,1967],{},[1193,10132,10133],{},[882,10134,2102],{},[1193,10136,10137],{},[882,10138,2039],{},[1193,10140,5691],{},[912,10142],{},[915,10144,5697],{"id":5696},[856,10146,5700,10147,5704,10149,5708,10151,5712],{},[882,10148,5703],{},[865,10150,147],{"href":5707},[882,10152,5711],{},[920,10154,5716],{"id":5715},[856,10156,5719,10157,4639,10159,5724],{},[882,10158,1768],{},[882,10160,2144],{},[1169,10162,10163,10179],{},[1172,10164,10165],{},[1175,10166,10167,10169,10171,10173,10175,10177],{},[1178,10168,1789],{},[1178,10170,1792],{},[1178,10172,1795],{},[1178,10174,1798],{},[1178,10176,1801],{},[1178,10178,2677],{},[1188,10180,10181,10199,10217,10235,10253],{},[1175,10182,10183,10185,10189,10191,10193,10195],{},[1193,10184,1808],{},[1193,10186,10187],{},[882,10188,1813],{},[1193,10190,1816],{},[1193,10192,1819],{},[1193,10194,1819],{},[1193,10196,10197],{},[882,10198,5763],{},[1175,10200,10201,10203,10207,10209,10211,10213],{},[1193,10202,1826],{},[1193,10204,10205],{},[882,10206,1831],{},[1193,10208,1834],{},[1193,10210,1819],{},[1193,10212,1839],{},[1193,10214,10215],{},[882,10216,5782],{},[1175,10218,10219,10221,10225,10227,10229,10231],{},[1193,10220,1844],{},[1193,10222,10223],{},[882,10224,1849],{},[1193,10226,1852],{},[1193,10228,1819],{},[1193,10230,1857],{},[1193,10232,10233],{},[882,10234,5801],{},[1175,10236,10237,10239,10243,10245,10247,10249],{},[1193,10238,5806],{},[1193,10240,10241],{},[882,10242,5811],{},[1193,10244,5814],{},[1193,10246,5817],{},[1193,10248,5820],{},[1193,10250,10251],{},[882,10252,5825],{},[1175,10254,10255,10257,10261,10263,10265,10267],{},[1193,10256,5830],{},[1193,10258,10259],{},[882,10260,5811],{},[1193,10262,1773],{},[1193,10264,5820],{},[1193,10266,5841],{},[1193,10268,10269],{},[882,10270,5825],{},[920,10272,5849],{"id":5848},[856,10274,5852,10275,5856,10277,944],{},[882,10276,5855],{},[882,10278,1997],{},[1169,10280,10281,10297],{},[1172,10282,10283],{},[1175,10284,10285,10287,10289,10291,10293,10295],{},[1178,10286,1789],{},[1178,10288,1792],{},[1178,10290,1795],{},[1178,10292,1798],{},[1178,10294,1801],{},[1178,10296,2677],{},[1188,10298,10299,10317,10335,10353,10371],{},[1175,10300,10301,10303,10307,10309,10311,10313],{},[1193,10302,1808],{},[1193,10304,10305],{},[882,10306,1813],{},[1193,10308,1816],{},[1193,10310,1819],{},[1193,10312,1819],{},[1193,10314,10315],{},[882,10316,5763],{},[1175,10318,10319,10321,10325,10327,10329,10331],{},[1193,10320,1844],{},[1193,10322,10323],{},[882,10324,1849],{},[1193,10326,1852],{},[1193,10328,1819],{},[1193,10330,1857],{},[1193,10332,10333],{},[882,10334,5915],{},[1175,10336,10337,10339,10343,10345,10347,10349],{},[1193,10338,5920],{},[1193,10340,10341],{},[882,10342,5925],{},[1193,10344,1852],{},[1193,10346,1819],{},[1193,10348,1857],{},[1193,10350,10351],{},[882,10352,5936],{},[1175,10354,10355,10357,10361,10363,10365,10367],{},[1193,10356,5806],{},[1193,10358,10359],{},[882,10360,5945],{},[1193,10362,5814],{},[1193,10364,5817],{},[1193,10366,5820],{},[1193,10368,10369],{},[882,10370,5956],{},[1175,10372,10373,10375,10379,10381,10383,10385],{},[1193,10374,5830],{},[1193,10376,10377],{},[882,10378,5945],{},[1193,10380,1773],{},[1193,10382,5820],{},[1193,10384,5841],{},[1193,10386,10387],{},[882,10388,5956],{},[920,10390,5978],{"id":5977},[856,10392,5719,10393,872,10395,876,10397,5987],{},[882,10394,2619],{},[882,10396,2039],{},[882,10398,2164],{},[1169,10400,10401,10417],{},[1172,10402,10403],{},[1175,10404,10405,10407,10409,10411,10413,10415],{},[1178,10406,1789],{},[1178,10408,1792],{},[1178,10410,1795],{},[1178,10412,1798],{},[1178,10414,1801],{},[1178,10416,2677],{},[1188,10418,10419,10437,10455],{},[1175,10420,10421,10423,10427,10429,10431,10433],{},[1193,10422,5806],{},[1193,10424,10425],{},[882,10426,1849],{},[1193,10428,6018],{},[1193,10430,5817],{},[1193,10432,5841],{},[1193,10434,10435],{},[882,10436,6027],{},[1175,10438,10439,10441,10445,10447,10449,10451],{},[1193,10440,5830],{},[1193,10442,10443],{},[882,10444,1849],{},[1193,10446,6038],{},[1193,10448,5820],{},[1193,10450,5820],{},[1193,10452,10453],{},[882,10454,6027],{},[1175,10456,10457,10459,10463,10465,10467,10469],{},[1193,10458,6051],{},[1193,10460,10461],{},[882,10462,6056],{},[1193,10464,6059],{},[1193,10466,6062],{},[1193,10468,6065],{},[1193,10470,6068],{},[856,10472,6071],{},[920,10474,6075],{"id":6074},[856,10476,5719,10477,6080],{},[882,10478,2188],{},[1169,10480,10481,10497],{},[1172,10482,10483],{},[1175,10484,10485,10487,10489,10491,10493,10495],{},[1178,10486,1789],{},[1178,10488,1792],{},[1178,10490,1795],{},[1178,10492,1798],{},[1178,10494,1801],{},[1178,10496,2677],{},[1188,10498,10499,10517,10535,10553],{},[1175,10500,10501,10503,10507,10509,10511,10513],{},[1193,10502,5806],{},[1193,10504,10505],{},[882,10506,1849],{},[1193,10508,5814],{},[1193,10510,5817],{},[1193,10512,5820],{},[1193,10514,10515],{},[882,10516,6119],{},[1175,10518,10519,10521,10525,10527,10529,10531],{},[1193,10520,5830],{},[1193,10522,10523],{},[882,10524,1849],{},[1193,10526,1852],{},[1193,10528,6132],{},[1193,10530,6132],{},[1193,10532,10533],{},[882,10534,6119],{},[1175,10536,10537,10539,10543,10545,10547,10549],{},[1193,10538,6143],{},[1193,10540,10541],{},[882,10542,6056],{},[1193,10544,5814],{},[1193,10546,5817],{},[1193,10548,5820],{},[1193,10550,10551],{},[882,10552,6119],{},[1175,10554,10555,10557,10561,10563,10565,10567],{},[1193,10556,6162],{},[1193,10558,10559],{},[882,10560,6167],{},[1193,10562,6170],{},[1193,10564,6132],{},[1193,10566,6132],{},[1193,10568,10569],{},[882,10570,6179],{},[856,10572,6182],{},[6184,10574,6186],{},{"title":951,"searchDepth":989,"depth":989,"links":10576},[10577,10582,10585,10586,10587,10592,10597,10600,10607,10608,10614,10615],{"id":917,"depth":989,"text":918,"children":10578},[10579,10580,10581],{"id":922,"depth":995,"text":925},{"id":1166,"depth":995,"text":1167},{"id":1263,"depth":995,"text":1264},{"id":1285,"depth":989,"text":1286,"children":10583},[10584],{"id":1289,"depth":995,"text":1292},{"id":1485,"depth":989,"text":1486},{"id":1599,"depth":989,"text":1600},{"id":1677,"depth":989,"text":1678,"children":10588},[10589,10590,10591],{"id":1684,"depth":995,"text":1685},{"id":1977,"depth":995,"text":1978},{"id":2121,"depth":995,"text":2122},{"id":2255,"depth":989,"text":2256,"children":10593},[10594,10595,10596],{"id":2265,"depth":995,"text":2266},{"id":2334,"depth":995,"text":2335},{"id":2483,"depth":995,"text":2484},{"id":2549,"depth":989,"text":2550,"children":10598},[10599],{"id":2658,"depth":995,"text":2659},{"id":2730,"depth":989,"text":2731,"children":10601},[10602,10603,10604,10605,10606],{"id":2734,"depth":995,"text":2735},{"id":2842,"depth":995,"text":2843},{"id":3231,"depth":995,"text":1978},{"id":3520,"depth":995,"text":3521},{"id":4631,"depth":995,"text":4632},{"id":5243,"depth":989,"text":1910},{"id":5305,"depth":989,"text":5306,"children":10609},[10610,10611,10612,10613],{"id":5318,"depth":995,"text":25},{"id":5381,"depth":995,"text":5382},{"id":5420,"depth":995,"text":5421},{"id":5477,"depth":995,"text":5478},{"id":5521,"depth":989,"text":2821},{"id":5696,"depth":989,"text":5697,"children":10616},[10617,10618,10619,10620],{"id":5715,"depth":995,"text":5716},{"id":5848,"depth":995,"text":5849},{"id":5977,"depth":995,"text":5978},{"id":6074,"depth":995,"text":6075},{},{"title":115,"description":6233},1780436282044]