2

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
Hammas_Stack
  • 99
  • 1
  • 9
  • Check [this](https://stackoverflow.com/questions/58179180/jwt-authentication-and-swagger-with-net-core-3-0). I think you need to set jwt athrorization in `AddSwaggerGen`. – Usama May 17 '22 at 13:56
  • @Usama I'm doing exactly like that already. – Hammas_Stack May 17 '22 at 14:03
  • https://www.c-sharpcorner.com/article/jwt-json-web-token-authentication-in-asp-net-core/. Follow this one you will understand better. – Kumar Sanu May 17 '22 at 16:54
  • @Hammas_Stack According to the code you provided, there is no 401 error when using my token, can you share the code of your AuthenticationService? Check your Token generation to see if it contains the proper information. – Qing Guo May 18 '22 at 05:32
  • @QingGuo yeah i also get `401`. I've updated the question and have added Authentication Service code. Please check it out. – Hammas_Stack May 18 '22 at 07:05
  • @Hammas_Stack: you've not configured issuer and audience while issuing token – YK1 May 18 '22 at 07:13
  • 1
    @YK1 Hey, sorry can you explain a bit? because I've added Issuer and Audience in `.AddJwtBearer` in `TokenValidaionParameters` – Hammas_Stack May 18 '22 at 07:29
  • @Hammas_Stack: Thats at validation. You also need to add them to the `SecurityTokenDescriptor` at the time of issuing token. – YK1 May 18 '22 at 07:44
  • 1
    @YK1 I've added issuer and audience to `SecurityTokenDescriptor` Please check updated code. Also, I've hosted this on azure you can check there.. It is working fine when using from postman but from swagger it gives me auth token when i use that token to access other methods i get `401` happening in swagger only. – Hammas_Stack May 18 '22 at 15:37

2 Answers2

2

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",
});
Hammas
  • 1,126
  • 1
  • 12
  • 37
1

In swagger Ui, You should add Bearer before Token. Reference:Bearer Authentication

Authorization: Bearer <token>

As you can see in Postman Headers, It has a Bearer. enter image description here

Zoe
  • 27,060
  • 21
  • 118
  • 148
Qing Guo
  • 6,041
  • 1
  • 2
  • 10
  • @Hammas_Stack Did you find your problem? I just use your code, and it works, so I want to know the reason. I see you have accept an answer. Did it solve your problem? – Qing Guo May 19 '22 at 11:03