[{"data":1,"prerenderedAt":631},["ShallowReactive",2],{"navigation":3,"\u002Fguides\u002Flink-external-accounts":203,"\u002Fguides\u002Flink-external-accounts-surround":626},[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":76,"body":205,"description":619,"extension":620,"links":621,"meta":622,"navigation":623,"path":77,"seo":624,"stem":78,"__hash__":625},"docs\u002F2.guides\u002F09.link-external-accounts.md",{"type":206,"value":207,"toc":612},"minimark",[208,221,226,235,337,353,448,452,483,512,515,518,527,530,587,591,608],[209,210,211,212,216,217,220],"p",{},"A signed-in user can add a GitHub or Google login to an existing account, then sign in with it later. These routes come from ",[213,214,215],"code",{},"MapExternalAccountEndpoints"," in the ",[213,218,219],{},"AuthEndpoints.External.OAuth"," preview package. The facade does not map them.",[222,223,225],"h2",{"id":224},"map-the-account-routes","Map the account routes",[209,227,228,229,234],{},"Register the providers as in ",[230,231,233],"a",{"href":232},"\u002Fguides\u002Fregistration#register-with-github-or-google","Register with GitHub or Google",". Then map the account routes on the same group as the sign-in routes:",[236,237,242],"pre",{"className":238,"code":239,"language":240,"meta":241,"style":241},"language-cs shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","var external = app.MapGroup(\"\u002Fauth\u002Fexternal\");\nexternal.MapGitHubAuthEndpoints\u003CAppUser>();\nexternal.MapGoogleAuthEndpoints\u003CAppUser>();\nexternal.MapExternalAccountEndpoints\u003CAppUser>();\n","cs","",[213,243,244,286,306,322],{"__ignoreMap":241},[245,246,249,253,256,260,264,267,271,274,277,281,283],"span",{"class":247,"line":248},"line",1,[245,250,252],{"class":251},"sBMFI","var",[245,254,255],{"class":251}," external",[245,257,259],{"class":258},"sMK4o"," =",[245,261,263],{"class":262},"sTEyZ"," app",[245,265,266],{"class":258},".",[245,268,270],{"class":269},"s2Zo4","MapGroup",[245,272,273],{"class":258},"(",[245,275,276],{"class":258},"\"",[245,278,280],{"class":279},"sfazB","\u002Fauth\u002Fexternal",[245,282,276],{"class":258},[245,284,285],{"class":258},");\n",[245,287,289,292,294,297,300,303],{"class":247,"line":288},2,[245,290,291],{"class":262},"external",[245,293,266],{"class":258},[245,295,296],{"class":269},"MapGitHubAuthEndpoints",[245,298,299],{"class":258},"\u003C",[245,301,302],{"class":251},"AppUser",[245,304,305],{"class":258},">();\n",[245,307,309,311,313,316,318,320],{"class":247,"line":308},3,[245,310,291],{"class":262},[245,312,266],{"class":258},[245,314,315],{"class":269},"MapGoogleAuthEndpoints",[245,317,299],{"class":258},[245,319,302],{"class":251},[245,321,305],{"class":258},[245,323,325,327,329,331,333,335],{"class":247,"line":324},4,[245,326,291],{"class":262},[245,328,266],{"class":258},[245,330,215],{"class":269},[245,332,299],{"class":258},[245,334,302],{"class":251},[245,336,305],{"class":258},[209,338,339,341,342,345,346,349,350,266],{},[213,340,215],{}," maps one ",[213,343,344],{},"link\u002F{scheme}"," route pair for each registered provider. The schemes are ",[213,347,348],{},"GitHub"," and ",[213,351,352],{},"Google",[354,355,356,378],"table",{},[357,358,359],"thead",{},[360,361,362,366,369,372,375],"tr",{},[363,364,365],"th",{},"Task",[363,367,368],{},"Endpoint",[363,370,371],{},"Needs a signed-in user",[363,373,374],{},"CSRF",[363,376,377],{},"ReAuth",[379,380,381,400,416,432],"tbody",{},[360,382,383,387,392,395,398],{},[384,385,386],"td",{},"Start a link",[384,388,389],{},[213,390,391],{},"GET \u002Fauth\u002Fexternal\u002Flink\u002F{scheme}?returnUrl=",[384,393,394],{},"Yes",[384,396,397],{},"No",[384,399,397],{},[360,401,402,405,410,412,414],{},[384,403,404],{},"Finish a link",[384,406,407],{},[213,408,409],{},"GET \u002Fauth\u002Fexternal\u002Flink\u002F{scheme}\u002Fcallback",[384,411,394],{},[384,413,397],{},[384,415,397],{},[360,417,418,421,426,428,430],{},[384,419,420],{},"List linked logins",[384,422,423],{},[213,424,425],{},"GET \u002Fauth\u002Fexternal\u002Flogins",[384,427,394],{},[384,429,397],{},[384,431,397],{},[360,433,434,437,442,444,446],{},[384,435,436],{},"Unlink a login",[384,438,439],{},[213,440,441],{},"DELETE \u002Fauth\u002Fexternal\u002Flogins\u002F{loginProvider}\u002F{providerKey}",[384,443,394],{},[384,445,394],{},[384,447,394],{},[222,449,451],{"id":450},"link-a-login","Link a login",[453,454,455,470,473],"ol",{},[456,457,458,459],"li",{},"While the user is signed in, send the browser to the link route with a top-level navigation:",[236,460,464],{"className":461,"code":462,"language":463,"meta":241,"style":241},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","window.location.assign('\u002Fauth\u002Fexternal\u002Flink\u002FGitHub?returnUrl=' + encodeURIComponent('\u002Fsettings\u002Flogins'));\n","js",[213,465,466],{"__ignoreMap":241},[245,467,468],{"class":247,"line":248},[245,469,462],{},[456,471,472],{},"The provider sends the user back to the callback. The callback checks that the provider login came from the same signed-in user.",[456,474,475,476,479,480,266],{},"On success, the callback links the login and returns a ",[213,477,478],{},"302"," to ",[213,481,482],{},"returnUrl",[209,484,485,486,489,490,493,494,497,498,501,502,501,505,508,509,266],{},"When ",[213,487,488],{},"RequireVerifiedEmail"," is ",[213,491,492],{},"true"," (the default), the provider email must be verified. A failed link redirects to ",[213,495,496],{},"ErrorPath?error=...&error_description=...",". The codes are ",[213,499,500],{},"provider_mismatch",", ",[213,503,504],{},"email_unverified",[213,506,507],{},"login_link_failed",", and ",[213,510,511],{},"external_login_info_missing",[209,513,514],{},"Linking does not compare the provider email with the account email. The user proves both identities by being signed in and completing the provider sign-in.",[222,516,420],{"id":517},"list-linked-logins",[209,519,520,521,523,524,266],{},"Send ",[213,522,425],{},". The response is an array of ",[213,525,526],{},"{ \"loginProvider\", \"providerKey\", \"providerDisplayName\" }",[222,528,436],{"id":529},"unlink-a-login",[453,531,532,537,543,553],{},[456,533,534,535,266],{},"Complete step-up. See ",[230,536,81],{"href":82},[456,538,539,540,266],{},"Get a CSRF token from ",[213,541,542],{},"GET \u002Fidentity\u002FcsrfToken",[456,544,520,545,548,549,552],{},[213,546,547],{},"DELETE \u002Fauth\u002Fexternal\u002Flogins\u002F\u003CloginProvider>\u002F\u003CproviderKey>"," with the ",[213,550,551],{},"RequestVerificationToken"," header. Take both values from the list response.",[456,554,555,556],{},"Check the response:\n",[557,558,559,565,571,577],"ul",{},[456,560,561,564],{},[213,562,563],{},"204",": the login is unlinked.",[456,566,567,570],{},[213,568,569],{},"401",": the user has no ReAuth proof.",[456,572,573,576],{},[213,574,575],{},"404",": the user has no such login.",[456,578,579,582,583,586],{},[213,580,581],{},"400"," with title ",[213,584,585],{},"last_signin_method",": the login is the last way to sign in. The user must keep a password, a passkey, or another external login.",[222,588,590],{"id":589},"related","Related",[557,592,593,599,603],{},[456,594,595],{},[230,596,598],{"href":597},"\u002Fguides\u002Fsign-in#sign-in-with-github-or-google","Sign in with GitHub or Google",[456,600,601],{},[230,602,154],{"href":155},[456,604,605],{},[230,606,167],{"href":607},"\u002Fmodules\u002Ferrors#external-oauth-errors",[609,610,611],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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}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);}",{"title":241,"searchDepth":248,"depth":288,"links":613},[614,615,616,617,618],{"id":224,"depth":288,"text":225},{"id":450,"depth":288,"text":451},{"id":517,"depth":288,"text":420},{"id":529,"depth":288,"text":436},{"id":589,"depth":288,"text":590},"Let a signed-in user link a GitHub or Google login to their account, list linked logins, and unlink one.","md",null,{},{"icon":79},{"title":76,"description":619},"B-xcLwLjzmcHOLurK17KwnDArASAwn161-Mxd2cjguw",[627,629],{"title":71,"path":72,"stem":73,"description":628,"icon":74,"children":-1},"Let a signed-in user add a passkey to their account, list passkeys, rename them, and remove them.",{"title":81,"path":82,"stem":83,"description":630,"icon":84,"children":-1},"Ask a signed-in user to prove their identity again before a sensitive change, then retry the change.",1791096170819]