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();
}
}
}