2

I have an ASP.NET Core 2.1 Web API which currently requires users to enter a username/password to recieve a JWT for authorization. I want to add the option of using Windows Authentication to recieve a JWT as well. Ultimately, I plan to have two authorization controllers, one for username/password, the other for Windows Auth.

To test this, I first enabled Windows Authentication in IIS express, by right clicking on my project and going to Properties.

Hey look, windows auth!

Then, I made a simple test controller to see if I could authenticate with my Windows Credentials.

[Authorize(AuthenticationSchemes = "Windows")]
[Route("api/ping")]
public class PingController : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "Pong" };
    }
}

This seemed to work, as when I navigated to this endpoint in my browser, the screen displayed Pong as expected.

However, I run into problems when I try and access any of my other controllers that use Bearer Authentication Schemes. The controllers declare their Authentication Scheme like so:

[Authorize(Policy = "MyPolicy", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Whenever I make a request to one of these controllers I get the following error:

HTTP Error 400. The request is badly formed.

This same request works just fine with Windows Authentication turned off.

How can I enforce Windows Authentication on some controllers and Bearer Authentication on others?

2

1 Answer 1

3

How can I enforce Windows Authentication on some controllers and Bearer Authentication on others?

Firstly, I have to say it is somehow weird when dealing with the mixed schemes of JWT and Windows Authentication. I mean when an user who is not authenticated by JwtBearer, tries to access those url resources protected by JwtBearer Scheme, will be challenged by Windows authentication.

Secondly, as for your question, we can configure the JwtBearer authentication to use a custom token which is not used as the HTTP header (i.e. Authorization: Bearer xxxx_yyyy_zzz) . For example, send the JWT token by querystring or a custom header.

How to In Details :

Configure the JwtBearer authentication to read token from querystring:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options=> {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = Configuration["Jwt:Issuer"],
                ValidAudience = Configuration["Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
            };
            options.Events = new JwtBearerEvents() {
                OnMessageReceived = async (context) =>{
                    var bearer=context.HttpContext.Request.Query["bearer"].FirstOrDefault();
                    if(!String.IsNullOrEmpty(bearer)){
                        context.Token = bearer;
                    }
                },
            };
        });

For a testing purpose, I add a dummy policy handler for your MyPolicy:

services.AddAuthorization(o => {
    o.AddPolicy("MyPolicy",p => {
        p.Requirements.Add(new MyPolicyRequirement());
    });
});
services.AddSingleton<IAuthorizationHandler,MyPolicyRequirementHandler>();
services.AddHttpContextAccessor();

Here the MyPolicyRequirementHandler is :

public class MyPolicyRequirementHandler : AuthorizationHandler<MyPolicyRequirement>
{

    public MyPolicyRequirementHandler()
    {
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyPolicyRequirement requirement)
    {
        var user= context.User;
        if (user.Identity.IsAuthenticated)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

And the two controllers protected by the Authentication of Windows or JwtBearer :

[Authorize(AuthenticationSchemes = "Windows")]
[Route("api/[controller]")]
[ApiController]
public class PingController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "Pong" };
    }
}

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy ="MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class FooController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "Bar" };
    }
}

Test Case :

Test with Windows Authentication

A Screenshot when accessing the /api/ping

enter image description here

Test with Jwt Bearer Authentication

Firstly, generate a JwtToken on server side :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Iml0bWludXMiLCJuYmYiOjE1NDIzNDMxNzMsImV4cCI6MTU0MjQxNTE3MywiaWF0IjoxNTQyMzQzMTczLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM4NSIsImF1ZCI6IndlYmNsaWVudCJ9.iMnq8UBRQforNeRBehrULAScD8D2-ta4nmdQt1rTZ3s

And then send a HTTP GET request to the endpoint of /api/foo with a querystring of bearer=xxx_yyy_zzz :

GET https://localhost:44385/api/foo?bearer=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Iml0bWludXMiLCJuYmYiOjE1NDIzNDMxNzMsImV4cCI6MTU0MjQxNTE3MywiaWF0IjoxNTQyMzQzMTczLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM4NSIsImF1ZCI6IndlYmNsaWVudCJ9.iMnq8UBRQforNeRBehrULAScD8D2-ta4nmdQt1rTZ3s HTTP/1.1

it will return [foo] as expected :

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTZcV2luZG93c0F1dGh0ZW50aWNhdGlvbkFuZEp3dEF1dGhlbnRpY2F0aW9uXEFwcFxhcGlcZm9v?=
X-Powered-By: ASP.NET

["Bar"]
Sign up to request clarification or add additional context in comments.

3 Comments

//when an user who is not authenticated by JwtBearer, tries to access those url resources protected by JwtBearer Scheme, will be challenged by Windows authentication// - Is it possible to avoid this behaviour while retaining both auth schemes?
@Jones have you got the answer for this? I'm having the same struggle. Couldn't figure out how to do mixed authentication but JWT endpoint shouldn't challenge by Windows Authentication. I don't want to make a different app to generate token.
@AngryCoder - Yeap, Windows auth expects token in header Authorization. So to work with both, send jwt token in some custom header. I use JWTToken.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.