-1

So i'm trying to paste JWT after login to SWAGGER, but after trying to enter controller which works only with authorized users i'm getting 401 unauthorized error:

getting token(https://i.sstatic.net/zHjN1.png)

trying to execute controller(https://i.sstatic.net/ZB3S1.png)

here's part of my code which figures with JWT:

AuthService.cs

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using BookStore.Application.Security;
using Bookstore.Configurations;
using BookStore.DataAccess;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;

namespace BookStore.Application.Services;

public class AuthService {
    private readonly BookStoreDbContext _context;

    public AuthService(BookStoreDbContext context) {
        _context = context;
    }

    public async Task<string> AuthenticateAsync(string email, string password) {
        var user = await _context.Users.Include(user => user.Role).FirstOrDefaultAsync(u => u.Email == email);

        if (user == null) {
            return null; 
        }

        if (!PasswordHasher.VerifyPassword(password, user.PasswordHash)) {
            return null; 
        }

        var claims = new List<Claim> {
            new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString()),
            new Claim(ClaimTypes.GivenName, user.FirstName),
            new Claim(ClaimTypes.Surname, user.LastName),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.Role, user.Role.Name)
        };

        var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthConfig.KEY));

        var jwt = new JwtSecurityToken(
            issuer: AuthConfig.ISSUER,
            audience: AuthConfig.AUDIENCE,
            claims: claims,
            expires: DateTime.UtcNow.Add(TimeSpan.FromHours(2)),
            signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256));

        return new JwtSecurityTokenHandler().WriteToken(jwt);
    }
}

AuthConfig.cs:

using System.Text;
using Microsoft.IdentityModel.Tokens;

namespace Bookstore.Configurations;

public class AuthConfig {
    public const string ISSUER = "randomauthserver"; 
    public const string AUDIENCE = "randomauthclient.com"; 
    public const string KEY = "randomrandomradndomrandomrandom_randomsecret@123123!!!";   
    
    // public static SymmetricSecurityKey GetSymmetricSecurityKey() => 
    //     new SymmetricSecurityKey(Encoding.UTF8.GetBytes(KEY));
}

Program.cs

using System.Text;
using BookStore.Application.Interfaces;
using BookStore.Application.Services;
using Bookstore.Configurations;
using BookStore.DataAccess;
using BookStore.DataAccess.Interfaces;
using BookStore.DataAccess.Repositories;
using Bookstore.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddControllers();

string connection =
    builder.Configuration.GetConnectionString("StoreDbContext") ?? string.Empty;
builder.Services.AddDbContext<BookStoreDbContext>(options => options.UseNpgsql(connection));

builder.Services.AddScoped<ICRUDService<Book>, BookCRUDService>();
builder.Services.AddScoped<IRepository<Book>, BookRepository>();

builder.Services.AddScoped<ICRUDService<OrderItem>, OrderItemCRUDService>();
builder.Services.AddScoped<IRepository<OrderItem>, OrderItemRepository>();

builder.Services.AddScoped<ICRUDService<Order>, OrderCRUDService>();
builder.Services.AddScoped<IRepository<Order>, OrderRepository>();

builder.Services.AddScoped<ICRUDService<Role>, RoleCRUDService>();
builder.Services.AddScoped<IRepository<Role>, RoleRepository>();

builder.Services.AddScoped<ICRUDService<User>, UserCRUDService>();
builder.Services.AddScoped<IRepository<User>, UserRepository>();

builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<RegistrationService>();

builder.Services.AddScoped<OrderService>();

builder.Services.AddSwaggerGen(opt => {
    opt.SwaggerDoc("v1", new OpenApiInfo { Title = "BookStore", Version = "v1" });
    opt.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
        In = ParameterLocation.Header,
        Description = "Please enter token",
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey,
        BearerFormat = "JWT",
        Scheme = "bearer"
    });
    opt.AddSecurityRequirement(new OpenApiSecurityRequirement {
        {
            new OpenApiSecurityScheme {
                Reference = new OpenApiReference {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new string[] { }
        }
    });
});

builder.Services.AddAuthorization();
// builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
//     .AddJwtBearer(options => {
//         options.TokenValidationParameters = new TokenValidationParameters {
//             ValidateIssuer = true,
//             ValidIssuer = AuthConfig.ISSUER,
//             ValidateAudience = true,
//             ValidAudience = AuthConfig.AUDIENCE,
//             ValidateLifetime = true,
//             // IssuerSigningKey = AuthConfig.GetSymmetricSecurityKey(),
//             IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthConfig.KEY)),
//             ValidateIssuerSigningKey = true,
//         };
//     });

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidIssuer = AuthConfig.ISSUER,
        ValidateAudience = true,
        ValidAudience = AuthConfig.AUDIENCE,
        ValidateLifetime = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthConfig.KEY)),
        ValidateIssuerSigningKey = true,
    };
});


