I have an existing ASP.NET Core MVC application with ASP.NET Core Identity where I use a combination of signInManager.PasswordSignInAsync
and [Authorize]
attributes to enforce that a user is logged in to website, has a certain role et cetera. This works fine locally and in an Azure WebApp.
Now, I want to publish a preview version of my application to another Azure WebApp. This time, I want each visitor to enter another set of credentials before anything from the website is being shown. I guess I'd like to have something like an .htaccess / BasicAuthenication equivalent. However, after a user entered the first set of credentials, he should not be logged in since he should need to use the normal login prodecure (just as in the live version which is publicly accessible but this has certain pages which require the user to be logged in). Basically, I just want to add another layer of password protection on top without impacting the currently existing authentication.
Given that I want allow access to anyone with the preview password, the following solutions do not seem to work in my case:
Is there a simple way like adding two lines of codes in the Startup class to achieve my desired second level of password protection?
You can do a second auth via a basic auth, something simple and not too much code. You will need a middleware that will intercept/called after the original authentication is done
Middleware
public class SecondaryBasicAuthenticationMiddleware : IMiddleware
{
//CHANGE THIS TO SOMETHING STRONGER SO BRUTE FORCE ATTEMPTS CAN BE AVOIDED
private const string UserName = "TestUser1";
private const string Password = "TestPassword1";
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
//Only do the secondary auth if the user is already authenticated
if (!context.User.Identity.IsAuthenticated)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
// Get the encoded username and password
var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();
// Decode from Base64 to string
var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword));
// Split username and password
var username = decodedUsernamePassword.Split(':', 2)[0];
var password = decodedUsernamePassword.Split(':', 2)[1];
// Check if login is correct
if (IsAuthorized(username, password))
{
await next.Invoke(context);
return;
}
}
// Return authentication type (causes browser to show login dialog)
context.Response.Headers["WWW-Authenticate"] = "Basic";
// Return unauthorized
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
await next.Invoke(context);
}
}
//If you have a db another source you want to check then you can do that here
private bool IsAuthorized(string username, string password) =>
UserName == username && Password == password;
}
In startup -> Configure (make sure you add this after your existing authentication and authorization)
//Enable Swagger and SwaggerUI
app.UseMiddleware<SecondaryBasicAuthenticationMiddleware>(); //can turn this into an extension if you wish
app.UseAuthentication();
app.UseAuthorization();
In Startup -> ConfigureServices register the middleware
services.AddTransient<SecondaryBasicAuthenticationMiddleware>();
And chrome should pop up a basic auth dialog like this
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With