[{"data":1,"prerenderedAt":328},["ShallowReactive",2],{"blog-post-\u002Fblog\u002Fauthendpoints-4-0":3,"blog-surround-\u002Fblog\u002Fauthendpoints-4-0":317},{"id":4,"title":5,"authors":6,"badge":12,"body":14,"date":300,"description":301,"extension":302,"image":303,"meta":304,"navigation":108,"path":312,"seo":313,"stem":315,"__hash__":316},"posts\u002Fblog\u002Fauthendpoints-4-0.md","AuthEndpoints 4.0: what changed",[7],{"name":8,"to":9,"avatar":10},"Made Y","\u002F",{"src":11},"\u002Fimages\u002Fprofile2.jpg",{"label":13},"Open Source",{"type":15,"value":16,"toc":294},"minimark",[17,21,58,63,73,77,91,122,128,139,149,152,164,175,179,182,206,231,242,246,282,290],[18,19,20],"p",{},"AuthEndpoints 4.0 is on NuGet. 4.0.0 shipped on 15 October 2026 and 4.0.1 followed on 18 October. It still targets .NET 10, and it is still a facade over ASP.NET Core Identity. Identity owns the users, passwords, and lockout. AuthEndpoints maps the HTTP endpoints on top.",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark monokai","dotnet add package AuthEndpoints --version 4.0.1\n","bash","",[29,30,31],"code",{"__ignoreMap":27},[32,33,36,40,44,47,50,54],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"sR7ES","dotnet",[32,41,43],{"class":42},"sLACW"," add",[32,45,46],{"class":42}," package",[32,48,49],{"class":42}," AuthEndpoints",[32,51,53],{"class":52},"sFhLe"," --version",[32,55,57],{"class":56},"sYThS"," 4.0.1\n",[59,60,62],"h2",{"id":61},"if-you-already-use-addauthendpoints","If you already use AddAuthEndpoints",[18,64,65,68,69,72],{},[29,66,67],{},"AddAuthEndpoints"," and ",[29,70,71],{},"MapAuthEndpoints"," stay the 3.4.1 facade. Same options, same routes, and they are not obsolete. A few small changes reach them, mostly around JWT refresh. Most of the breaking changes hit the smaller building-block maps, not the facade.",[59,74,76],{"id":75},"whats-new","What's new",[18,78,79,80,68,83,86,87,90],{},"There is a second facade for new projects. ",[29,81,82],{},"AddPasswordAuth",[29,84,85],{},"MapPasswordAuth"," give you one password session under ",[29,88,89],{},"\u002Fauth",". Cookie is the default, and JWT is the other option.",[22,92,96],{"className":93,"code":94,"language":95,"meta":27,"style":27},"language-cs shiki shiki-themes material-theme-lighter github-light github-dark monokai","builder.Services.AddPasswordAuth\u003CAppUser, AppDbContext>();\n\napp.UseAuthEndpoints();\napp.MapPasswordAuth\u003CAppUser>();\n","cs",[29,97,98,103,110,116],{"__ignoreMap":27},[32,99,100],{"class":34,"line":35},[32,101,102],{},"builder.Services.AddPasswordAuth\u003CAppUser, AppDbContext>();\n",[32,104,106],{"class":34,"line":105},2,[32,107,109],{"emptyLinePlaceholder":108},true,"\n",[32,111,113],{"class":34,"line":112},3,[32,114,115],{},"app.UseAuthEndpoints();\n",[32,117,119],{"class":34,"line":118},4,[32,120,121],{},"app.MapPasswordAuth\u003CAppUser>();\n",[18,123,124,125,127],{},"Identity bearer tokens stay on ",[29,126,67],{},". Pick one facade per host. The startup check fails if you call both.",[18,129,130,131,134,135,138],{},"Sign-in now finishes in one place. You implement ",[29,132,133],{},"ISignInCompleter\u003CTUser>"," and register it with ",[29,136,137],{},"AddSignInCompleter",". The 3.x completers for email code, passkeys, and external logins still work in 4.0. They are marked obsolete and go away in 5.0.",[18,140,141,142,145,146,148],{},"Users who only sign in with an email code can now pass step-up. ",[29,143,144],{},"POST \u002FconfirmIdentity\u002FemailCode\u002Fsend"," sends a \"confirm it's you\" code to accounts that have no password, authenticator, or passkey. ",[29,147,71],{}," doesn't map it.",[18,150,151],{},"Simple JWT can return the refresh token in the JSON body. That's for native apps and CLIs that have no cookie jar. Cookie transport is still the default. Refresh token families also get an absolute lifetime now, 14 days by default, and rotating the token doesn't extend it.",[18,153,154,157,158,160,161,163],{},[29,155,156],{},"POST \u002Fmanage\u002Fpassword"," lets an account with no password set its first one. It needs a ReAuth proof first. ",[29,159,85],{}," maps it and ",[29,162,71],{}," doesn't.",[18,165,166,167,170,171,174],{},"GitHub and Google sign-in moved into the main package. Upgrade the provider packages to 4.0.0 and drop ",[29,168,169],{},"AuthEndpoints.External.OAuth",". A version of that package below 4.0.0 fails the build with ",[29,172,173],{},"AE4000",".",[59,176,178],{"id":177},"before-you-upgrade","Before you upgrade",[18,180,181],{},"Two setups need a database migration:",[183,184,185,197],"ul",{},[186,187,188,189,192,193,196],"li",{},"If you call ",[29,190,191],{},"UseEmailCodes()",", add one for the new ",[29,194,195],{},"Purpose"," column.",[186,198,188,199,202,203,174],{},[29,200,201],{},"UseRefreshToken()",", add one for ",[29,204,205],{},"AbsoluteExpiresAt",[18,207,208,68,211,214,215,218,219,222,223,226,227,230],{},[29,209,210],{},"MapGitHubAuthEndpoints",[29,212,213],{},"MapGoogleAuthEndpoints"," are gone. Use ",[29,216,217],{},"MapExternalAuthProvider"," or ",[29,220,221],{},"MapExternalAuthEndpoints",". Every ",[29,224,225],{},"Map*"," also throws at startup now when its ",[29,228,229],{},"Add*"," is missing, so a broken setup fails before the first request.",[18,232,233,234,241],{},"The full list, with a migration step for every breaking change, is in the ",[235,236,240],"a",{"href":237,"rel":238},"https:\u002F\u002Fauthendpoints.harten.id\u002Fguides\u002Fupgrading-to-4\u002F",[239],"nofollow","upgrade guide",". Read it before you bump the version.",[59,243,245],{"id":244},"links","Links",[183,247,248,254,261,268,275],{},[186,249,250],{},[235,251,253],{"href":237,"rel":252},[239],"Upgrade to 4.0",[186,255,256],{},[235,257,260],{"href":258,"rel":259},"https:\u002F\u002Fauthendpoints.harten.id\u002Fgetting-started\u002Fpassword-auth\u002F",[239],"Password auth",[186,262,263],{},[235,264,267],{"href":265,"rel":266},"https:\u002F\u002Fauthendpoints.harten.id\u002Fchangelog\u002F",[239],"Changelog",[186,269,270],{},[235,271,274],{"href":272,"rel":273},"https:\u002F\u002Fwww.nuget.org\u002Fpackages\u002FAuthEndpoints\u002F4.0.1",[239],"NuGet",[186,276,277],{},[235,278,281],{"href":279,"rel":280},"https:\u002F\u002Fgithub.com\u002Fmadeyoga\u002FAuthEndpoints",[239],"GitHub",[18,283,284,285,174],{},"If something in the upgrade breaks for you, ",[235,286,289],{"href":287,"rel":288},"https:\u002F\u002Fgithub.com\u002Fmadeyoga\u002FAuthEndpoints\u002Fissues",[239],"open an issue",[291,292,293],"style",{},"html pre.shiki code .sR7ES, html code.shiki .sR7ES{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sFhLe, html code.shiki .sFhLe{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .sYThS, html code.shiki .sYThS{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}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 .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":27,"searchDepth":105,"depth":112,"links":295},[296,297,298,299],{"id":61,"depth":105,"text":62},{"id":75,"depth":105,"text":76},{"id":177,"depth":105,"text":178},{"id":244,"depth":105,"text":245},"2026-10-19","AuthEndpoints 4.0 is on NuGet. A new password facade, one sign-in completer, email-code step-up, and JWT refresh in the body. AddAuthEndpoints stays as it was.","md",null,{"sitemap":305,"schemaOrg":306},{"lastmod":300},[307],{"type":308,"headline":5,"author":309,"datePublished":300},"BlogPosting",{"type":310,"name":311},"Person","Made Yoga","\u002Fblog\u002Fauthendpoints-4-0",{"keywords":314,"title":5,"description":301},"AuthEndpoints, AuthEndpoints 4.0, ASP.NET Core authentication, ASP.NET Core Identity, .NET 10, JWT refresh token, sign-in completer, NuGet","blog\u002Fauthendpoints-4-0","tb3HTdehqKGRsPyiSIKHFvh5b6bejGV3nm84GM8ctP8",[318,323],{"title":319,"path":320,"stem":321,"description":322,"children":-1},"AuthEndpoints 3.0 RC: a composable auth endpoints library for ASP.NET Core","\u002Fblog\u002Fauthendpoints-3-0-rc","blog\u002Fauthendpoints-3-0-rc","Announcing AuthEndpoints 3.0 RC, an auth library for ASP.NET Core. Composable auth endpoints on top of ASP.NET Core Identity: registration, login, 2FA, password reset, and passkeys bootstrapped in minutes.",{"title":324,"path":325,"stem":326,"description":327,"children":-1},"ASP.NET Core auth for SPAs and native clients without the Identity boilerplate","\u002Fblog\u002Fauthendpoints-spa-native","blog\u002Fauthendpoints-spa-native","Identity works. SPA and native auth still meant rewriting endpoints each time. AuthEndpoints is the Identity facade I built for that.",1792385154935]