[{"data":1,"prerenderedAt":630},["ShallowReactive",2],{"navigation":3,"\u002Fguides\u002Fstep-up":203,"\u002Fguides\u002Fstep-up-surround":625},[4,33,96,114,177],{"title":5,"path":6,"stem":7,"children":8,"icon":32},"Get started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,12,17,22,27],{"title":10,"path":6,"stem":7,"icon":11},"Introduction","i-lucide-house",{"title":13,"path":14,"stem":15,"icon":16},"Install AuthEndpoints","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":18,"path":19,"stem":20,"icon":21},"Quick start","\u002Fgetting-started\u002Fquick-start","1.getting-started\u002F3.quick-start","i-lucide-play",{"title":23,"path":24,"stem":25,"icon":26},"Choose a sign-in stack","\u002Fgetting-started\u002Fchoose-a-sign-in-stack","1.getting-started\u002F4.choose-a-sign-in-stack","i-lucide-signpost",{"title":28,"path":29,"stem":30,"icon":31},"Use the AuthEndpoints skill with coding agents","\u002Fgetting-started\u002Fai-agents","1.getting-started\u002F5.ai-agents","i-lucide-bot","i-lucide-rocket",{"title":34,"path":35,"stem":36,"children":37,"icon":95},"Guides","\u002Fguides","2.guides\u002F01.index",[38,40,45,50,55,60,65,70,75,80,85,90],{"title":34,"path":35,"stem":36,"icon":39},"i-lucide-list-checks",{"title":41,"path":42,"stem":43,"icon":44},"Register users","\u002Fguides\u002Fregistration","2.guides\u002F02.registration","i-lucide-user-plus",{"title":46,"path":47,"stem":48,"icon":49},"Sign users in","\u002Fguides\u002Fsign-in","2.guides\u002F03.sign-in","i-lucide-log-in",{"title":51,"path":52,"stem":53,"icon":54},"Sign users out","\u002Fguides\u002Fsign-out","2.guides\u002F04.sign-out","i-lucide-log-out",{"title":56,"path":57,"stem":58,"icon":59},"Turn on two-factor authentication","\u002Fguides\u002Ftwo-factor","2.guides\u002F05.two-factor","i-lucide-smartphone",{"title":61,"path":62,"stem":63,"icon":64},"Reset a forgotten password","\u002Fguides\u002Freset-password","2.guides\u002F06.reset-password","i-lucide-key-round",{"title":66,"path":67,"stem":68,"icon":69},"Change a user's email or password","\u002Fguides\u002Fmanage-account","2.guides\u002F07.manage-account","i-lucide-user-cog",{"title":71,"path":72,"stem":73,"icon":74},"Add, rename, and remove passkeys","\u002Fguides\u002Fmanage-passkeys","2.guides\u002F08.manage-passkeys","i-lucide-scan-face",{"title":76,"path":77,"stem":78,"icon":79},"Link and unlink GitHub or Google accounts","\u002Fguides\u002Flink-external-accounts","2.guides\u002F09.link-external-accounts","i-lucide-link",{"title":81,"path":82,"stem":83,"icon":84},"Require step-up before sensitive actions","\u002Fguides\u002Fstep-up","2.guides\u002F10.step-up","i-lucide-shield-check",{"title":86,"path":87,"stem":88,"icon":89},"Call the API from a browser","\u002Fguides\u002Fbrowser-clients","2.guides\u002F11.browser-clients","i-lucide-globe",{"title":91,"path":92,"stem":93,"icon":94},"Prepare for production","\u002Fguides\u002Fproduction","2.guides\u002F12.production","i-lucide-factory","i-lucide-waypoints",{"title":97,"path":98,"stem":99,"children":100,"icon":113},"Composable endpoints","\u002Fcomposables","3.composables\u002F1.index",[101,104,108],{"title":102,"path":98,"stem":99,"icon":103},"How composition works","i-lucide-layout-grid",{"title":105,"path":106,"stem":107,"icon":39},"Composition requirements","\u002Fcomposables\u002Frequirements","3.composables\u002F2.requirements",{"title":109,"path":110,"stem":111,"icon":112},"Compose a custom auth stack","\u002Fcomposables\u002Frecipes","3.composables\u002F3.recipes","i-lucide-book-marked","i-lucide-blocks",{"title":115,"icon":116,"path":117,"stem":118,"children":119,"page":176},"Reference","i-lucide-book-open","\u002Fmodules","4.modules",[120,125,129,134,139,144,148,153,157,162,166,171],{"title":121,"path":122,"stem":123,"icon":124},"Endpoint reference","\u002Fmodules\u002Fendpoints","4.modules\u002F01.endpoints","i-lucide-route",{"title":126,"path":127,"stem":128,"icon":69},"Identity management module","\u002Fmodules\u002Fidentity-management","4.modules\u002F02.identity-management",{"title":130,"path":131,"stem":132,"icon":133},"Cookie sign-in module","\u002Fmodules\u002Fcookie-auth","4.modules\u002F03.cookie-auth","i-lucide-cookie",{"title":135,"path":136,"stem":137,"icon":138},"Identity bearer sign-in module","\u002Fmodules\u002Fbearer-auth","4.modules\u002F04.bearer-auth","i-lucide-key",{"title":140,"path":141,"stem":142,"icon":143},"JWT module","\u002Fmodules\u002Fjwt","4.modules\u002F05.jwt","i-lucide-fingerprint",{"title":145,"path":146,"stem":147,"icon":74},"Passkeys module","\u002Fmodules\u002Fpasskeys","4.modules\u002F06.passkeys",{"title":149,"path":150,"stem":151,"icon":152},"ReAuth module","\u002Fmodules\u002Freauth","4.modules\u002F07.reauth","i-lucide-shield-alert",{"title":154,"path":155,"stem":156,"icon":49},"External OAuth packages","\u002Fmodules\u002Fexternal-oauth","4.modules\u002F08.external-oauth",{"title":158,"path":159,"stem":160,"icon":161},"Configuration options","\u002Fmodules\u002Fconfiguration","4.modules\u002F09.configuration","i-lucide-settings",{"title":163,"path":164,"stem":165,"icon":84},"Antiforgery (CSRF) rules","\u002Fmodules\u002Fcsrf","4.modules\u002F10.csrf",{"title":167,"path":168,"stem":169,"icon":170},"Responses and errors","\u002Fmodules\u002Ferrors","4.modules\u002F11.errors","i-lucide-circle-alert",{"title":172,"path":173,"stem":174,"icon":175},"Rate-limit policies","\u002Fmodules\u002Frate-limits","4.modules\u002F12.rate-limits","i-lucide-gauge",false,{"title":178,"icon":179,"path":180,"stem":181,"children":182,"page":176},"Concepts","i-lucide-lightbulb","\u002Fconcepts","5.concepts",[183,188,193,198],{"title":184,"path":185,"stem":186,"icon":187},"AuthEndpoints compared with other options","\u002Fconcepts\u002Fcompare","5.concepts\u002F1.compare","i-lucide-git-compare",{"title":189,"path":190,"stem":191,"icon":192},"Stock Identity endpoints vs AuthEndpoints","\u002Fconcepts\u002Fstock-identity-vs-authendpoints","5.concepts\u002F2.stock-identity-vs-authendpoints","i-lucide-columns-2",{"title":194,"path":195,"stem":196,"icon":197},"Security model","\u002Fconcepts\u002Fsecurity-model","5.concepts\u002F3.security-model","i-lucide-shield",{"title":199,"path":200,"stem":201,"icon":202},"FAQ","\u002Fconcepts\u002Ffaq","5.concepts\u002F4.faq","i-lucide-circle-help",{"id":204,"title":81,"body":205,"description":618,"extension":619,"links":620,"meta":621,"navigation":622,"path":82,"seo":623,"stem":83,"__hash__":624},"docs\u002F2.guides\u002F10.step-up.md",{"type":206,"value":207,"toc":612},"minimark",[208,229,232,262,287,292,390,393,411,504,509,513,524,586,590,608],[209,210,211,212,216,217,220,221,224,225,228],"p",{},"Some changes need a fresh proof of identity (ReAuth) even when the user is signed in. Browser clients receive the ",[213,214,215],"code",{},"AuthEndpoints.ReAuth"," cookie. API clients send the ",[213,218,219],{},"reauthToken"," from the confirm response in the ",[213,222,223],{},"X-AuthEndpoints-Reauth"," header. The cookie and the token share one lifetime: 5 minutes by default (",[213,226,227],{},"ReAuth.Lifetime",").",[209,230,231],{},"These actions require ReAuth:",[233,234,235,246,253,256],"ul",{},[236,237,238,241,242,245],"li",{},[213,239,240],{},"POST \u002Fidentity\u002Fmanage\u002F2fa"," and ",[213,243,244],{},"POST \u002Fidentity\u002Fmanage\u002Finfo",".",[236,247,248,249,252],{},"Passkey ",[213,250,251],{},"creationOptions",", add, rename, and delete.",[236,254,255],{},"External OAuth unlink.",[236,257,258,259,245],{},"Host endpoints that call ",[213,260,261],{},".RequireReauth()",[209,263,264,265,241,268,271,272,275,276,279,280,283,284],{},"The steps assume a signed-in cookie session. Both ",[213,266,267],{},"POST \u002Fidentity\u002FconfirmIdentity",[213,269,270],{},"POST \u002Fidentity\u002FconfirmIdentity\u002FpasskeyOptions"," require a CSRF token, because the facade maps them with management. Get the token from ",[213,273,274],{},"GET \u002Fidentity\u002FcsrfToken"," and send it in the ",[213,277,278],{},"RequestVerificationToken"," header. A missing token returns ",[213,281,282],{},"400"," with the body ",[213,285,286],{},"Invalid or missing CSRF token.",[288,289,291],"h2",{"id":290},"complete-step-up","Complete step-up",[293,294,295,308,318,349,355,383],"ol",{},[236,296,297,298,300,301,304,305,245],{},"Call the protected action, for example ",[213,299,244],{},". Without a ReAuth proof, the response is ",[213,302,303],{},"401"," or ",[213,306,307],{},"403",[236,309,310,311,314,315,245],{},"Send ",[213,312,313],{},"GET \u002Fidentity\u002Fmanage\u002FauthMethods"," to see which proofs the user has. The response is ",[213,316,317],{},"{ \"password\", \"authenticator\", \"recoveryCodes\", \"passkeys\", \"passkeyCount\" }",[236,319,320,321],{},"Collect exactly one proof:\n",[233,322,323,328,333,338],{},[236,324,325],{},[213,326,327],{},"{ \"password\": \"...\" }",[236,329,330],{},[213,331,332],{},"{ \"twoFactorCode\": \"...\" }",[236,334,335],{},[213,336,337],{},"{ \"twoFactorRecoveryCode\": \"...\" }",[236,339,340,343,344,245],{},[213,341,342],{},"{ \"credentialJson\": \"...\" }"," from a passkey. See ",[345,346,348],"a",{"href":347},"#use-a-passkey-as-the-proof","Use a passkey as the proof",[236,350,351,352,354],{},"Send the proof to ",[213,353,267],{}," with the CSRF header.",[236,356,357,358],{},"Check the response:\n",[233,359,360,373,378],{},[236,361,362,365,366,369,370,372],{},[213,363,364],{},"200 { \"reauthToken\" }"," with a ",[213,367,368],{},"Set-Cookie"," header for ",[213,371,215],{},": step-up succeeded.",[236,374,375,377],{},[213,376,282],{}," \"Provide exactly one of Password, TwoFactorCode, TwoFactorRecoveryCode, or CredentialJson.\": the body had zero proofs or more than one.",[236,379,380,382],{},[213,381,303],{},": the proof was wrong.",[236,384,385,386,389],{},"Retry the protected action. A browser sends the ReAuth cookie automatically. An API client adds the ",[213,387,388],{},"X-AuthEndpoints-Reauth: \u003CreauthToken>"," header.",[288,391,348],{"id":392},"use-a-passkey-as-the-proof",[293,394,395,400,406],{},[236,396,310,397,399],{},[213,398,270],{}," with the CSRF header. The options are for the signed-in user.",[236,401,402,403,245],{},"Call ",[213,404,405],{},"navigator.credentials.get",[236,407,408,409,245],{},"Send the credential to ",[213,410,267],{},[412,413,418],"pre",{"className":414,"code":415,"language":416,"meta":417,"style":417},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const headers = { 'Content-Type': 'application\u002Fjson', 'RequestVerificationToken': csrfToken };\n\nconst options = await fetch('\u002Fidentity\u002FconfirmIdentity\u002FpasskeyOptions', {\n  method: 'POST', credentials: 'include', headers\n}).then(r => r.json());\n\nconst credential = await navigator.credentials.get({\n  publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options)\n});\n\nawait fetch('\u002Fidentity\u002FconfirmIdentity', {\n  method: 'POST', credentials: 'include', headers,\n  body: JSON.stringify({ credentialJson: JSON.stringify(credential) })\n});\n","js","",[213,419,420,428,435,441,447,453,458,464,470,476,481,487,493,499],{"__ignoreMap":417},[421,422,425],"span",{"class":423,"line":424},"line",1,[421,426,427],{},"const headers = { 'Content-Type': 'application\u002Fjson', 'RequestVerificationToken': csrfToken };\n",[421,429,431],{"class":423,"line":430},2,[421,432,434],{"emptyLinePlaceholder":433},true,"\n",[421,436,438],{"class":423,"line":437},3,[421,439,440],{},"const options = await fetch('\u002Fidentity\u002FconfirmIdentity\u002FpasskeyOptions', {\n",[421,442,444],{"class":423,"line":443},4,[421,445,446],{},"  method: 'POST', credentials: 'include', headers\n",[421,448,450],{"class":423,"line":449},5,[421,451,452],{},"}).then(r => r.json());\n",[421,454,456],{"class":423,"line":455},6,[421,457,434],{"emptyLinePlaceholder":433},[421,459,461],{"class":423,"line":460},7,[421,462,463],{},"const credential = await navigator.credentials.get({\n",[421,465,467],{"class":423,"line":466},8,[421,468,469],{},"  publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options)\n",[421,471,473],{"class":423,"line":472},9,[421,474,475],{},"});\n",[421,477,479],{"class":423,"line":478},10,[421,480,434],{"emptyLinePlaceholder":433},[421,482,484],{"class":423,"line":483},11,[421,485,486],{},"await fetch('\u002Fidentity\u002FconfirmIdentity', {\n",[421,488,490],{"class":423,"line":489},12,[421,491,492],{},"  method: 'POST', credentials: 'include', headers,\n",[421,494,496],{"class":423,"line":495},13,[421,497,498],{},"  body: JSON.stringify({ credentialJson: JSON.stringify(credential) })\n",[421,500,502],{"class":423,"line":501},14,[421,503,475],{},[209,505,506,507,245],{},"A passkey that belongs to a different user returns ",[213,508,303],{},[288,510,512],{"id":511},"protect-your-own-endpoint","Protect your own endpoint",[209,514,402,515,517,518,304,521,245],{},[213,516,261],{}," on the endpoint. The facade registers the ReAuth schemes. A composed host calls ",[213,519,520],{},"AddCookieAuthEndpoints",[213,522,523],{},"AddBearerAuthEndpoints",[412,525,529],{"className":526,"code":527,"language":528,"meta":417,"style":417},"language-cs shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","app.MapPost(\"\u002Fbilling\u002Fupdate\", handler)\n    .RequireAuthorization()\n    .RequireReauth();\n","cs",[213,530,531,565,576],{"__ignoreMap":417},[421,532,533,537,540,544,547,550,554,556,559,562],{"class":423,"line":424},[421,534,536],{"class":535},"sTEyZ","app",[421,538,245],{"class":539},"sMK4o",[421,541,543],{"class":542},"s2Zo4","MapPost",[421,545,546],{"class":539},"(",[421,548,549],{"class":539},"\"",[421,551,553],{"class":552},"sfazB","\u002Fbilling\u002Fupdate",[421,555,549],{"class":539},[421,557,558],{"class":539},",",[421,560,561],{"class":535}," handler",[421,563,564],{"class":539},")\n",[421,566,567,570,573],{"class":423,"line":430},[421,568,569],{"class":539},"    .",[421,571,572],{"class":542},"RequireAuthorization",[421,574,575],{"class":539},"()\n",[421,577,578,580,583],{"class":423,"line":437},[421,579,569],{"class":539},[421,581,582],{"class":542},"RequireReauth",[421,584,585],{"class":539},"();\n",[288,587,589],{"id":588},"related","Related",[233,591,592,596,600,604],{},[236,593,594],{},[345,595,149],{"href":150},[236,597,598],{},[345,599,56],{"href":57},[236,601,602],{},[345,603,66],{"href":67},[236,605,606],{},[345,607,71],{"href":72},[609,610,611],"style",{},"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 .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":417,"searchDepth":424,"depth":430,"links":613},[614,615,616,617],{"id":290,"depth":430,"text":291},{"id":392,"depth":430,"text":348},{"id":511,"depth":430,"text":512},{"id":588,"depth":430,"text":589},"Ask a signed-in user to prove their identity again before a sensitive change, then retry the change.","md",null,{},{"icon":84},{"title":81,"description":618},"4QhRc7Qh6auLgFYiJbiPXbC9HqgqP0sk443WnyeJ1Dw",[626,628],{"title":76,"path":77,"stem":78,"description":627,"icon":79,"children":-1},"Let a signed-in user link a GitHub or Google login to their account, list linked logins, and unlink one.",{"title":86,"path":87,"stem":88,"description":629,"icon":89,"children":-1},"Send cookies and CSRF tokens from a browser client, and set up CORS when the client runs on another origin.",1791096171008]