skip to Main Content

I have a problem that when the user makes login and send his JWT token in the response when he try to access authorized endpoint it returns 401 always i don’t know why he’s not authenticated even if he has JWT?

Here is the program.cs code:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using ProuductsShopWepAPI.Models;
using ProuductsShopWepAPI.Reopositories;
using ProuductsShopWepAPI.RepositoriesContracts;
using ProuductsShopWepAPI.Services;
using ProuductsShopWepAPI.ServicesContracts;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{

    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Description = "Please enter a valid token",
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        BearerFormat = "JWT",
        Scheme = "Bearer"
    });
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type=ReferenceType.SecurityScheme,
                    Id="Bearer"
                }
            },
            Array.Empty<string>()
        }
    });
});


// Add Api services
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<ICategoryRepository, CategoryRepository>();
builder.Services.AddScoped<IAccountService, AccountService>();

// Configure Entity frame work
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
});

// Configure jwt authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = true,
            ValidIssuer = builder.Configuration["JWT:Issuer"],
            ValidateAudience = true,
            ValidAudience = builder.Configuration["JWT:Audience"],
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"])),
        };
    });

//Configure Identity
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
    options.User.RequireUniqueEmail = true;
    options.Password.RequiredUniqueChars = 5;
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequiredLength = 8;
    options.Password.RequireUppercase = true;
}).AddEntityFrameworkStores<ApplicationDbContext>();


//Configure CORS
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyHeader();
        policy.AllowAnyMethod();
        policy.AllowAnyOrigin();

    });
});


builder.Services.ConfigureApplicationCookie(options =>
{
    options.Events.OnRedirectToAccessDenied =
        options.Events.OnRedirectToLogin = (context) =>
        {
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            return Task.FromResult<object>(null);
        };
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseCors();
app.UseResponseCaching();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

and this is the code that generates the JWT token:

public async Task<JwtSecurityToken> CreateJwtAsync(LoginDTO loginDTO)
{
    var user = new IdentityUser()
    {
        Email = loginDTO.Email
    };
    var issuer = _configuration["JWT:Issuer"];
    var audience = _configuration["JWT:Audience"];
    var key = _configuration["JWT:key"];

    var signingCredintials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)), SecurityAlgorithms.HmacSha256);

    var roles = await _userManager.GetRolesAsync(user);
    var claims = await _userManager.GetClaimsAsync(user) ?? new List<Claim>();

    foreach (var role in roles)
    {
        claims.Add(new Claim(ClaimTypes.Role, role));
    }
    claims.Add(new Claim(ClaimTypes.Name, loginDTO.Email));

    var jwtObject = new JwtSecurityToken(
        issuer: issuer,
        audience: audience,
        claims: claims,
        expires: DateTime.UtcNow.AddSeconds(1000),
        signingCredentials: signingCredintials
        );

    return jwtObject;

}

Login endpoint:

    [HttpPost]
    public async Task<IActionResult> Login(LoginDTO loginDTO)
    {
        try
        {
            if (!ModelState.IsValid)
            {
                var details = new ValidationProblemDetails(ModelState);
                details.Status = StatusCodes.Status400BadRequest;
                return BadRequest(details);
            }
            var isLoginValid = await _accountService.IsLoginValidAsync(loginDTO);

            if (!isLoginValid) throw new Exception("Email or password is not valid");

            var jwtObject = await _accountService.CreateJwtAsync(loginDTO);

            var stringJwt = new JwtSecurityTokenHandler().WriteToken(jwtObject);
            HttpContext.Response.Headers["Authorization"] = stringJwt;
            return StatusCode(StatusCodes.Status200OK, stringJwt);

        }
        catch (Exception ex)
        {
            var problemDetails = new ProblemDetails();
            problemDetails.Detail = ex.Message;
            problemDetails.Status = StatusCodes.Status400BadRequest;
            return StatusCode(StatusCodes.Status400BadRequest, problemDetails);
        }

    }

Here is Seed/Categories endpoint

    [HttpPost]
    [Authorize]
    public async Task<IActionResult> Categories()
    {
        var newCategoriees = new List<Category>();
        using (var reader = new StreamReader($"{_environment.ContentRootPath}/Data/amazon_categories.csv"))
        {
            using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
            {
                var dic = _categoryRepository.GetAll();
                var dic1 = await dic.ToDictionaryAsync(c => c.Id);
                var categories = csv.GetRecordsAsync<ProductCategorie>();
                await foreach (var category in categories)
                {
                    if (!dic1.ContainsKey(long.Parse(category.id)))
                    {
                        var newCategory = new Category();
                        newCategory.Id = long.Parse(category.id);
                        newCategory.Name = category.category_name;
                        newCategoriees.Add(newCategory);
                    }
                }
            }
        }
        await _categoryRepository.AddCategoriesAsync(newCategoriees);
        return StatusCode(StatusCodes.Status200OK, $"{newCategoriees.Count} categories has been added");

    }

as you see here even though the user has authorization in the request header it returns 401 status code error instead making him seed the categories in the database
enter image description here
if you know how to solve this problem then please tell me.

2

Answers


  1. In your categories endpoint, replace [Authorize] with:

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    
    Login or Signup to reply.
  2. You can use Authorize attribute for you controller or action for example (may you have incorrect AuthenticationSchemes for /[controller]/[action] )

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search