Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine the windows authentication and JWT with .Net Core 2.1

I have tried to use the windows authentication and JWT together with .NET Core 2.1.

I have following startup settings of the authentication:

services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,

                    ValidIssuer = "Test",
                    ValidAudience = "Test",
                    IssuerSigningKey = JwtSecurityKey.Create("677efa87-aa4d-42d6-adc8-9f866e5f75f7")
                };

                options.Events = new JwtBearerEvents()
                {
                    OnAuthenticationFailed = OnAuthenticationFailed
                };
            });

IIS settings:

"iisSettings": {
    "windowsAuthentication": true, 
    "anonymousAuthentication": true, 
    ..
  }

I have tried following code snippet to create the JWT token with windows authentication:

[Route("api/[controller]")]
    [ApiController]
    [Authorize(AuthenticationSchemes = "Windows")]
    public class AuthController : ControllerBase
    {
        [HttpPost("token")]
        public IActionResult Token()
        {
            //Setup claims
            var claims = new[]
            {
                new Claim(ClaimTypes.Name, User.Identity.Name),
                //Add additional claims
            };

            //Read signing symmetric key
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("677efa87-aa4d-42d6-adc8-9f866e5f75f7"));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            //Create a token
            var token = new JwtSecurityToken(
                issuer: "Test",
                audience: "Test",
                claims: claims,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: creds);

            //Return signed JWT token
            return Ok(new
            {
                token = new JwtSecurityTokenHandler().WriteToken(token)
            });
        }
    }

And in another controller I need use only JWT authentication:

[Route("api/[controller]")]
    [ApiController]
    [Authorize(AuthenticationSchemes = "Bearer")]
    public class ProductController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            var userName = User.Identity.Name;

            var claims = User.Claims.Select(x => new { x.Type, x.Value });

            return Ok(new { userName, claims });
        }
    }

If the JWT token is expired then I correctly received the response code 401 but I still get the dialog in the browser for putting the credentials.

How can I configure the windows authentication only for a part when I want to create the JWT token and disable response which is responsible for showing the browser dialog with credentials? How to correctly combine these things?

like image 437
Jenan Avatar asked Sep 03 '18 09:09

Jenan


3 Answers

The way I would handle this is to create two different web applications: one for Windows Authentication and one that uses JWT Token Authentication.

The Windows Authentication web application would be very small and only does one thing. Authenticate the user via Windows Authentication at an endpoint and return a JWT Token.

Then, that token can be used for the main application. As long as your signing key and audience is the same, it doesn't matter if the token is created on a different web application.

You won't need to struggle with trying to handle both at the same time.

like image 144
Todd Skelton Avatar answered Sep 19 '22 13:09

Todd Skelton


To work with both windows and JWT bearer authentication-- windows authentication by default get applied to all pages and it over ride the functionality of JWT Bearer. For combining both into one single application :-

  1. Apply Windows authentication on the provider which are used to generate Token of JWt Bearer using [AllowAnonymous] tag on it, which is using windows authentication by default
  2. Apply Jwt Bearer authentication on rest of pages in the application using [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] on it

So, In above case we are using windows authentication for generating JWT Token and using that JWT Token we are authenticating rest of pages in application.

For Testing scenario, You can try postman with NTLM(windows authentication) for token generation controller and BearerToken for page which are having JWTAuthentication

like image 24
Pooja Suryawanshi Avatar answered Sep 21 '22 13:09

Pooja Suryawanshi


Here is how I did it in .net 5 to use both Windows authentication and Jwt on a web api. I first let windows authentication get identity name, then check against a database table for authorization to get roles etc, then return a jwt token. the client web site uses the jwt token to access jwt protected resources.

  1. enable both Anonymous and windows authentication in launchingsettings when debug. in iis if deployed on the server.

  2. if you want to debug through Kestrel, at the time of this writing, in startup, DON'T use services.AddAuthentication(IISDefaults.AuthenticationScheme);
    instead, use

     services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
    
  3. standard set up in startup configureservices to add jwt authentication. in configure function, add authentication.

  4. decorate your function that you want to use windows authentication with this:

    [Authorize(AuthenticationSchemes = NegotiateDefaults.AuthenticationScheme)]
    [Route("GetToken")]
    [HttpGet]
    public IActionResult GetToken() 
    {}
  1. decorate your controller that you want to use jwt token with this:
    [Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme)]
    [ApiController]
    [Route("[controller]")]
    public class ReportController : ControllerBase
    {
    }

that's all.

like image 33
wxm146 Avatar answered Sep 18 '22 13:09

wxm146