2

I am making a web application using ASP.NET Core using CookieAuthentication as my MVC authentication and JWT for Web-APIs.

I have configured the cookie authentication and created the JWT token successfully. However I have trouble authorizing my API methods that contain the [Authorization(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] attribute.

Startup.cs

services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.Cookie.Name = "CookieAuthentication";
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.ExpireTimeSpan = TimeSpan.FromDays(14);
        options.LoginPath = "/Account/Login";
        options.LogoutPath = "/Account/Logout";
        options.AccessDeniedPath = "/Account/Unauthorized";
    });

services
    .AddAuthentication()
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:JwtIssuer"],
            ValidAudience = Configuration["Jwt:JwtAudience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:JwtSecretKey"]))
        };
    });

AccountController.cs

[HttpPost]
[Route("Login")]
[AllowAnonymous]
public async Task<IActionResult> Login([FromForm]LoginViewModel model)
{
    await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

    if (model.Username == "test" && model.Password == "test")
    {
        var name = "Name";
        var role = "Admin";
        var email = "test123@gmail.com";

        var claims = new List<Claim>()
        {
            new Claim(ClaimTypes.Name, name),
            new Claim(ClaimTypes.Email, email),
            new Claim(ClaimTypes.Role, role)
        };

        var credientials = new SigningCredentials(
            new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_config["Jwt:JwtSecretKey"])),
                SecurityAlgorithms.HmacSha256);

        var securityToken = new JwtSecurityToken(   
            issuer: _config["Jwt:JwtIssuer"],
            audience: _config["Jwt:JwtIssuer"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(1),
            signingCredentials: credientials
        );

        HttpContext.Response.Cookies.Append(
            "JwtCookie",
            "Bearer " + new JwtSecurityTokenHandler().WriteToken(securityToken),
            new CookieOptions
            {
                Expires = DateTime.Now.AddMinutes(1),
                HttpOnly = true,
                Secure = true,
                SameSite = SameSiteMode.Strict
            });

            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

            var authenticationProperties = new AuthenticationProperties()
            {
                ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1),
                IsPersistent = true
            };

            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity),
                authenticationProperties);

            #endregion

            return Redirect($"{role}/Index");

Private.cshtml

@{
    ViewData["Title"] = "Private";
}

<h2>Private Area</h2>

<input type="button" onclick="addUser()" value="Login" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
    function addUser()
    {
        $.ajax
            ({
                type: 'POST',
                url: '/api/Test/AddUser'
            }).done(function (data)
            {
                console.log("done");
            }).fail(function (data, textStatus, xhr)
            {
                console.log("fail");
            })
    }
</script>

/api/Test/AddUser method

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpPost]
[Route("AddUser")]
public IActionResult AddUser()
{
    var u = new User
    {
        SamAccountName = "Test",
        Role = ""
    };
    _context.Add(u);
    try
    {
        _context.SaveChanges();
    }
    catch (Exception e)
    {
        return BadRequest($"Cannot save: {e}");
    }

    return Ok();
}

The question I have is:

  1. I am getting 401 error when I try to call the api/Test/AddUser method. Is there a way I can send the cookie containing the JWT token through the authorization header automatically, similar to how CookieAuthentication does it? I know this is because of my not having an Authorization header. However because I stored my JWT token inside the HttpOnly cookie, I cannot find a way to pass the token into the authorization header. I could add Authorization { "Bearer " + token }. I would have to use sessionStorage or use a normal cookie but that would pop up some security concerns. I have seen many tutorials but have never seen how the javascript passes its authorization header, most show passing through authorization header in Postman.

  2. If the above could be done, is there a way the expiration date could be synced up with the CookieAuthentication cookie? Since a JWT token expiration date cannot be extended like cookie, how would I ensure that the user is able to access the APIs and MVC if they are using two different types of authentication?

  3. The project only uses HTTPS and I implemented anti forgery tokens inside the forms I submit. Is there any attacks I should protect against?

tzrm
  • 513
  • 1
  • 8
  • 14
  • Does this answer your question? [How can I validate a JWT passed via cookies?](https://stackoverflow.com/questions/37398276/how-can-i-validate-a-jwt-passed-via-cookies) – Dour High Arch Oct 11 '21 at 18:57

0 Answers0