jQuery AJAX Request Failing with 401 Error When Passing Bearer Token to .NET Core API

I’m getting a 401 unauthorized error when trying to call my .NET Core Web API endpoint with a bearer token using jQuery AJAX.

Here’s my frontend JavaScript code:

$(function() {
  $("#loginBtn").on('click', function(event) {
    event.preventDefault();
    
    var credentials = JSON.stringify({
      Email: $("#userEmail").val(),
      Pass: $("#userPass").val()
    });
    
    $.ajax({
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      method: "POST",
      url: "https://localhost:5001/api/Auth/signin",
      data: credentials,
      dataType: "json",
      success: function(response) {
        if (response && response.token) {
          console.log('Login successful');
          fetchUserData(response.token);
        } else {
          console.log('Authentication failed');
        }
      },
      error: function(xhr, status, err) {
        alert('Login error: ' + err);
      }
    });
  });
  
  function fetchUserData(authToken) {
    $.ajax({
      url: 'https://localhost:5001/api/Users',
      type: 'GET',
      headers: {
        'Authorization': 'Bearer ' + authToken
      },
      contentType: "application/json",
      dataType: 'json',
      success: function(data) {
        console.log('User data:', data);
      },
      error: function(xhr) {
        console.log('Error fetching users:', xhr.status);
      }
    });
  }
});

My backend controller looks like this:

[HttpPost]
[Route("signin")]
public async Task<IActionResult> SignIn([FromBody] LoginRequest request)
{
    var appUser = await _userManager.FindByEmailAsync(request.Email);
    if (appUser != null && await _userManager.CheckPasswordAsync(appUser, request.Pass))
    {
        var roles = await _userManager.GetRolesAsync(appUser);
        
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Email, appUser.Email),
            new Claim(JwtRegisteredClaimNames.Sub, appUser.Id),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };
        
        foreach (var role in roles)
        {
            claims.Add(new Claim(ClaimTypes.Role, role));
        }
        
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtSettings:Key"]));
        
        var jwt = new JwtSecurityToken(
            issuer: _config["JwtSettings:Issuer"],
            audience: _config["JwtSettings:Audience"],
            expires: DateTime.UtcNow.AddHours(2),
            claims: claims,
            signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
        );
        
        return Ok(new {
            token = new JwtSecurityTokenHandler().WriteToken(jwt),
            expires = jwt.ValidTo
        });
    }
    return BadRequest();
}

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
    private readonly DataContext _db;
    
    public UsersController(DataContext db)
    {
        _db = db;
    }
    
    [HttpGet]
    public async Task<ActionResult<List<User>>> GetAllUsers()
    {
        return await _db.Users.ToListAsync();
    }
}

The login endpoint works fine and returns a valid JWT token. However, when I try to use that token to access the protected Users endpoint, I keep getting a 401 error. Without the [Authorize] attribute, the API call works perfectly. What am I doing wrong with the token authentication?

Had the same issue with bearer tokens - CORS was messing with my Authorization header. Your login works fine, but the preflight request for protected endpoints might be stripping the header. Make sure your API’s CORS setup explicitly allows the Authorization header with builder.WithHeaders("Authorization"). Also check if your token’s getting URL encoded or has extra whitespace when it hits fetchUserData. Debug by logging the raw Authorization header in your controller and compare it to what you expect. Double-check that your JWT middleware is actually registered too - try hitting the endpoint with Postman using the same token to see if it’s a frontend or backend problem.

check your startup.cs or program.cs file - you probably forgot to configure JWT authentication middleware properly. you need to add services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) and the bearer token validation settings. also make sure app.UseAuthentication() comes before app.UseAuthorization() in the pipeline.

Had this exact issue last month - drove me crazy for hours. It’s probably your JWT configuration, specifically the token validation parameters. Double-check that your JwtSettings values in appsettings.json match exactly what you’re using when creating the token. Even tiny differences in the Issuer or Audience strings will cause silent validation failures. Also make sure your JWT key has the same length and encoding for both token generation and validation. Try logging the actual token claims in your controller to see if it’s parsing correctly. Clock skew is another common gotcha - add some tolerance with ClockSkew = TimeSpan.Zero or a few minutes buffer.