var app = builder.Build();

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

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

AuthController.cs

using BookStore.Application.Services;
using BookStore.Application.Utilities;
using Microsoft.AspNetCore.Mvc;

namespace Bookstore.Controllers;

[Route("api/[controller]")]
[ApiController]
public class AuthController : Controller {
    private readonly AuthService _authService;

    public AuthController(AuthService authService) {
        _authService = authService;
    }

    [HttpPost("login")]
    public async Task<IActionResult> LoginAsync([FromBody] LoginModel loginModel) {
        var token = await _authService.AuthenticateAsync(loginModel.Email, loginModel.Password);
        if (token == null) {
            return Unauthorized("Invalid email or password");
        }
        return Ok(new { Token = token });
    }
}

RoleCRUDController.cs (trying to use it)

using BookStore.Application.Interfaces;
using Bookstore.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Bookstore.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

// [Authorize]
public class RoleCRUDController : ControllerBase {
    private readonly ICRUDService<Role> _icrudService;

    public RoleCRUDController(ICRUDService<Role> icrudService) {
        _icrudService = icrudService;
    }

    [HttpGet]
    public async Task<ActionResult<List<Role>>> GetAllRoles() {
        var books = await _icrudService.GetAll();
        return Ok(books);
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Role>> GetRoleById(Guid id) {
        var role = await _icrudService.GetById(id);
        if (role == null) {
            return BadRequest("Wrong ID");
        }

        return Ok(role);
    }

    [HttpPost]
    public async Task<ActionResult<Role>> CreateRole([FromBody] Role role) {
        var createdRole = await _icrudService.Create(role);
        return CreatedAtAction(nameof(GetRoleById), new { id = createdRole.RoleId }, createdRole);
    }

    [HttpPut("{id}")]
    public async Task<ActionResult<Role>> UpdateRole(Guid id, [FromBody] Role role) {
        if (id != role.RoleId) {
            return BadRequest("Wrong ID");
        }

        var updatedRole = await _icrudService.Update(role);
        return Ok(updatedRole);
    }

    [HttpDelete("{id}")]
    public async Task<ActionResult> DeleteRole(Guid id) {
        return Ok(await _icrudService.Delete(id));
    }
}

so here's logger output:

"Logging": {
  "Console": {
    "LogLevel": {
      "Microsoft.Hosting.Lifetime": "Trace",
      "Microsoft.AspNetCore.Authentication": "Information",
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
},
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
      AuthenticationScheme: Bearer was challenged.

github if needed: https://github.com/oleggl47l/BookStoreFullstackApp/tree/tryin_other_auth

0

3 Answers 3

0

In your RoleCRUD endpoint (or any endpoint that requires authorization), replace [Authorize] with:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

So your controller should look like this:

[ApiController]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class RoleCRUDController : ControllerBase
Sign up to request clarification or add additional context in comments.

2 Comments

still the same problem(
well, you definitely need that code, otherwise it won't work. You were using [Authorize] - which is incorrect. It must be something to do with how you're generating the token in Program.cs
0

Try to use SecurityTokenDescriptor Class to create a security token

Contains some information which used to create a security token.

Try to have a little modify to your code like:

 public async Task<string> AuthenticateAsync(string email, string password) {
        var user = await _context.Users.Include(user => user.Role).FirstOrDefaultAsync(u => u.Email == email);

        if (user == null) {
            return null; 
        }

        if (!PasswordHasher.VerifyPassword(password, user.PasswordHash)) {
            return null; 
        }

        var claims = new List<Claim> {
            new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString()),
            new Claim(ClaimTypes.GivenName, user.FirstName),
            new Claim(ClaimTypes.Surname, user.LastName),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.Role, user.Role.Name)
        };

        var tokenDescriptor1 = new SecurityTokenDescriptor {
           Issuer =  AuthConfig.ISSUER,
           Audience =AuthConfig.AUDIENCE,
           Subject = new ClaimsIdentity(claims),
           Expires= DateTime.UtcNow.AddMinutes(10),
           SigningCredentials= new SigningCredentials(AuthConfig.GetSymmetricSecurityKey(),
                SecurityAlgorithms.HmacSha256) };

         var tokenObject1 = new JwtSecurityTokenHandler().CreateToken(tokenDescriptor1);
         string Token = new JwtSecurityTokenHandler().WriteToken(tokenObject1);
         return Token;
    }

1 Comment

thanks, but still not woking. i have no idea what's wrong
0

There were some NuGet packages that apparently caused problems. After deleting them, everything's fine. I guess it's due to Microsoft.IdentityModel.Tokens; there's a problem

Before: Before

After: After

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.