0

I have a .NET 7 Web API which generates a JWT on login; however, I am unable to utilize the token to gain access to an endpoint. I attempted the fix here .NET Core 2 Web API JWT token not recognized but this just changed the error code from 405 to 401.

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("CORSPolicy", builder =>
    {
        builder
        .AllowAnyMethod()
        .AllowAnyHeader()
        .WithOrigins("http://localhost:3000", "https://localhost:3000", "https://appname.asurestaticapps.net");
    });
});
// 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("auth0", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey
    });

    options.OperationFilter<SecurityRequirementsOperationFilter>();
});

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

builder.Services.AddAuthorization(auth =>
{
    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser().Build());
});

builder.Services.AddDbContext<SdeResearchDbContext>();
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<SdeResearchDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddScoped<JwtService>();

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password Settings.
    options.Password.RequireDigit= true;
    options.Password.RequireLowercase= true;
    options.Password.RequireNonAlphanumeric= true;
    options.Password.RequireUppercase= true;
    options.Password.RequiredLength= 8;
    options.Password.RequireLowercase= true;
    options.Password.RequiredUniqueChars = 6;

    // User settings
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+!";
    options.User.RequireUniqueEmail= true;
});

var app = builder.Build();

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

app.UseHttpsRedirection();

app.UseRouting();

app.UseCors("CORSPolicy");
app.UseAuthentication();
app.UseAuthorization();


app.MapControllerRoute(
    name: "Topic",
    pattern: "topic/*{action}"
    );

app.Run();

appsettings.json

{
  "Jwt": {
    "Key": "this is the secret key for the jwt, it must be kept secure",
    "Issuer": "https://localhost:7174",
    "Audience": "https://localhost:7174",
    "Subject": "JWT for this site"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

JwtService.cs

namespace SdeResearch.Api.Services
{
    public class JwtService
    {
        private const int EXPIRATION_MINUTES = 5;

        private readonly IConfiguration _configuration;

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

        public AuthenticationResponse CreateToken(ApplicationUser user)
        {
            var expiration = DateTime.UtcNow.AddMinutes(EXPIRATION_MINUTES);

            var token = CreateJwtToken(
                CreateClaims(user),
                CreateSigningCredentials(),
                expiration
                );

            var tokenHandler = new JwtSecurityTokenHandler();

            return new AuthenticationResponse
            {
                Token = tokenHandler.WriteToken(token),
                Expiration = expiration
            };
        }

        private JwtSecurityToken CreateJwtToken(Claim[] claims, SigningCredentials credentials, DateTime expiration) =>
             new JwtSecurityToken(
                 _configuration["Jwt:Issuer"],
                 _configuration["Jwt:Audience"],
                 claims,
                 expires: expiration,
                 signingCredentials: credentials
             );

        private Claim[] CreateClaims(ApplicationUser user) =>
            new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, _configuration["Jwt:Subject"]),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
                new Claim(ClaimTypes.NameIdentifier, user.Id),
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim(ClaimTypes.Email, user.Email),
            };

        private SigningCredentials CreateSigningCredentials() =>
            new SigningCredentials(
                    new SymmetricSecurityKey(
                            Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])
                        ),
                    SecurityAlgorithms.HmacSha256
                );
    }
}

AuthController login methed

        [HttpPost]
        [Route("/account/login")]
        public async Task<ActionResult<AuthenticationResponse>> Login(LoginDTO userLogin)
        {
            ApplicationUser user = await _userManager.FindByEmailAsync(userLogin.Email);
            bool result = await _userManager.CheckPasswordAsync(user, userLogin.Password);
            if (!result)
                return BadRequest(result);

            var token = _jwtService.CreateToken(user);

            return Ok(token);
        }

TopicControllor.cs

namespace SdeResearch.Api.Controllers
{
    [ApiController]
    [Route("[controller]")]
    [Authorize("Bearer")]
    public class TopicController : ControllerBase
    {
        private readonly SdeResearchDbContext _db;

        public TopicController(SdeResearchDbContext db)
        {
            _db = db;
        }

        [HttpGet]
        [Route("/topic/get-all-topics")]
        public async Task<List<Topic>> GetTopicsAsync()
        {
            return await _db.Topics.ToListAsync();
        }
    }
}

Postman endpoint test

Tiny Wang
  • 10,423
  • 1
  • 11
  • 29
  • 1
    I had a test in my side with your code and it worked well. My idea for troubleshooting it is [decode the token](https://jwt.io/) first to check if the token is correct. And then removing the policy you defined, just using `[Authorize]` instead of `[Authorize("Bearer")]`, this is to check the policy. – Tiny Wang Mar 21 '23 at 10:49
  • by the way, have you test your api with the token you get via the tool like postman? https://i.stack.imgur.com/IKnYN.png – Tiny Wang Mar 21 '23 at 10:56
  • @TinyWang I originally utilized `[Authorize]` and not `[Authorize("Bearer")]` and it didn't work. I'll try again, but when testing in SwaggerUI and in Postman in was still giving me a 405. I used auth bearer token in Postman, I'll try what you used. – Morgan Bradford Mar 21 '23 at 17:08
  • I tried this and it didn't work. I added a photo of the test in postman. – Morgan Bradford Mar 21 '23 at 17:14
  • 405 means the request url is correct but the method is not allowed, the first thing we can check is changing the route for the api like what mentioned in this [troubelshot document](https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications#resolve-http-405-errors). so maybe you can change the route `[Route("/topic/get-all-topics")]` to `[Route("/test-topic")]` then test call this api in postman with url `https://localhost:7174/test-topic` – Tiny Wang Mar 22 '23 at 07:18

0 Answers0