Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Additionally password protect an ASP.NET Core MVC website hosted as Azure WebApp with existing authentication

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:

  • Limit the access to the WebApp as a firewall setting. The client IPs will not be from a certain IP range and they will be dynamically assigned by their ISP.
  • Use an individual user account with Azure AD in front. This might be my fallback (although I'm not sure on how to implement exactly) but I'd rather not have another set of individual user credentials to take care. The credentials could even be something as simple as preview // preview.

Is there a simple way like adding two lines of codes in the Startup class to achieve my desired second level of password protection?

like image 618
citronas Avatar asked Oct 27 '22 20:10

citronas


1 Answers

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

enter image description here

like image 123
Ricky Gummadi Avatar answered Oct 29 '22 15:10

Ricky Gummadi