Basic Authentication: Standalone login page for frontend-only deployments (closes #22144)#22168
Conversation
Use 2FA view even when login page is not configured.
There was a problem hiding this comment.
Pull request overview
Adds a lightweight, server-rendered Basic Auth login + 2FA flow (and optional external providers) intended to work even when the backoffice SPA/OpenIddict aren’t present, and splits backoffice auth registration so cookie sign-in can be enabled independently via AddBackOfficeSignIn().
Changes:
- Introduces
/umbraco/basic-auth/loginand/umbraco/basic-auth/2faendpoints (controller, routes, models, Razor views). - Hardens
BasicAuthenticationMiddlewarebehavior (route exclusions, 2FA redirect behavior, auth-scheme guard). - Refactors backoffice DI to separate cookie auth from OpenIddict services; updates integration tests accordingly.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Middleware/BasicAuthenticationMiddlewareTests.cs | Adds middleware unit tests for redirects/401/2FA and missing services scenarios. |
| tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Controllers/BasicAuthLoginControllerTests.cs | Adds controller unit tests for login, 2FA, returnPath validation, and configuration error paths. |
| tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs | Updates integration wiring to new backoffice auth registration split. |
| tests/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs | Updates integration wiring to new backoffice auth registration split. |
| src/Umbraco.Web.Website/Routing/BasicAuthLoginRoutes.cs | Adds endpoint routing for the standalone basic-auth login/2FA pages. |
| src/Umbraco.Web.Website/Models/BasicAuthTwoFactorModel.cs | Adds 2FA view model. |
| src/Umbraco.Web.Website/Models/BasicAuthLoginModel.cs | Adds login view model (incl. external providers list). |
| src/Umbraco.Web.Website/Middleware/BasicAuthenticationMiddleware.cs | Adds standalone login redirect, 2FA redirect behavior, and backoffice-scheme guard. |
| src/Umbraco.Web.Website/Extensions/UmbracoApplicationBuilderExtensions.cs | Registers the new basic-auth routes alongside existing website routes. |
| src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs | Registers BasicAuthLoginRoutes in DI. |
| src/Umbraco.Web.Website/Controllers/BasicAuthLoginController.cs | Adds server-rendered login/2FA/external-login endpoints for basic-auth flows. |
| src/Umbraco.Cms.StaticAssets/umbraco/BasicAuthLogin/TwoFactor.cshtml | Adds standalone 2FA page markup/styles. |
| src/Umbraco.Cms.StaticAssets/umbraco/BasicAuthLogin/Login.cshtml | Adds standalone login page markup/styles (incl. external provider buttons). |
| src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs | Moves OpenIddict-dependent registration out of backoffice identity. |
| src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs | Introduces AddBackOfficeSignIn() and reworks AddBackOffice() composition. |
| src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs | Splits cookie auth vs OpenIddict registrations; obsoletes AddBackOfficeAuthentication(). |
| global.json | Allows prerelease SDK selection. |
You can also share your feedback on Copilot code review. Take the survey.
Migaroez
left a comment
There was a problem hiding this comment.
Just some cde comments, nothing blocking. Testing functionality next
Migaroez
left a comment
There was a problem hiding this comment.
Overal happy with the functionality, the only 2 minor points are
- Views aren't customizable which might be an issue for some implementors. Made a quick addition on https://github.com/umbraco/Umbraco-CMS/tree/v17/improvement/22144-login-endpoints-front-end-only-custom-view
- There is no clear indication the form was submitted and it can be slow on first use. For successes this isn't a big problem but would be annoying if it was an incorrect password and the user submitted enough times to lock themselves out.
Happy to let it be merged in without the improvements.
…https://github.com/umbraco/Umbraco-CMS into v17/improvement/22144-login-endpoints-front-end-only
Yes, I was thinking this could come later if requested, but your solution works just fine, so let's include it from the start. I've cherry-picked it in here.
I've added a small amount of JavaScript to disable the button/update the text, so you have a bit more than the standard browser navigation to indicate that a form submission is pending. |
|
Docs PR is here: umbraco/UmbracoDocs#7954 |
Summary
Addresses #22144 which reports that Umbraco configurations that a) disable the backoffice and b) want to use basic authentication for the front-end of the website and c) have 2FA or external login providers, are unable to do so. Basic authentication uses a backoffice account, and, for anything on than basic user name and password, the built-in browser login won't suffice. There needs to be HTML forms, and without the backoffice available, these don't exist.
This PR adds a standalone, server-rendered login page for basic authentication that works independently of the backoffice SPA.
I've reworked the registration extension methods by extracting
AddBackOfficeSignIn()fromAddBackOffice()so backoffice cookie authentication can be registered without the full backoffice (OpenIddict, Management API, SPA).The server-rendered pages supports username/password login, two-factor authentication, and external login providers.
Behaviour change: standalone login for all basic auth
This PR changes the login redirect behaviour for all
RedirectToLoginPage: trueconfigurations, not just frontend-only deployments. Previously, basic auth redirected to the backoffice SPA login (/umbraco/). Now it always redirects to a lightweight server-rendered login page at/umbraco/basic-auth/login.Rationale:
We don't have to make this change of course, but now we have this dedicated login flow, it seems to make more sense to use it than the backoffice.
New public API:
AddBackOfficeSignIn()For frontend-only deployments that need basic auth with backoffice credentials but no backoffice UI:
Three deployment tiers are now supported:
.AddCore().AddWebsite().AddCore().AddBackOfficeSignIn().AddWebsite().AddBackOffice().AddWebsite()All also support and/or for
.AddDeliveryApi(). For full details, see umbraco/UmbracoDocs#7861 and #21630.Login flow
Username/password
/umbraco/basic-auth/login?returnPath=...Two-factor authentication
/umbraco/basic-auth/2faExternal login providers
External providers (Google, Microsoft, etc.) appear as buttons below the username/password form when configured. The OAuth flow uses
IBackOfficeSignInManagerdirectly — no OpenIddict dependency.Security and accessibility checklist
[ValidateAntiForgeryToken]returnPathvalidated viaUrl.IsLocalUrl()to prevent open redirects.lockoutOnFailure: trueon password sign-in.role="alert"on errors,aria-describedby/aria-invalidon inputs,<main>landmark, proper focus outlines, WCAG AA contrast.Testing
These scenarios verify the standalone login page for basic authentication across different deployment configurations.
Prerequisites
Umbraco site with published content
You need an Umbraco site with at least one published content node so there is a frontend page to protect.
Basic authentication configuration
All scenarios require basic authentication to be enabled in
appsettings.json. AdjustRedirectToLoginPageper scenario.{ "Umbraco": { "CMS": { "BasicAuth": { "Enabled": true, "RedirectToLoginPage": true } } } }See Basic Auth Settings documentation for all available options.
Program.cs configurations
Two configurations are used across these scenarios:
Normal Umbraco setup (with backoffice):
Without backoffice (frontend-only):
Scenario 1: Login page redirect — normal Umbraco setup
Config:
BasicAuth.Enabled: true,RedirectToLoginPage: true, normal Umbraco setupSteps
appsettings.jsonwithEnabled: trueandRedirectToLoginPage: trueProgram.cssetup (with.AddBackOffice())/umbraco/basic-auth/login?returnPath=%2FExpected result
Scenario 2: Browser popup — normal Umbraco setup
Config:
BasicAuth.Enabled: true,RedirectToLoginPage: false, normal Umbraco setupSteps
appsettings.jsonwithEnabled: trueandRedirectToLoginPage: falseProgram.cssetup (with.AddBackOffice())Expected result
WWW-Authenticate: Basic realm="Umbraco login"headerScenario 3: Login page redirect — without backoffice
Config:
BasicAuth.Enabled: true,RedirectToLoginPage: true, frontend-only setupSteps
appsettings.jsonwithEnabled: trueandRedirectToLoginPage: trueProgram.cssetup (with.AddCore()+.AddBackOfficeSignIn()+.AddWebsite())/umbraco/basic-auth/login?returnPath=%2FExpected result
/umbraco/would return 404Scenario 4: Browser popup — without backoffice
Config:
BasicAuth.Enabled: true,RedirectToLoginPage: false, frontend-only setupSteps
appsettings.jsonwithEnabled: trueandRedirectToLoginPage: falseProgram.cssetup (with.AddCore()+.AddBackOfficeSignIn()+.AddWebsite())Expected result
Scenario 5: Two-factor authentication — without backoffice
Config:
BasicAuth.Enabled: true,RedirectToLoginPage: true, frontend-only setup, 2FA enabledSetup: Configure 2FA
2FA must be configured and enabled on the test user's account before switching to the frontend-only setup.
Install and configure 2FA
Install the Umbraco.Community.User2FA NuGet package into your project:
Add the configuration to
appsettings.json:{ "User2FA": { "AuthenticatorIssuerName": "My Umbraco Site" } }Run the site with the normal Umbraco setup (
.AddBackOffice()) so the backoffice is availableLog into the backoffice
Click your user avatar in the top right corner
Select "Configure Two Factor" and scan the QR code with an authenticator app (e.g. Microsoft Authenticator, Google Authenticator)
Verify the code to complete setup
Steps
appsettings.jsonwithEnabled: trueandRedirectToLoginPage: trueProgram.cssetup (.AddCore()+.AddBackOfficeSignIn()+.AddWebsite())/umbraco/basic-auth/2faAdditional test: Browser popup with 2FA
RedirectToLoginPagetofalseExpected result
RedirectToLoginPage: false), the middleware still redirects to the 2FA pageScenario 6: External login provider — without backoffice
Config:
BasicAuth.Enabled: true,RedirectToLoginPage: true, frontend-only setup, Google external login configuredSetup: Configure Google external login
The external login provider must be configured before testing. This requires a Google OAuth application.
Create Google OAuth credentials
https://localhost:44339/umbraco-google-signinas an Authorized redirect URI (adjust port to match your setup)Install NuGet package
Create GoogleBackOfficeExternalLoginProviderOptions.cs
Create ConfigureGoogleAuthenticationOptions.cs
Create GoogleBackOfficeExternalLoginComposer.cs
See the External Login Providers documentation for full details.
Steps
appsettings.jsonwithEnabled: trueandRedirectToLoginPage: trueProgram.cssetup (.AddCore()+.AddBackOfficeSignIn()+.AddWebsite())Expected result
UserBypassTwoFactorForExternalLoginsis configured)