How to fix error 401 when using JWT authentication bearer to secure web pages

JUAN MIGUEL C. NIETO 61 Reputation points
2023-10-13T09:35:08.8766667+00:00
**Here is my View that I want to secure using the JWTBEARER. **
   `     [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        public IActionResult Privacy()
        {
            var token = HttpContext.Request.Headers["Authorization"].FirstOrDefault();
            Debug.WriteLine($"Received token: {token}");

            if (token != null && token.StartsWith("Bearer "))
            {
                Debug.WriteLine($"Token content: {token}");

                // Rest of your code
                return View();
            }
            else
            {
                // Handle the case where the token is missing or in an invalid format
                return Unauthorized();
            }
        }

`

and this is my javascript that is being used to authenticate the login and that if it's a success it will redirect on the Home/Privacy view.

`    <script>
        $(document).ready(function () {
            $('#login-button').click(function () {
                console.log('login button clicked.');
                var username = $('#username').val();
                var password = $('#password').val();

                var formData = new FormData();
                formData.append("UserName", username);
                formData.append("Password", password);

                $.ajax({
                    url: '/api/Account/Login',
                    method: 'POST',
                    processData: false,  // Important: Prevent jQuery from processing data
                    contentType: false,  // Important: Prevent jQuery from setting content type
                    data: formData,
                    success: function (data) {
                        console.log('Login success:', data);
                        if (data.token) {
                            // Store the received JWT token securely
                            localStorage.setItem('jwtToken', data.token);
                            console.log('JWT token stored:', data.token);

                   var jwtToken = localStorage.getItem('jwtToken').trim();

                            if (!jwtToken.startsWith('Bearer ')) {
                                jwtToken = 'Bearer ' + jwtToken;
                            }

                            $.ajax({
                                url: '/Home/Privacy',
                                method: 'GET', // Assuming it's a GET request
                                headers: {
                                    'Authorization':  jwtToken

                                },
                                success: function (privacyData) {
                                    // Handle the successful response for 'Home/Privacy'
                                    window.location.href = '/Home/Privacy';
                                },
                                error: function (xhr, textStatus, errorThrown) {
                                    // Handle the error response for 'Home/Privacy'
                                    console.error('Error accessing Home/Privacy:', textStatus, errorThrown);
                                    alert('Failed to access Home/Privacy. Please check your authentication.');
                                }
                            });
                        } else {
                            console.warn('Login failed:', data.error);
                            alert('Login failed. Please check your credentials.');
                        }
                    },
                    error: function (xhr, textStatus, errorThrown) {
                        console.error('Login error occurred:', textStatus, errorThrown);
                        alert('Login failed. Please check your credentials.');
                    }
                });
            });
        });
    </script>
`

now the problem is that even though I checked the returned type and it match I'm receiving error 401 after being redirected to the Home/Privacy. The token match but it's still giving that error.


My login api on Account 

`
        [HttpPost]
        [Route("Login")]
        public IActionResult Login([FromForm] AuthenticationRequest authenticationRequest)
        {
            try
            {
                var authResult = _jwtAuthenticationManager.Authenticate(authenticationRequest.UserName, authenticationRequest.Password);

                if (authResult == null)
                {
                    Debug.WriteLine("Authentication failed.");
                    return Unauthorized();
                }
                else
                {
                    Debug.WriteLine("Authentication successful. Token: " + authResult.token);
                    return Ok(authResult);
                }
            }
            catch (Exception ex)
            {
                // Log the exception details
                Console.WriteLine($"Exception: {ex.Message}");
                return StatusCode(500, "An error occurred while processing the request.");
            }
        }

    }
}`



Here is my AuthenticationManager where the token creation and the where the username and password condition is written that is being called on the javascript using ajax 

  `public class JwtAuthenticationManager
    {
        private readonly IConfiguration _configuration;

        public JwtAuthenticationManager(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public JwtAuthResponse Authenticate(string userName, string password)
        {
            // Validating the Username and Password

            string storedUsername = _configuration["MySecrets:Username"];
            string storedPassword = _configuration["MySecrets:Password"];

            // Add logging to see the values
            Debug.WriteLine($"Input Username: {userName}, Stored Username: {storedUsername}");
            Debug.WriteLine($"Input Password: {password}, Stored Password: {storedPassword}");

            if (userName != storedUsername || password != storedPassword)
            {
                Console.WriteLine("Authentication failed.");
                return null;
            }

            var tokenExpiryTimeStamp = DateTime.Now.AddMinutes(Constants.JWT_TOKEN_VALIDITY_MINS);
            var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
            var tokenKey = Encoding.ASCII.GetBytes(Constants.JWT_SECURITY_KEY);
            var securityTokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new System.Security.Claims.ClaimsIdentity(new List<Claim>
                {
                    new Claim("username", userName),
                }),
                // Expiration of the token
                Expires = tokenExpiryTimeStamp,
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey), SecurityAlgorithms.HmacSha256Signature)
            };

            var securityToken = jwtSecurityTokenHandler.CreateToken(securityTokenDescriptor);
            var token = jwtSecurityTokenHandler.WriteToken(securityToken);

            return new JwtAuthResponse
            {
                token = token,
                user_name = userName,
                expires_in = (int)tokenExpiryTimeStamp.Subtract(DateTime.Now).TotalSeconds
            };
        }
    }
}
`

lastly my program.cs I'm using .net core also.

I think i've searched enough before asking this problem here, and I can't think of any errors that I have made. The headers and token is being sent successfully but its still returning error 401 


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
    options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
});
builder.Configuration.AddUserSecrets<Program>();

//jwt authentication 



builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
   
}).AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Constants.JWT_SECURITY_KEY)), // Use the correct key
        ValidateIssuer = false,
        ValidateAudience = false,
    };
});


var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}



app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();



app.UseAuthentication();


app.UseAuthorization();



app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");



app.Run();





ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,815 questions
JavaScript API
JavaScript API
An Office service that supports add-ins to interact with objects in Office client applications.
1,058 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Bruce (SqlWork.com) 74,851 Reputation points
    2023-10-13T15:54:20.58+00:00

    your approach will not work. a browser navigation request like:

    window.location.href = '/Home/Privacy'

    will not include the jwt token, which the controller action requires.

    if you want to use jwt tokens, then your application can only be ajax calls with the token passed. then javascript can use the response, to update the browser's dom. this is typically of a SPA application, but typically the response payloads are json and the javascript has the render logic.

    0 comments No comments

  2. Ping Ni-MSFT 5,130 Reputation points Microsoft External Staff
    2023-10-19T08:36:55.6266667+00:00

    Hi @JUAN MIGUEL C. NIETO ,

    Just use .html() to render the view

    success: function (privacyData) {
        // Handle the successful response for 'Home/Privacy'
        $("body").html(privacyData);
    },
    

    Besides, it seems no need to judge the token in your Privacy action, because if you can get into the Privacy action, it indicates the successful authorization.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,
    Rena

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.