[{"data":1,"prerenderedAt":2323},["ShallowReactive",2],{"navigation":3,"\u002Fguides\u002Fupgrading-to-4":250,"\u002Fguides\u002Fupgrading-to-4-surround":2318},[4,43,120,143,219],{"title":5,"path":6,"stem":7,"children":8,"icon":42},"Get started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,12,17,22,27,32,37],{"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},"Pick your setup","\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",{"title":33,"path":34,"stem":35,"icon":36},"Password auth","\u002Fgetting-started\u002Fpassword-auth","1.getting-started\u002F6.password-auth","i-lucide-key-round",{"title":38,"path":39,"stem":40,"icon":41},"3.x facade","\u002Fgetting-started\u002Fcompatibility-facade","1.getting-started\u002F7.compatibility-facade","i-lucide-archive","i-lucide-rocket",{"title":44,"path":45,"stem":46,"children":47,"icon":119},"Guides","\u002Fguides","2.guides\u002F01.index",[48,50,55,60,65,70,74,79,84,89,94,99,104,109,114],{"title":44,"path":45,"stem":46,"icon":49},"i-lucide-list-checks",{"title":51,"path":52,"stem":53,"icon":54},"Register users","\u002Fguides\u002Fregistration","2.guides\u002F02.registration","i-lucide-user-plus",{"title":56,"path":57,"stem":58,"icon":59},"Sign users in","\u002Fguides\u002Fsign-in","2.guides\u002F03.sign-in","i-lucide-log-in",{"title":61,"path":62,"stem":63,"icon":64},"Sign users out","\u002Fguides\u002Fsign-out","2.guides\u002F04.sign-out","i-lucide-log-out",{"title":66,"path":67,"stem":68,"icon":69},"Turn on two-factor authentication","\u002Fguides\u002Ftwo-factor","2.guides\u002F05.two-factor","i-lucide-smartphone",{"title":71,"path":72,"stem":73,"icon":36},"Reset a forgotten password","\u002Fguides\u002Freset-password","2.guides\u002F06.reset-password",{"title":75,"path":76,"stem":77,"icon":78},"Change a user's email or password","\u002Fguides\u002Fmanage-account","2.guides\u002F07.manage-account","i-lucide-user-cog",{"title":80,"path":81,"stem":82,"icon":83},"Add, rename, and remove passkeys","\u002Fguides\u002Fmanage-passkeys","2.guides\u002F08.manage-passkeys","i-lucide-scan-face",{"title":85,"path":86,"stem":87,"icon":88},"Link and unlink GitHub or Google accounts","\u002Fguides\u002Flink-external-accounts","2.guides\u002F09.link-external-accounts","i-lucide-link",{"title":90,"path":91,"stem":92,"icon":93},"Require step-up before sensitive actions","\u002Fguides\u002Fstep-up","2.guides\u002F10.step-up","i-lucide-shield-check",{"title":95,"path":96,"stem":97,"icon":98},"Call the API from a browser","\u002Fguides\u002Fbrowser-clients","2.guides\u002F11.browser-clients","i-lucide-globe",{"title":100,"path":101,"stem":102,"icon":103},"Prepare for production","\u002Fguides\u002Fproduction","2.guides\u002F12.production","i-lucide-factory",{"title":105,"path":106,"stem":107,"icon":108},"Sign in with an emailed code","\u002Fguides\u002Femail-code","2.guides\u002F13.email-code","i-lucide-mail",{"title":110,"path":111,"stem":112,"icon":113},"Upgrade to 4.0","\u002Fguides\u002Fupgrading-to-4","2.guides\u002F14.upgrading-to-4","i-lucide-arrow-up-right",{"title":115,"path":116,"stem":117,"icon":118},"Run an app with no public sign-up","\u002Fguides\u002Finternal-app","2.guides\u002F15.internal-app","i-lucide-building-2","i-lucide-waypoints",{"title":121,"path":122,"stem":123,"children":124,"icon":142},"Composable endpoints","\u002Fcomposables","3.composables\u002F1.index",[125,128,132,137],{"title":126,"path":122,"stem":123,"icon":127},"How composition works","i-lucide-layout-grid",{"title":129,"path":130,"stem":131,"icon":49},"Composition requirements","\u002Fcomposables\u002Frequirements","3.composables\u002F2.requirements",{"title":133,"path":134,"stem":135,"icon":136},"Compose a custom auth stack","\u002Fcomposables\u002Frecipes","3.composables\u002F3.recipes","i-lucide-book-marked",{"title":138,"path":139,"stem":140,"icon":141},"Composition","\u002Fcomposables\u002Fcomposition","3.composables\u002F4.composition","i-lucide-table","i-lucide-blocks",{"title":144,"icon":145,"path":146,"stem":147,"children":148,"page":218},"Reference","i-lucide-book-open","\u002Fmodules","4.modules",[149,154,158,163,168,173,177,182,186,191,195,200,205,209,213],{"title":150,"path":151,"stem":152,"icon":153},"Endpoint reference","\u002Fmodules\u002Fendpoints","4.modules\u002F01.endpoints","i-lucide-route",{"title":155,"path":156,"stem":157,"icon":78},"Identity management module","\u002Fmodules\u002Fidentity-management","4.modules\u002F02.identity-management",{"title":159,"path":160,"stem":161,"icon":162},"Cookie sign-in module","\u002Fmodules\u002Fcookie-auth","4.modules\u002F03.cookie-auth","i-lucide-cookie",{"title":164,"path":165,"stem":166,"icon":167},"Identity bearer sign-in module","\u002Fmodules\u002Fbearer-auth","4.modules\u002F04.bearer-auth","i-lucide-key",{"title":169,"path":170,"stem":171,"icon":172},"JWT module","\u002Fmodules\u002Fjwt","4.modules\u002F05.jwt","i-lucide-fingerprint",{"title":174,"path":175,"stem":176,"icon":83},"Passkeys module","\u002Fmodules\u002Fpasskeys","4.modules\u002F06.passkeys",{"title":178,"path":179,"stem":180,"icon":181},"ReAuth module","\u002Fmodules\u002Freauth","4.modules\u002F07.reauth","i-lucide-shield-alert",{"title":183,"path":184,"stem":185,"icon":59},"External OAuth packages","\u002Fmodules\u002Fexternal-oauth","4.modules\u002F08.external-oauth",{"title":187,"path":188,"stem":189,"icon":190},"Configuration options","\u002Fmodules\u002Fconfiguration","4.modules\u002F09.configuration","i-lucide-settings",{"title":192,"path":193,"stem":194,"icon":93},"Antiforgery (CSRF) rules","\u002Fmodules\u002Fcsrf","4.modules\u002F10.csrf",{"title":196,"path":197,"stem":198,"icon":199},"Responses and errors","\u002Fmodules\u002Ferrors","4.modules\u002F11.errors","i-lucide-circle-alert",{"title":201,"path":202,"stem":203,"icon":204},"Rate-limit policies","\u002Fmodules\u002Frate-limits","4.modules\u002F12.rate-limits","i-lucide-gauge",{"title":206,"path":207,"stem":208,"icon":108},"Email-code module","\u002Fmodules\u002Femail-code","4.modules\u002F13.email-code",{"title":210,"path":211,"stem":212,"icon":59},"Sign-in completer","\u002Fmodules\u002Fsign-in-completer","4.modules\u002F14.sign-in-completer",{"title":214,"path":215,"stem":216,"icon":217},"Enumeration-safe responses","\u002Fmodules\u002Fenumeration","4.modules\u002F15.enumeration","i-lucide-eye-off",false,{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":218},"Concepts","i-lucide-lightbulb","\u002Fconcepts","5.concepts",[225,230,235,240,245],{"title":226,"path":227,"stem":228,"icon":229},"AuthEndpoints compared with other options","\u002Fconcepts\u002Fcompare","5.concepts\u002F1.compare","i-lucide-git-compare",{"title":231,"path":232,"stem":233,"icon":234},"Stock Identity endpoints vs AuthEndpoints","\u002Fconcepts\u002Fstock-identity-vs-authendpoints","5.concepts\u002F2.stock-identity-vs-authendpoints","i-lucide-columns-2",{"title":236,"path":237,"stem":238,"icon":239},"MapIdentityApi alternative","\u002Fconcepts\u002Fmapidentityapi-alternative","5.concepts\u002F3.mapidentityapi-alternative","i-lucide-arrow-left-right",{"title":241,"path":242,"stem":243,"icon":244},"Security model","\u002Fconcepts\u002Fsecurity-model","5.concepts\u002F4.security-model","i-lucide-shield",{"title":246,"path":247,"stem":248,"icon":249},"FAQ","\u002Fconcepts\u002Ffaq","5.concepts\u002F5.faq","i-lucide-circle-help",{"id":251,"title":110,"body":252,"description":2311,"extension":2312,"faq":218,"links":2313,"meta":2314,"navigation":2315,"path":111,"seo":2316,"stem":112,"__hash__":2317},"docs\u002F2.guides\u002F14.upgrading-to-4.md",{"type":253,"value":254,"toc":2280},"minimark",[255,264,285,290,319,323,331,374,387,391,394,405,439,482,486,496,526,530,537,571,576,589,593,602,639,643,646,655,659,673,682,820,838,845,858,876,887,903,916,952,956,963,980,984,1013,1045,1054,1081,1098,1204,1212,1216,1233,1275,1279,1290,1293,1296,1313,1320,1337,1363,1368,1380,1550,1596,1602,1614,1617,1631,1648,1654,1669,1672,1680,1684,1718,1755,1759,1766,1780,1806,1817,1821,1827,1835,1862,1877,1881,1888,1904,1908,1911,1931,1935,1938,1950,1953,1957,1973,2000,2116,2122,2126,2137,2161,2165,2173,2189,2197,2240,2248,2252,2260,2276],[256,257,258,259,263],"p",{},"This page lists every breaking change from 3.4.1 to 4.0.0 and its migration. A taken email on ",[260,261,262],"code",{},"POST \u002Fregister"," is not one of them.",[256,265,266,267,271,272,275,276,279,280,284],{},"New hosts use ",[268,269,33],"a",{"href":270},"\u002Fgetting-started\u002Fpassword-auth\u002F",". Existing hosts keep the ",[268,273,38],{"href":274},"\u002Fgetting-started\u002Fcompatibility-facade\u002F",". Which call to make is in ",[268,277,23],{"href":278},"\u002Fgetting-started\u002Fchoose-a-sign-in-stack\u002F",". Add and Map pairs are in the ",[268,281,283],{"href":282},"\u002Fcomposables\u002Fcomposition\u002F","composition table",". This page does not copy that table.",[286,287,289],"h2",{"id":288},"options-that-stay","Options that stay",[256,291,292,295,296,299,300,303,304,306,307,310,311,314,315,318],{},[260,293,294],{},"AddAuthEndpoints"," keeps the 3.4.1 options and the ",[260,297,298],{},"AuthEndpointsSignIn"," and ",[260,301,302],{},"EmailCode.Enabled"," names. Nothing to migrate there. ",[260,305,262],{}," already returned an empty ",[260,308,309],{},"200"," for a taken email in 3.4.1. There is no migration for ",[260,312,313],{},"DuplicateUserName"," or ",[260,316,317],{},"DuplicateEmail",".",[286,320,322],{"id":321},"mapauthendpoints-exceptions","MapAuthEndpoints exceptions",[256,324,325,299,327,330],{},[260,326,294],{},[260,328,329],{},"MapAuthEndpoints"," do not gain 4.0-only behaviour, except for three cases:",[332,333,334,360,371],"ul",{},[335,336,337,338,341,342,345,346,349,350,353,354,356,357,318],"li",{},"Opt-in body transport. A ",[260,339,340],{},"RefreshTokenTransport"," other than ",[260,343,344],{},"Cookie"," in ",[260,347,348],{},"Jwt.Configure"," is copied onto ",[260,351,352],{},"JwtSessionOptions",". The default ",[260,355,344],{}," does not overwrite an earlier ",[260,358,359],{},"Configure\u003CJwtSessionOptions>",[335,361,362,363,366,367,370],{},"Unknown-email password-hash verify on facade ",[260,364,365],{},"POST \u002Fcreate",". That handler calls ",[260,368,369],{},"DefaultAuthenticator",". Facade login does not run the unknown-email hash verify, so an unknown email can return faster than a wrong password.",[335,372,373],{},"An absolute refresh lifetime, and a lost concurrent refresh that revokes the family. Hashed tokens, rotation, reuse that revokes the family, the security-stamp bind, and logout revoke are unchanged from 3.4.1.",[256,375,376,377,379,380,383,384,386],{},"The shared JWT ",[260,378,365],{}," responses and the password ",[260,381,382],{},"\u002Flogin"," pipeline do not apply to ",[260,385,329],{},", apart from that unknown-email hash verify.",[286,388,390],{"id":389},"email-code-table-gains-purpose","Email-code table gains Purpose",[256,392,393],{},"Migration. Add an EF migration and apply it before you deploy. Existing rows default to sign-in.",[256,395,396,397,400,401,404],{},"Hosts that call ",[260,398,399],{},"UseEmailCodes()"," add an EF migration and apply it before deploying. The library does not ship a ",[260,402,403],{},"Migrations"," folder.",[256,406,407,408,411,412,415,416,419,420,423,424,427,428,431,432,435,436,438],{},"The model adds ",[260,409,410],{},"Purpose"," (",[260,413,414],{},"int",", not null, default ",[260,417,418],{},"0",") on ",[260,421,422],{},"AuthEndpoints.AuthEndpointsEmailCodes",". It drops the index on ",[260,425,426],{},"(EmailHash, CreatedAt)"," and adds ",[260,429,430],{},"(EmailHash, Purpose, CreatedAt)",", named ",[260,433,434],{},"IX_AuthEndpointsEmailCodes_EmailHash_Purpose_CreatedAt",". Existing rows default to sign-in (",[260,437,418],{},").",[440,441,446],"pre",{"className":442,"code":443,"language":444,"meta":445,"style":445},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","dotnet ef migrations add AuthEndpoints4 --project YourDataProject --startup-project YourHost\n","bash","",[260,447,448],{"__ignoreMap":445},[449,450,453,457,461,464,467,470,473,476,479],"span",{"class":451,"line":452},"line",1,[449,454,456],{"class":455},"sBMFI","dotnet",[449,458,460],{"class":459},"sfazB"," ef",[449,462,463],{"class":459}," migrations",[449,465,466],{"class":459}," add",[449,468,469],{"class":459}," AuthEndpoints4",[449,471,472],{"class":459}," --project",[449,474,475],{"class":459}," YourDataProject",[449,477,478],{"class":459}," --startup-project",[449,480,481],{"class":459}," YourHost\n",[286,483,485],{"id":484},"email-code-messages-include-reauth","Email-code messages include ReAuth",[256,487,488,489,345,492,495],{},"Migration. Handle ",[260,490,491],{},"EmailCodePurpose.ReAuth",[260,493,494],{},"IEmailCodeSender\u003CTUser>",". The message confirms identity. It is not a sign-in.",[256,497,498,501,502,411,505,508,509,511,512,514,515,518,519,411,522,525],{},[260,499,500],{},"EmailCodePurpose"," gains ",[260,503,504],{},"ReAuth",[260,506,507],{},"2","). ",[260,510,494],{}," implementations that switch on purpose handle ",[260,513,504],{},": the message is \"confirm it's you\", not a sign-in. ",[260,516,517],{},"LinkUrl"," is null. Sign-up stays ",[260,520,521],{},"SignUp",[260,523,524],{},"1","). Those rows are still stored as sign-in.",[286,527,529],{"id":528},"confirm-identity-accepts-an-email-code","Confirm identity accepts an email code",[256,531,532,533,536],{},"Migration. Match this ",[260,534,535],{},"400"," on status. Do not match the sentence.",[256,538,539,501,542,545,546,549,550,553,554,553,557,553,560,563,564,567,568,570],{},[260,540,541],{},"ConfirmIdentityRequest",[260,543,544],{},"EmailCode",". ",[260,547,548],{},"POST \u002FconfirmIdentity"," accepts exactly one of ",[260,551,552],{},"password",", ",[260,555,556],{},"twoFactorCode",[260,558,559],{},"twoFactorRecoveryCode",[260,561,562],{},"credentialJson",", or ",[260,565,566],{},"emailCode",". The ",[260,569,535],{}," body is:",[256,572,573],{},[260,574,575],{},"Provide exactly one of Password, TwoFactorCode, TwoFactorRecoveryCode, CredentialJson, or EmailCode.",[256,577,578,579,581,582,584,585,588],{},"Match that response on status. A password, authenticator, or passkey user who posts only ",[260,580,566],{}," gets ",[260,583,535],{}," with title ",[260,586,587],{},"EmailCodeProofUnavailable"," when step-up by email is not offered.",[286,590,592],{"id":591},"authmethods-adds-emailcode","authMethods adds emailCode",[256,594,595,596,598,599,318],{},"Migration. Add the ",[260,597,544],{}," argument where you construct ",[260,600,601],{},"AuthMethodsResponse",[256,603,604,501,606,609,610,612,613,299,616,619,620,622,623,625,626,628,629,545,632,635,636,638],{},[260,605,601],{},[260,607,608],{},"bool EmailCode"," as the last constructor argument. JSON responses include ",[260,611,566],{},". On ",[260,614,615],{},"MapAccountManagementEndpoints",[260,617,618],{},"MapReAuthEndpoints"," it is true when email-code step-up is registered and available for that user. The ",[260,621,294],{}," \u002F ",[260,624,329],{}," facade keeps ",[260,627,566],{}," false and does not map ",[260,630,631],{},"POST \u002FconfirmIdentity\u002FemailCode\u002Fsend",[260,633,634],{},"ReAuthEndpoints\u003CTUser>.AuthMethods(ClaimsPrincipal, UserManager\u003CTUser>)"," still forwards with ",[260,637,544],{}," false.",[286,640,642],{"id":641},"email-code-cooldown-and-send-cap-are-per-purpose","Email-code cooldown and send cap are per purpose",[256,644,645],{},"Migration. No host change beyond the Purpose column. A step-up send does not consume the sign-in cooldown or send cap.",[256,647,648,299,651,654],{},[260,649,650],{},"ResendCooldown",[260,652,653],{},"MaxSendsPerEmail"," apply per stored purpose. A step-up send does not supersede a sign-in code, and it does not consume that address's sign-in cooldown or send cap. The reverse is also true. No migration beyond the Purpose column.",[286,656,658],{"id":657},"per-method-sign-in-completers-are-obsolete","Per-method sign-in completers are obsolete",[256,660,661,662,665,666,669,670,318],{},"Migration. Implement ",[260,663,664],{},"ISignInCompleter\u003CTUser>"," and register it with ",[260,667,668],{},"AddSignInCompleter",". Pass that name to the map, or set ",[260,671,672],{},"SignInCompletionOptions.Default",[256,674,675,676,314,679,318],{},"Removed in 5.0. An explicitly registered 3.x completer still runs when the sign-in map does not pass ",[260,677,678],{},"session",[260,680,681],{},"completer",[683,684,685,698],"table",{},[686,687,688],"thead",{},[689,690,691,695],"tr",{},[692,693,694],"th",{},"3.x",[692,696,697],{},"4.0",[699,700,701,719,731,744,755,779,793],"tbody",{},[689,702,703,712],{},[704,705,706,622,709],"td",{},[260,707,708],{},"IEmailCodeSignInCompleter\u003CTUser>",[260,710,711],{},"EmailCodeSignInContext",[704,713,714,622,716],{},[260,715,664],{},[260,717,718],{},"SignInCompletionContext",[689,720,721,726],{},[704,722,723],{},[260,724,725],{},"AddEmailCodeSignInCompleter\u003CTUser, TCompleter>()",[704,727,728],{},[260,729,730],{},"AddSignInCompleter\u003CTUser, TCompleter>(name)",[689,732,733,741],{},[704,734,735,622,738],{},[260,736,737],{},"IPasskeySignInCompleter\u003CTUser>",[260,739,740],{},"PasskeySignInCompletionContext",[704,742,743],{},"same keyed completer",[689,745,746,751],{},[704,747,748],{},[260,749,750],{},"AddPasskeySignInCompleter\u003CTUser, TCompleter>()",[704,752,753],{},[260,754,730],{},[689,756,757,762],{},[704,758,759],{},[260,760,761],{},"IExternalLoginCompleter\u003CTUser>",[704,763,764,766,767,545,770,773,774,776,777],{},[260,765,664],{}," with ",[260,768,769],{},"SignInMethod.External",[260,771,772],{},"AddCompleter\u003CT>()"," still wins when the map passes no ",[260,775,678],{}," and no ",[260,778,681],{},[689,780,781,786],{},[704,782,783],{},[260,784,785],{},"ExternalAuthBuilder.AddCompleter\u003CTCompleter>()",[704,787,788,563,790],{},[260,789,730],{},[260,791,792],{},"MapExternalAuthEndpoints\u003CTUser>(session)",[689,794,795,815],{},[704,796,797,553,800,553,803,553,806,553,809,553,812],{},[260,798,799],{},"IdentityEmailCodeSignInCompleter",[260,801,802],{},"JwtEmailCodeSignInCompleter",[260,804,805],{},"IdentityPasskeySignInCompleter",[260,807,808],{},"JwtPasskeySignInCompleter",[260,810,811],{},"CookieExternalLoginCompleter",[260,813,814],{},"JwtExternalLoginCompleter",[704,816,817],{},[268,818,210],{"href":819},"\u002Fmodules\u002Fsign-in-completer\u002F",[256,821,822,823,545,825,773,827,776,829,545,831,766,834,837],{},"Sessions, the passkey string overload, password login, and a custom completer are on ",[268,824,210],{"href":819},[260,826,772],{},[260,828,678],{},[260,830,681],{},[260,832,833],{},"AuthEndpointsSession.Jwt",[260,835,836],{},"RefreshTokenTransport.Body"," throws at map time.",[286,839,841,842,844],{"id":840},"jwt-post-create-runs-the-shared-password-pipeline","JWT ",[260,843,365],{}," runs the shared password pipeline",[256,846,847,848,850,851,854,855,857],{},"Migration. Match ",[260,849,365],{}," failures on status and ",[260,852,853],{},"title",". This does not apply to ",[260,856,329],{},", except the unknown-email hash verify below.",[256,859,860,299,863,866,867,545,871,873,874,318],{},[260,861,862],{},"MapJwtPasswordEndpoints",[260,864,865],{},"MapJwtAuthEndpoints"," use the shared password pipeline. Status, title, and lockout are in ",[268,868,870],{"href":869},"\u002Fmodules\u002Fjwt\u002F#two-factor","JWT",[260,872,329],{}," keeps the 3.4.1 create handler. The shared password pipeline does not apply to ",[260,875,329],{},[256,877,878,879,881,882,884,885,370],{},"The unknown-email hash verify is the one change ",[260,880,329],{}," sees. It applies to facade ",[260,883,365],{}," because that handler calls ",[260,886,369],{},[286,888,890,891,893,894,622,897,622,900],{"id":889},"password-login-no-longer-calls-signinmanagerpasswordsigninasync-twofactorauthenticatorsigninasync-twofactorrecoverycodesigninasync","Password ",[260,892,382],{}," no longer calls ",[260,895,896],{},"SignInManager.PasswordSignInAsync",[260,898,899],{},"TwoFactorAuthenticatorSignInAsync",[260,901,902],{},"TwoFactorRecoveryCodeSignInAsync",[256,904,905,906,553,909,911,912,854,914,318],{},"Migration. Move sign-in side effects out of ",[260,907,908],{},"PasswordSignInAsync",[260,910,899],{},", and ",[260,913,902],{},[260,915,329],{},[256,917,918,299,921,924,925,928,929,932,933,936,937,940,941,553,944,911,947,949,950,370],{},[260,919,920],{},"MapPasswordEndpoints\u003CTUser>()",[260,922,923],{},"MapPasswordSignInEndpoints\u003CTUser>()"," use ",[260,926,927],{},"CheckPasswordSignInAsync"," and the completer. Responses and a custom ",[260,930,931],{},"SignInManager\u003CTUser>"," are in ",[268,934,210],{"href":935},"\u002Fmodules\u002Fsign-in-completer\u002F#password-login",". An explicit ",[260,938,939],{},"PasswordLoginMode"," uses the 3.4.1 handlers. ",[260,942,943],{},"MapCookieAuthEndpoints",[260,945,946],{},"MapBearerAuthEndpoints",[260,948,329],{}," do too. This login change does not apply to ",[260,951,329],{},[286,953,955],{"id":954},"mappasswordendpoints-overloads","MapPasswordEndpoints overloads",[256,957,958,959,962],{},"Migration. Recompile assemblies built against AuthEndpoints 3.4.x. Source that calls ",[260,960,961],{},"MapPasswordEndpoints"," still compiles.",[256,964,965,966,969,970,553,972,975,976,979],{},"3.4.x had one method, ",[260,967,968],{},"MapPasswordEndpoints\u003CTUser>(PasswordLoginMode login = PasswordLoginMode.Cookie, string? confirmEmailEndpointName = null)",". That optional-parameter method is now separate overloads: no arguments, ",[260,971,939],{},[260,973,974],{},"AuthEndpointsSession",", and a completer name. A binary built against 3.4.x passes ",[260,977,978],{},"PasswordLoginMode.Cookie"," into the call, so it binds to the mode overload.",[286,981,983],{"id":982},"per-session-and-shared-routes","Per-session and shared routes",[256,985,986,987,622,989,991,992,553,995,553,997,553,999,553,1001,553,1004,553,1006,911,1009,1012],{},"No breaking change for route paths. ",[260,988,294],{},[260,990,329],{}," stay on the 3.4.1 route set, including antiforgery and rate limits. ",[260,993,994],{},"MapIdentityManagementApi",[260,996,943],{},[260,998,946],{},[260,1000,865],{},[260,1002,1003],{},"MapPasskeyEndpoints",[260,1005,961],{},[260,1007,1008],{},"MapEmailCodeEndpoints",[260,1010,1011],{},"MapPasskeySignInEndpoints"," keep their names and routes.",[256,1014,1015,1017,1018,553,1021,553,1024,911,1027,1030,1031,299,1034,545,1037,1040,1041,1044],{},[260,1016,1011],{}," still maps ",[260,1019,1020],{},"requestOptions",[260,1022,1023],{},"register\u002Foptions",[260,1025,1026],{},"register",[260,1028,1029],{},"login",", and still ensures confirm-email. Hosts that want the split call ",[260,1032,1033],{},"MapPasskeySignUpEndpoints",[260,1035,1036],{},"MapPasskeyRegisterOptionsEndpoints",[260,1038,1039],{},"AddPasskeyEndpoints"," remains the full set and calls the finer ",[260,1042,1043],{},"Add*"," methods.",[256,1046,1047,1048,1051,1052,318],{},"Routes that finish in a completer are mapped once per session. The others are mapped once. Passkey ",[260,1049,1050],{},"POST \u002Fpasskeys\u002FrequestOptions"," finishes the ceremony on that session's login route, so it is mapped per session with login, not on the shared map. Add and Map pairs, routes, and scope are in the ",[268,1053,283],{"href":282},[256,1055,1056,1059,1060,1063,1064,545,1066,1069,1070,1073,1074,1077,1078,318],{},[260,1057,1058],{},"MapSignInEndpoints\u003CTUser>(AuthEndpointsSession)"," maps the per-session routes for modules that are registered, plus that session's routes. It logs a warning when no sign-in module is registered. Cookie and Identity bearer password login is ",[260,1061,1062],{},"POST \u002Flogin",". JWT password login is ",[260,1065,365],{},[260,1067,1068],{},"MapSharedAuthEndpoints\u003CTUser>(sharedPath)"," maps the shared routes once. Null ",[260,1071,1072],{},"sharedPath"," uses the current builder (the app root when called on the app). Each ",[260,1075,1076],{},"Map*"," returns ",[260,1079,1080],{},"IEndpointConventionBuilder",[256,1082,1083,1086,1087,1090,1091,1094,1095,1097],{},[260,1084,1085],{},"MapSignInEndpoints"," maps passkey register on the session prefix and does not map confirm-email. The register handler still writes the confirmation link with the shared confirm-email endpoint name. Mapping passkey register without ",[260,1088,1089],{},"MapSharedAuthEndpoints"," (or ",[260,1092,1093],{},"MapConfirmEmailEndpoints",") leaves that link pointing at a route that was never mapped. ",[260,1096,1033],{}," maps confirm-email itself when that name is new.",[440,1099,1103],{"className":1100,"code":1101,"language":1102,"meta":445,"style":445},"language-cs shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","app.MapGroup(\"\u002Fweb\").MapSignInEndpoints\u003CAppUser>(AuthEndpointsSession.Cookie);\napp.MapGroup(\"\u002Fapp\").MapSignInEndpoints\u003CAppUser>(AuthEndpointsSession.Jwt);\napp.MapSharedAuthEndpoints\u003CAppUser>();\n","cs",[260,1104,1105,1151,1188],{"__ignoreMap":445},[449,1106,1107,1111,1114,1118,1121,1124,1127,1129,1131,1133,1136,1139,1142,1144,1146,1148],{"class":451,"line":452},[449,1108,1110],{"class":1109},"sTEyZ","app",[449,1112,318],{"class":1113},"sMK4o",[449,1115,1117],{"class":1116},"s2Zo4","MapGroup",[449,1119,1120],{"class":1113},"(",[449,1122,1123],{"class":1113},"\"",[449,1125,1126],{"class":459},"\u002Fweb",[449,1128,1123],{"class":1113},[449,1130,438],{"class":1113},[449,1132,1085],{"class":1116},[449,1134,1135],{"class":1113},"\u003C",[449,1137,1138],{"class":455},"AppUser",[449,1140,1141],{"class":1113},">(",[449,1143,974],{"class":1109},[449,1145,318],{"class":1113},[449,1147,344],{"class":1109},[449,1149,1150],{"class":1113},");\n",[449,1152,1154,1156,1158,1160,1162,1164,1167,1169,1171,1173,1175,1177,1179,1181,1183,1186],{"class":451,"line":1153},2,[449,1155,1110],{"class":1109},[449,1157,318],{"class":1113},[449,1159,1117],{"class":1116},[449,1161,1120],{"class":1113},[449,1163,1123],{"class":1113},[449,1165,1166],{"class":459},"\u002Fapp",[449,1168,1123],{"class":1113},[449,1170,438],{"class":1113},[449,1172,1085],{"class":1116},[449,1174,1135],{"class":1113},[449,1176,1138],{"class":455},[449,1178,1141],{"class":1113},[449,1180,974],{"class":1109},[449,1182,318],{"class":1113},[449,1184,1185],{"class":1109},"Jwt",[449,1187,1150],{"class":1113},[449,1189,1191,1193,1195,1197,1199,1201],{"class":451,"line":1190},3,[449,1192,1110],{"class":1109},[449,1194,318],{"class":1113},[449,1196,1089],{"class":1116},[449,1198,1135],{"class":1113},[449,1200,1138],{"class":455},[449,1202,1203],{"class":1113},">();\n",[256,1205,1206,1207,314,1209,1211],{},"Confirm-email keeps one endpoint name, so it is mapped with the shared routes and not under each session prefix. Password sign-in (",[260,1208,1062],{},[260,1210,365],{},") records that password sign-in is mapped. Sign-up and recovery do not.",[286,1213,1215],{"id":1214},"set-a-password","Set a password",[256,1217,1218,1219,299,1221,1223,1224,545,1227,1229,1230,318],{},"No breaking change. ",[260,1220,615],{},[260,1222,994],{}," map ",[260,1225,1226],{},"POST \u002Fmanage\u002Fpassword",[260,1228,329],{}," leaves it unmapped. Changing a password stays on ",[260,1231,1232],{},"POST \u002Fmanage\u002Finfo",[256,1234,1235,1236,1239,1240,1243,1244,1246,1247,1250,1251,1253,1254,1257,1258,1260,1261,545,1264,299,1267,1270,1271,1274],{},"The new route sets a password when the account has none. It requires a sign-in, a CSRF token, and a ReAuth proof, and it uses the ",[260,1237,1238],{},"AuthEndpoints.AccountAbuse"," rate limit. It returns ",[260,1241,1242],{},"404"," when password sign-in is not mapped, including for an anonymous caller. It returns ",[260,1245,535],{}," ",[260,1248,1249],{},"UserAlreadyHasPassword"," when a password already exists, and ",[260,1252,535],{}," with the Identity password-rule code when ",[260,1255,1256],{},"newPassword"," fails validation. The account still has no password after that failure. Success is an empty ",[260,1259,309],{},". Adding the password rotates the security stamp and calls ",[260,1262,1263],{},"IAccountSecurityEmailSender\u003CTUser>.SendPasswordSetAsync",[260,1265,1266],{},"AddAccountManagementEndpoints",[260,1268,1269],{},"AddJwtEndpoints"," register ",[260,1272,1273],{},"LoggingAccountSecurityEmailSender\u003CTUser>"," when no sender is registered. Startup logs a warning while that logging sender is still the registration. Register an implementation to deliver the notice. The password is not included. A cookie sign-in is refreshed so that browser session stays. Other sessions that check the stamp end.",[286,1276,1278],{"id":1277},"jwt-refresh-rotation-and-body-transport","JWT refresh rotation and body transport",[256,1280,1281,1282,1285,1286,1289],{},"Migration. Serialize refresh calls. Add the ",[260,1283,1284],{},"AbsoluteExpiresAt"," migration before you deploy. Set ",[260,1287,1288],{},"AbsoluteLifetime"," when a session should outlive 14 days.",[256,1291,1292],{},"Hashed refresh tokens, rotation, reuse that revokes the family, the security-stamp bind, and logout revoke are unchanged from 3.4.1. Presenting a refresh token that was already rotated still revokes every token in that family, including the successor.",[256,1294,1295],{},"A lost concurrent refresh is new. Two callers that present the same still-live token at the same time are both signed out. The request that loses the update revokes the family, including the successor the other request stored. Serialize refresh calls so only one request presents a given token.",[256,1297,1298,1299,1302,1303,1305,1306,1308,1309,1312],{},"Each family has an absolute lifetime, measured from the original sign-in. The default is 14 days, the same as ",[260,1300,1301],{},"JwtSessionOptions.RefreshTokenLifetime",". Rotation cannot extend it. A refresh after the cap returns ",[260,1304,535],{},". Set ",[260,1307,1288],{}," when a session should outlive that window. A successor then expires at the earlier of \"now plus ",[260,1310,1311],{},"RefreshTokenLifetime","\" and the family's absolute end.",[256,1314,1315,1316,1319],{},"The security-stamp bind is unchanged. A password change, reset, or ",[260,1317,1318],{},"UpdateSecurityStampAsync"," revokes the family. Logout revokes the family on the server. Deleting the cookie does not.",[256,1321,396,1322,1325,1326,1328,1329,1332,1333,1336],{},[260,1323,1324],{},"UseRefreshToken()"," add a migration and apply it before deploying. The model adds ",[260,1327,1284],{}," (non-null datetime) on ",[260,1330,1331],{},"AuthEndpoints.AuthEndpointsRefreshTokens",". Backfill existing rows from ",[260,1334,1335],{},"ExpiresAt"," so a stored token cannot outlive the expiry it already has.",[440,1338,1340],{"className":442,"code":1339,"language":444,"meta":445,"style":445},"dotnet ef migrations add AuthEndpointsRefreshAbsoluteLifetime --project YourDataProject --startup-project YourHost\n",[260,1341,1342],{"__ignoreMap":445},[449,1343,1344,1346,1348,1350,1352,1355,1357,1359,1361],{"class":451,"line":452},[449,1345,456],{"class":455},[449,1347,460],{"class":459},[449,1349,463],{"class":459},[449,1351,466],{"class":459},[449,1353,1354],{"class":459}," AuthEndpointsRefreshAbsoluteLifetime",[449,1356,472],{"class":459},[449,1358,475],{"class":459},[449,1360,478],{"class":459},[449,1362,481],{"class":459},[1364,1365,1367],"h3",{"id":1366},"refresh-token-in-the-json-body","Refresh token in the JSON body",[256,1369,1370,1373,1374,545,1376,1379],{},[260,1371,1372],{},"JwtSessionOptions.RefreshTokenTransport"," defaults to ",[260,1375,344],{},[260,1377,1378],{},"Body"," is for native and CLI clients.",[440,1381,1383],{"className":1100,"code":1382,"language":1102,"meta":445,"style":445},"builder.Services.AddJwtEndpoints\u003CAppUser, AppDbContext>(jwt =>\n{\n    jwt.Issuer = \"https:\u002F\u002Fexample.com\";\n    jwt.Audience = \"https:\u002F\u002Fexample.com\";\n    jwt.SigningOptions.SymmetricKey = key;\n});\nbuilder.Services.Configure\u003CJwtSessionOptions>(session =>\n{\n    session.RefreshTokenTransport = RefreshTokenTransport.Body;\n});\n",[260,1384,1385,1417,1422,1446,1466,1488,1494,1518,1523,1545],{"__ignoreMap":445},[449,1386,1387,1390,1392,1395,1397,1399,1401,1403,1406,1409,1411,1414],{"class":451,"line":452},[449,1388,1389],{"class":1109},"builder",[449,1391,318],{"class":1113},[449,1393,1394],{"class":1109},"Services",[449,1396,318],{"class":1113},[449,1398,1269],{"class":1116},[449,1400,1135],{"class":1113},[449,1402,1138],{"class":455},[449,1404,1405],{"class":1113},",",[449,1407,1408],{"class":455}," AppDbContext",[449,1410,1141],{"class":1113},[449,1412,1413],{"class":455},"jwt",[449,1415,1416],{"class":1113}," =>\n",[449,1418,1419],{"class":451,"line":1153},[449,1420,1421],{"class":1113},"{\n",[449,1423,1424,1427,1429,1432,1435,1438,1441,1443],{"class":451,"line":1190},[449,1425,1426],{"class":1109},"    jwt",[449,1428,318],{"class":1113},[449,1430,1431],{"class":1109},"Issuer ",[449,1433,1434],{"class":1113},"=",[449,1436,1437],{"class":1113}," \"",[449,1439,1440],{"class":459},"https:\u002F\u002Fexample.com",[449,1442,1123],{"class":1113},[449,1444,1445],{"class":1113},";\n",[449,1447,1449,1451,1453,1456,1458,1460,1462,1464],{"class":451,"line":1448},4,[449,1450,1426],{"class":1109},[449,1452,318],{"class":1113},[449,1454,1455],{"class":1109},"Audience ",[449,1457,1434],{"class":1113},[449,1459,1437],{"class":1113},[449,1461,1440],{"class":459},[449,1463,1123],{"class":1113},[449,1465,1445],{"class":1113},[449,1467,1469,1471,1473,1476,1478,1481,1483,1486],{"class":451,"line":1468},5,[449,1470,1426],{"class":1109},[449,1472,318],{"class":1113},[449,1474,1475],{"class":1109},"SigningOptions",[449,1477,318],{"class":1113},[449,1479,1480],{"class":1109},"SymmetricKey ",[449,1482,1434],{"class":1113},[449,1484,1485],{"class":1109}," key",[449,1487,1445],{"class":1113},[449,1489,1491],{"class":451,"line":1490},6,[449,1492,1493],{"class":1113},"});\n",[449,1495,1497,1499,1501,1503,1505,1508,1510,1512,1514,1516],{"class":451,"line":1496},7,[449,1498,1389],{"class":1109},[449,1500,318],{"class":1113},[449,1502,1394],{"class":1109},[449,1504,318],{"class":1113},[449,1506,1507],{"class":1116},"Configure",[449,1509,1135],{"class":1113},[449,1511,352],{"class":455},[449,1513,1141],{"class":1113},[449,1515,678],{"class":455},[449,1517,1416],{"class":1113},[449,1519,1521],{"class":451,"line":1520},8,[449,1522,1421],{"class":1113},[449,1524,1526,1529,1531,1534,1536,1539,1541,1543],{"class":451,"line":1525},9,[449,1527,1528],{"class":1109},"    session",[449,1530,318],{"class":1113},[449,1532,1533],{"class":1109},"RefreshTokenTransport ",[449,1535,1434],{"class":1113},[449,1537,1538],{"class":1109}," RefreshTokenTransport",[449,1540,318],{"class":1113},[449,1542,1378],{"class":1109},[449,1544,1445],{"class":1113},[449,1546,1548],{"class":451,"line":1547},10,[449,1549,1493],{"class":1113},[256,1551,1552,1553,1555,1556,1559,1560,1563,1564,545,1567,299,1569,1572,1573,1576,1577,1580,1581,1584,1585,1587,1588,1591,1592,1595],{},"In body mode, ",[260,1554,365],{},", email-code ",[260,1557,1558],{},"POST \u002FemailCode\u002Fverify",", passkey register and login, and ",[260,1561,1562],{},"POST \u002Frefresh"," include ",[260,1565,1566],{},"refreshToken",[260,1568,1562],{},[260,1570,1571],{},"POST \u002Flogout"," accept ",[260,1574,1575],{},"{ \"refreshToken\" }",". Those two routes do not require antiforgery, and ",[260,1578,1579],{},"GET \u002FcsrfToken"," is not mapped. Cookie mode keeps the HttpOnly cookie ",[260,1582,1583],{},"AuthEndpoints.Jwt.RefreshToken",", antiforgery on refresh and logout, and no ",[260,1586,1566],{}," field in the JSON. ",[260,1589,1590],{},"GET \u002Fverify"," stays ",[260,1593,1594],{},"204"," and does not return a refresh token.",[256,1597,1598,1601],{},[260,1599,1600],{},"IdentityBearer"," refresh tokens are unchanged. They are self-contained, have no reuse detection, and end only when the security stamp changes.",[256,1603,1604,1605,1607,1608,1610,1611,1613],{},"External browser sign-in still redirects and does not put a refresh token in the URL. The shared ",[260,1606,1185],{}," completer and ",[260,1609,814],{}," both refuse ",[260,1612,836],{}," before they create a refresh token. The shared completer throws when the external map runs. The legacy completer fails startup validation. Use cookie transport for that session.",[286,1615,214],{"id":1616},"enumeration-safe-responses",[256,1618,1619,1620,1622,1623,1625,1626,776,1628,1630],{},"No breaking change. 3.4.1 already answered ",[260,1621,262],{}," with an empty ",[260,1624,309],{}," when the email already had an account. The body has no ",[260,1627,313],{},[260,1629,317],{},". Keep the same check-your-email screen.",[256,1632,1633,1634,1637,1638,553,1641,911,1643,1270,1645,1647],{},"A valid password for an existing email does not create a second account. 4.0 notifies that owner through ",[260,1635,1636],{},"IAccountSecurityEmailSender\u003CTUser>.SendRegistrationAttemptedAsync",". The interface is new in 4.0. The attempted password is left out of the message. ",[260,1639,1640],{},"AddPasswordSignUpEndpoints",[260,1642,1266],{},[260,1644,1269],{},[260,1646,1273],{}," when no sender is registered. That default logs the attempt and does not send mail. Register an implementation to deliver it. Startup logs a warning while the logging sender is still the registration.",[256,1649,1650,1651,1653],{},"Invalid email and password-rule failures stay ",[260,1652,535],{},". Those bodies match whether or not the email is registered.",[256,1655,1656,553,1659,553,1662,911,1665,1668],{},[260,1657,1658],{},"POST \u002FforgotPassword",[260,1660,1661],{},"POST \u002FresendConfirmationEmail",[260,1663,1664],{},"POST \u002FemailCode\u002Fsend",[260,1666,1667],{},"POST \u002Fpasskeys\u002Fregister\u002Foptions"," return the same status, body shape, and headers for an email that has an account and an email that does not. The unknown-account branch runs the same class of work as the account branch: a password-reset token, a confirmation token, an email-code issue, or passkey creation options. Enumeration-safe responses are always on.",[256,1670,1671],{},"Queue confirmation, reset, and registration-attempt mail and return from the sender. A sender that waits on SMTP makes a known address slower than an unknown one.",[256,1673,1674,1676,1677,1679],{},[260,1675,1661],{}," still ignores the email in the body and resends for the signed-in user. ",[260,1678,1658],{}," still sends a reset code only when the account exists and the email is confirmed.",[286,1681,1683],{"id":1682},"password-facade","Password facade",[256,1685,1218,1686,622,1689,1692,1693,622,1695,1697,1698,1700,1701,1703,1704,553,1707,911,1710,1713,1714,1717],{},[260,1687,1688],{},"AddPasswordAuth",[260,1690,1691],{},"MapPasswordAuth"," are new. ",[260,1694,294],{},[260,1696,329],{}," stay the 3.4.1 facade: same options, same ",[260,1699,298],{}," values, same ",[260,1702,302],{}," switch, and the same ",[260,1705,1706],{},"\u002Fidentity",[260,1708,1709],{},"\u002Faccount",[260,1711,1712],{},"\u002Fauth"," routes. They are not obsolete. The exceptions are in ",[268,1715,322],{"href":1716},"#mapauthendpoints-exceptions",". There is no multi-session facade and no other scenario facade in 4.0.",[256,1719,1720,1721,299,1723,1725,1726,1729,1730,299,1732,1734,1735,1729,1737,1739,1740,299,1742,1744,1745,1747,1748,1751,1752,1754],{},"New hosts that want one password session call ",[260,1722,1688],{},[260,1724,1691],{},". Cookie is the default. JWT is allowed. Identity bearer is not. Startup validation fails when ",[260,1727,1728],{},"Session"," is ",[260,1731,1185],{},[260,1733,1185],{}," is null, when ",[260,1736,1728],{},[260,1738,1600],{},", and when the host calls both ",[260,1741,1688],{},[260,1743,294],{},". The call and the building-block expansion are on ",[268,1746,33],{"href":270},". Option defaults are in ",[268,1749,187],{"href":1750},"\u002Fmodules\u002Fconfiguration\u002F#passwordauthoptions",". The facade maps ",[260,1753,1226],{},". It does not map email-code sign-in or external sign-in.",[286,1756,1758],{"id":1757},"external-oauth-types-live-in-authendpointsdll","External OAuth types live in AuthEndpoints.dll",[256,1760,1761,1762,1765],{},"Migration. Upgrade the GitHub and Google packages to 4.0.0. Remove ",[260,1763,1764],{},"AuthEndpoints.External.OAuth",", or upgrade it to the 4.0.0 shim.",[256,1767,1768,1769,1772,1773,1775,1776,1779],{},"The external endpoint types moved into ",[260,1770,1771],{},"AuthEndpoints.dll",". The namespace stays ",[260,1774,1764],{},". No ",[260,1777,1778],{},"using"," changes.",[256,1781,1782,1783,299,1786,1789,1790,1793,1794,1796,1797,1799,1800,1803,1804,318],{},"Upgrade ",[260,1784,1785],{},"AuthEndpoints.OAuth.GitHub",[260,1787,1788],{},"AuthEndpoints.OAuth.Google"," to 4.0.0. Those packages reference ",[260,1791,1792],{},"AuthEndpoints"," directly. Remove ",[260,1795,1764],{},", or upgrade it to the 4.0.0 forwarding shim. The shim assembly is still named ",[260,1798,1764],{},". It contains ",[260,1801,1802],{},"[TypeForwardedTo]"," for the public types and no implementation. Source that referenced the old package compiles against the shim, and the compiler emits references to ",[260,1805,1792],{},[256,1807,1808,1810,1811,1813,1814,318],{},[260,1809,1792],{},", the shim, and the GitHub and Google packages are 4.0.0. The shim and the provider packages depend on ",[260,1812,1792],{}," with the range ",[260,1815,1816],{},"[4.0.0, 5.0.0)",[286,1818,1820],{"id":1819},"externaloauth-below-400-fails-the-build-ae4000","External.OAuth below 4.0.0 fails the build (AE4000)",[256,1822,1823,1824,1826],{},"Migration. Remove every ",[260,1825,1764],{}," package below 4.0.0, or upgrade it to 4.0.0.",[256,1828,1829,1831,1832,1834],{},[260,1830,1764],{}," below 4.0.0 defines the external types itself and depends on ",[260,1833,1792],{}," with an open-ended minimum. Together with AuthEndpoints 4.0 that pair defines the same types twice (CS0433).",[256,1836,1837,1839,1840,1843,1844,1847,1848,1851,1852,563,1855,1858,1859,1861],{},[260,1838,1792],{}," ships ",[260,1841,1842],{},"buildTransitive\u002FAuthEndpoints.targets",". Restore and build fail with ",[260,1845,1846],{},"AE4000"," when a ",[260,1849,1850],{},"PackageReference",", a ",[260,1853,1854],{},"PackageVersion",[260,1856,1857],{},"project.assets.json"," resolves ",[260,1860,1764],{}," below 4.0.0. Remove the package reference, or upgrade it to 4.0.0.",[256,1863,1864,1865,1868,1869,1872,1873,1876],{},"The 4.0.0 shim warns ",[260,1866,1867],{},"AE4001",": ",[260,1870,1871],{},"AuthEndpoints.External.OAuth is merged into AuthEndpoints 4.0. Remove the package reference; no code changes are needed."," Hosts that treat warnings as errors can ",[260,1874,1875],{},"NoWarn"," AE4001 for this release. The shim is removed in 5.0.",[286,1878,1880],{"id":1879},"oauthticketchain-is-public","OAuthTicketChain is public",[256,1882,1883,1884,1887],{},"Migration. Call the public ",[260,1885,1886],{},"OAuthTicketChain"," API.",[256,1889,1890,1892,1893,1896,1897,1899,1900,1903],{},[260,1891,1886],{}," is public. ",[260,1894,1895],{},"InternalsVisibleTo"," for ",[260,1898,1764],{}," is gone. Adapter authors call ",[260,1901,1902],{},"OAuthTicketChain.Seal",". Code that reached it through the old friend assembly uses the public method.",[286,1905,1907],{"id":1906},"list-and-link-routes-use-authendpoints-sign-in-schemes","List and link routes use AuthEndpoints sign-in schemes",[256,1909,1910],{},"Migration. Standard Identity and JWT hosts do not change. A custom default scheme has to be one of the AuthEndpoints primary schemes.",[256,1912,1913,553,1916,911,1919,1922,1923,1926,1927,1930],{},[260,1914,1915],{},"GET \u002Flogins",[260,1917,1918],{},"GET \u002Flink\u002F{scheme}",[260,1920,1921],{},"GET \u002Flink\u002F{scheme}\u002Fcallback"," call ",[260,1924,1925],{},"RequireAuthEndpointsUser()",". The authorize data lists the AuthEndpoints primary schemes that are registered: the application cookie, Identity bearer, and JWT bearer. A host whose default scheme is something else no longer satisfies those routes. Standard Identity and JWT hosts are unchanged. ",[260,1928,1929],{},"DELETE \u002Flogins\u002F{loginProvider}\u002F{providerKey}"," already used that check, plus ReAuth and antiforgery.",[286,1932,1934],{"id":1933},"email-code-counts-as-a-sign-in-method","Email code counts as a sign-in method",[256,1936,1937],{},"Migration. No host change. Passkey delete and external unlink succeed when mapped email-code sign-in and a confirmed email are the remaining method.",[256,1939,1940,1941,1943,1944,1946,1947,318],{},"Passkey delete and external unlink keep at least one sign-in method. Methods are a password, each passkey, each external login, and email code. Email code counts only when email-code sign-in routes are mapped (",[260,1942,1558],{},") and the email is confirmed. Send-only mapping does not count. Refusal stays ",[260,1945,535],{}," problem ",[260,1948,1949],{},"last_signin_method",[256,1951,1952],{},"An external-only user with a confirmed email can unlink that login when email-code sign-in is mapped. The same user is refused when those routes are not mapped, until a password, a passkey, or another login remains.",[286,1954,1956],{"id":1955},"provider-packages-only-register-handlers","Provider packages only register handlers",[256,1958,1959,1960,299,1963,1966,1967,314,1970,318],{},"Migration. Delete ",[260,1961,1962],{},"MapGitHubAuthEndpoints",[260,1964,1965],{},"MapGoogleAuthEndpoints",". Map ",[260,1968,1969],{},"MapExternalAuthProvider",[260,1971,1972],{},"MapExternalAuthEndpoints",[256,1974,1975,299,1978,1981,1982,299,1985,1988,1989,553,1992,1995,1996,1999],{},[260,1976,1977],{},"MapGitHubAuthEndpoints\u003CTUser>",[260,1979,1980],{},"MapGoogleAuthEndpoints\u003CTUser>"," are removed. ",[260,1983,1984],{},"AddGitHub",[260,1986,1987],{},"AddGoogle"," stay. Routes are unchanged (",[260,1990,1991],{},"login\u002Fgithub",[260,1993,1994],{},"login\u002Fgoogle",", the callbacks, ",[260,1997,1998],{},"\u002Flogins",", and the link routes). Map them from the core:",[440,2001,2003],{"className":1100,"code":2002,"language":1102,"meta":445,"style":445},"var external = app.MapGroup(\"\u002Fauth\u002Fexternal\");\nexternal.MapExternalAuthProvider\u003CAppUser>(\"GitHub\");\nexternal.MapExternalAuthProvider\u003CAppUser>(\"Google\");\nexternal.MapExternalAccountEndpoints\u003CAppUser>();\n\u002F\u002F or every registered provider:\nexternal.MapExternalAuthEndpoints\u003CAppUser>();\n",[260,2004,2005,2034,2058,2081,2096,2102],{"__ignoreMap":445},[449,2006,2007,2010,2013,2016,2019,2021,2023,2025,2027,2030,2032],{"class":451,"line":452},[449,2008,2009],{"class":455},"var",[449,2011,2012],{"class":455}," external",[449,2014,2015],{"class":1113}," =",[449,2017,2018],{"class":1109}," app",[449,2020,318],{"class":1113},[449,2022,1117],{"class":1116},[449,2024,1120],{"class":1113},[449,2026,1123],{"class":1113},[449,2028,2029],{"class":459},"\u002Fauth\u002Fexternal",[449,2031,1123],{"class":1113},[449,2033,1150],{"class":1113},[449,2035,2036,2039,2041,2043,2045,2047,2049,2051,2054,2056],{"class":451,"line":1153},[449,2037,2038],{"class":1109},"external",[449,2040,318],{"class":1113},[449,2042,1969],{"class":1116},[449,2044,1135],{"class":1113},[449,2046,1138],{"class":455},[449,2048,1141],{"class":1113},[449,2050,1123],{"class":1113},[449,2052,2053],{"class":459},"GitHub",[449,2055,1123],{"class":1113},[449,2057,1150],{"class":1113},[449,2059,2060,2062,2064,2066,2068,2070,2072,2074,2077,2079],{"class":451,"line":1190},[449,2061,2038],{"class":1109},[449,2063,318],{"class":1113},[449,2065,1969],{"class":1116},[449,2067,1135],{"class":1113},[449,2069,1138],{"class":455},[449,2071,1141],{"class":1113},[449,2073,1123],{"class":1113},[449,2075,2076],{"class":459},"Google",[449,2078,1123],{"class":1113},[449,2080,1150],{"class":1113},[449,2082,2083,2085,2087,2090,2092,2094],{"class":451,"line":1448},[449,2084,2038],{"class":1109},[449,2086,318],{"class":1113},[449,2088,2089],{"class":1116},"MapExternalAccountEndpoints",[449,2091,1135],{"class":1113},[449,2093,1138],{"class":455},[449,2095,1203],{"class":1113},[449,2097,2098],{"class":451,"line":1468},[449,2099,2101],{"class":2100},"sHwdD","\u002F\u002F or every registered provider:\n",[449,2103,2104,2106,2108,2110,2112,2114],{"class":451,"line":1490},[449,2105,2038],{"class":1109},[449,2107,318],{"class":1113},[449,2109,1972],{"class":1116},[449,2111,1135],{"class":1113},[449,2113,1138],{"class":455},[449,2115,1203],{"class":1113},[256,2117,2118,2119,2121],{},"Pass ",[260,2120,974],{}," or a completer name when the callback should finish as cookie, Identity bearer, or JWT. The default is the cookie completer. There is no two-factor step.",[286,2123,2125],{"id":2124},"compatibility-composites","Compatibility composites",[256,2127,2128,2129,2131,2132,299,2134,2136],{},"The 3.x composite names, signatures, and routes stay in 4.0. ",[260,2130,939],{}," stays. ",[260,2133,294],{},[260,2135,329],{}," stay the 3.4.1 facade.",[256,2138,2139,2140,299,2142,2144,2145,545,2147,2149,2150,2152,2153,2155,2156,299,2158,2160],{},"What each composite maps, and which ",[260,2141,1043],{},[260,2143,1076],{}," pairs it contains, is in the ",[268,2146,283],{"href":282},[260,2148,329],{}," calls the preserving helpers, so password login, JWT ",[260,2151,365],{},", and email-code verify stay on the 3.4.1 handlers. ",[260,2154,1039],{}," registers the password, cookie-session, and account-management modules, so ",[260,2157,994],{},[260,2159,943],{}," can follow that single call.",[286,2162,2164],{"id":2163},"a-missing-add-throws-at-map-time","A missing Add* throws at map time",[256,2166,2167,2168,2170,2171,318],{},"Migration. Call the ",[260,2169,1043],{}," named in the exception before the ",[260,2172,1076],{},[256,2174,2175,2176,2178,2179,2182,2183,2185,2186],{},"Every public ",[260,2177,1076],{}," throws ",[260,2180,2181],{},"InvalidOperationException"," when its ",[260,2184,1043],{}," was not called. The message names the method to call, for example ",[260,2187,2188],{},"Call AddPasswordEndpoints before MapPasswordEndpoints.",[256,2190,2191,2192,2194,2195,318],{},"A host that previously started and failed on the first request now fails while endpoints are mapped. Call the named ",[260,2193,1043],{}," before the ",[260,2196,1076],{},[256,2198,2199,2200,2202,2203,553,2206,553,2209,563,2212,545,2215,2217,2218,545,2221,2217,2223,545,2226,2217,2228,545,2230,2217,2232,2234,2235,2237,2238,318],{},"Passkey maps accept ",[260,2201,1039],{}," or the specific ",[260,2204,2205],{},"AddPasskeySignInEndpoints",[260,2207,2208],{},"AddPasskeySignUpEndpoints",[260,2210,2211],{},"AddPasskeyRegisterOptionsEndpoints",[260,2213,2214],{},"AddPasskeyManagementEndpoints",[260,2216,1093],{}," names ",[260,2219,2220],{},"AddAccountAbuseRateLimiting",[260,2222,618],{},[260,2224,2225],{},"AddReAuthScheme",[260,2227,329],{},[260,2229,294],{},[260,2231,1691],{},[260,2233,1688],{},". Selecting the ",[260,2236,1185],{}," completer names ",[260,2239,1269],{},[256,2241,2242,2244,2245,2247],{},[260,2243,1085],{}," still logs a warning when no sign-in module is registered, then maps that session's routes. Those session routes throw when their ",[260,2246,1043],{}," is missing.",[286,2249,2251],{"id":2250},"account-abuse-and-confirm-identity-rate-limits-are-public","Account-abuse and confirm-identity rate limits are public",[256,2253,2254,2255,2257,2258,318],{},"Migration. No host change. Call ",[260,2256,2220],{}," when the only map is ",[260,2259,1093],{},[256,2261,2262,299,2264,2267,2268,2270,2271,2273,2274,318],{},[260,2263,2220],{},[260,2265,2266],{},"AddConfirmIdentityRateLimiting"," are public. This is additive. Broader ",[260,2269,1043],{}," methods still register the policies. Call ",[260,2272,2220],{}," directly when the only map is ",[260,2275,1093],{},[2277,2278,2279],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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);}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 .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":445,"searchDepth":452,"depth":1153,"links":2281},[2282,2283,2284,2285,2286,2287,2288,2289,2290,2292,2294,2295,2296,2297,2300,2301,2302,2303,2304,2305,2306,2307,2308,2309,2310],{"id":288,"depth":1153,"text":289},{"id":321,"depth":1153,"text":322},{"id":389,"depth":1153,"text":390},{"id":484,"depth":1153,"text":485},{"id":528,"depth":1153,"text":529},{"id":591,"depth":1153,"text":592},{"id":641,"depth":1153,"text":642},{"id":657,"depth":1153,"text":658},{"id":840,"depth":1153,"text":2291},"JWT POST \u002Fcreate runs the shared password pipeline",{"id":889,"depth":1153,"text":2293},"Password \u002Flogin no longer calls SignInManager.PasswordSignInAsync \u002F TwoFactorAuthenticatorSignInAsync \u002F TwoFactorRecoveryCodeSignInAsync",{"id":954,"depth":1153,"text":955},{"id":982,"depth":1153,"text":983},{"id":1214,"depth":1153,"text":1215},{"id":1277,"depth":1153,"text":1278,"children":2298},[2299],{"id":1366,"depth":1190,"text":1367},{"id":1616,"depth":1153,"text":214},{"id":1682,"depth":1153,"text":1683},{"id":1757,"depth":1153,"text":1758},{"id":1819,"depth":1153,"text":1820},{"id":1879,"depth":1153,"text":1880},{"id":1906,"depth":1153,"text":1907},{"id":1933,"depth":1153,"text":1934},{"id":1955,"depth":1153,"text":1956},{"id":2124,"depth":1153,"text":2125},{"id":2163,"depth":1153,"text":2164},{"id":2250,"depth":1153,"text":2251},"Breaking changes from AuthEndpoints 3.x to 4.0, starting with the unified sign-in completer.","md",null,{},{"icon":113},{"title":110,"description":2311},"excEgvbt0OF34EAJr4MXGBG7vblKOALYa-MQSr5sgIU",[2319,2321],{"title":105,"path":106,"stem":107,"description":2320,"icon":108,"children":-1},"Send a one-time code, verify it, and optionally create an account or accept a magic-link token.",{"title":115,"path":116,"stem":117,"description":2322,"icon":118,"children":-1},"Map password sign-in, recovery, and account management without a public register route.",1792054256555]