Enabling AzueAD authentication with user interface and JWT bearer header

andbe

Andreas Bergström

Posted on June 30, 2019

Enabling AzueAD authentication with user interface and JWT bearer header

The .NET Core framework allows for multiple authentication schemes in order to authenticate agains, for example, AzureAD and Facebook. The typical cookie-based option works great with user interfaces, and JWT is the go-to for API (user-less) authentication. What if you want to mix the way these schemes authenticate?

I've been setting up a solution with a user interface built with Razor Pages and a few endpoints which I intend to run when called from an Azure Function App. Sure, I could split the application in to several applications and that would make a lot of sense from some perspectives, but adds lots of complexity to a single app. That approach would also require an additional Azure App Service, which I don't really need.

Configuring the application to support multiple schemes turned out a bit less intuitive than I initially expected. My first approach included setting up different schemes and trying to enforce them with a PolicyBuilder and/or [Authorize] attributes on the API controllers without success.

Turns out there was a small piece missing to the puzzle. As always, when stumbling upon the solution on StackPath, it was obvious what piece was missing.

The key was to add another PolicyScheme and check for the Authorization HTTP header, and if it starts with "Bearer", forward the scheme selector:

services.AddPolicyScheme("dynamic", "Dynamic Policy scheme", options =>
{
    // https://stackoverflow.com/questions/45695382/how-do-i-setup-multiple-auth-schemes-in-asp-net-core-2-0/51897159#51897159
    options.ForwardDefaultSelector = context =>
    {
        var header = context.Request.Headers["Authorization"].FirstOrDefault();
        if (header?.StartsWith("Bearer") == true)
        {
            return JwtBearerDefaults.AuthenticationScheme;
        }

        return OpenIdConnectDefaults.AuthenticationScheme;
    };
})
Enter fullscreen mode Exit fullscreen mode

To make the dynamic scheme selector the default:

services.AddAuthentication(options =>
{

    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultAuthenticateScheme = "dynamic";
    options.DefaultChallengeScheme = "dynamic";
})
Enter fullscreen mode Exit fullscreen mode

Now, each request will first pass through the DefaultAuthenticationScheme, which will determine the authentication scheme to use.

Are there any other ways to achieve this? Do you know of a better solution? Please share!

💖 💪 🙅 🚩
andbe
Andreas Bergström

Posted on June 30, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related