I'm new to writing Web APIs in .NET. I wrote this API which is working fine normally but then I added JWT
authentication and now when I provide correct username and password I get an authentication bearer token that I add to swagger UI but now when I try to access any other end point I get this 401
Unauthorized status. I'm unable to understand why. I've also tried this with Postman but same response.
Here is my Program.cs
using System.Text;
using Comply_Api_DotNet.Database;
using Comply_Api_DotNet.Repository;
using Comply_Api_DotNet.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddScoped<IUsersDb, UsersDb>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"Please provide authorization token to access restricted features.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
});
// ADD JWT Authentication
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
var key = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]);
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JWT:Issuer"],
ValidAudience = builder.Configuration["JWT:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(key)
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Her is my Controller.
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IAuthenticationService _authenticationService;
private readonly IUsersDb _usersDb;
public UsersController(IAuthenticationService authenticationService, IUsersDb usersDb)
{
_authenticationService = authenticationService;
_usersDb = usersDb;
}
[AllowAnonymous]
[HttpPost]
[Route("authenticate")]
public IActionResult Authenticate(User user)
{
var token = _authenticationService.Authenticate(user);
if (token == null)
{
return Unauthorized();
}
return Ok(token);
}
// GET api/<UsersController>/5
[HttpGet]
public IEnumerable<User> Get(long id)
{
var usersFound = _usersDb.GetAllUsers(id);
return usersFound;
}
// POST api/<UsersController>
[HttpPost]
public User Post([FromBody] User user)
{
var userAdded = _usersDb.AddNewUser(user);
return userAdded;
}
// PUT api/<UsersController>/5
[HttpPut("{id:long}")]
public void Put(long id, [FromBody] User user)
{
throw new NotImplementedException();
}
[HttpDelete("{id:long}")]
public bool Delete(long id)
{
return _usersDb.DeleteUser(id);
}
} // end of class
appsettings.Json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"JWT": {
"Key": "fc746b61cde4f6665d3f9791446cd5395661860c0075a905ed9810b7391af467",
"Issuer": "Comply",
"Audience": "comply"
}
}
UPDATE: Authentication Service
public class AuthenticationService : IAuthenticationService
{
private readonly IConfiguration _configuration;
private readonly IUsersDb _usersDb;
public AuthenticationService(IConfiguration configuration, IUsersDb usersDb)
{
_configuration = configuration;
_usersDb = usersDb;
}
public AuthenticationToken? Authenticate(User user)
{
var foundUser = _usersDb.GetAllUsers(0)
.FirstOrDefault(x => x.Name == user.Name && x.Password == user.Password);
if (foundUser == null)
{
return null;
}
//If user found then generate JWT
return CreateAuthenticationToken(foundUser);
}
private AuthenticationToken CreateAuthenticationToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenKey = Encoding.UTF8.GetBytes(_configuration["JWT:Key"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new(ClaimTypes.Name, user.Name),
}),
Expires = DateTime.UtcNow.AddMinutes(10),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey),
SecurityAlgorithms.HmacSha256Signature),
Issuer = _configuration["JWT:Issuer"],
Audience = _configuration["JWT:Audience"],
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return new AuthenticationToken()
{
Token = tokenHandler.WriteToken(token),
};
}
} //end of class
CodePudding user response:
The issue is here Type = SecuritySchemeType.ApiKey,
you are specifying security scheme type as apiKey
. you need to replace that with Type = SecuritySchemeType.Http,
. So, your OpenApiSecurityScheme
should now look like this.
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"Please provide authorization token to access restricted features.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT",
});