Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JWT bearer token Authorization not working asp net core web api

I created a web api that uses JWT tokens for authorization with a role based policy (based on this article). The user logs in generates a token that is used for authorization. I successfully generate the token but when I start to use it to access restricted API actions with it it doesn't work and keeps giving me the 401 HTTP error (I cant even debug considering the action call doesn't trigger). What am I doing wrong?.

Classes:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddScoped<ICountriesService, CountriesService>();
        services.AddScoped<ICompanyService, CompanyService>();
        services.AddScoped<IPlaneServices, PlaneService>();
        services.AddScoped<IPlaneTypeService, PlaneTypeService>();
        services.AddScoped<ICitiesService, CitiesService>();
        services.AddScoped<IAirfieldService, AirfieldService>();
        services.AddScoped<ITicketTypeService, TicketTypeService>();
        services.AddScoped<IFlightService, FlightService>();
        services.AddScoped<ILuxuryService, LuxuryService>();
        services.AddScoped<IUserService, UserService>();

        // Register the Swagger generator, defining 1 or more Swagger documents
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "My API", Version = "v1" });


            c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n 
                  Enter 'Bearer' [space] and then your token in the text input below.
                  \r\n\r\nExample: 'Bearer 12345abcdef'",
                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>()
                  }
            });
            //        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            //var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
            //c.IncludeXmlComments(xmlPath);
        });

        services.AddAutoMapper(cfg => cfg.AddProfile<Mapper.Mapper>(),
                          AppDomain.CurrentDomain.GetAssemblies());

        services.AddDbContext<FlightMasterContext>();


        services.AddCors();

        var secret = Configuration.GetValue<string>(
            "AppSettings:Secret");

        var key = Encoding.ASCII.GetBytes(secret);
        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });



    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        // Enable middleware to serve generated Swagger as a JSON endpoint.
        app.UseSwagger();

        // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
        // specifying the Swagger JSON endpoint.
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            c.RoutePrefix = string.Empty;
        });



        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();
        app.UseAuthentication();

        // global cors policy
        app.UseCors(x => x
            .AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader());

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();

        });
    }
}

The controller:

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class AaTestController : ControllerBase
{

    private FlightMasterContext db { get; set; }

    private IUserService _userService;

    public AaTestController(FlightMasterContext db, IUserService userService)
    {
        this.db = db;
        _userService = userService;
    }

    [AllowAnonymous]
    [HttpPost("authenticate")]
    public IActionResult Authenticate([FromBody]AuthenticateModel model)
    {
        var user = _userService.Authenticate(model.Username, model.Password);

        if (user == null)
            return BadRequest(new { message = "Username or password is incorrect" });

        return Ok(user);
    }
    //DOESNT TRIGGER
    [Authorize(Roles = Role.Admin)]
    [HttpGet]
    public IActionResult GetAll()
    {
        var users = _userService.GetAll();
        return Ok(users);
    }

    [HttpGet("{id}")]
    public IActionResult GetById(int id)
    {
        // only allow admins to access other user records
        var currentUserId = int.Parse(User.Identity.Name);
        if (id != currentUserId && !User.IsInRole(Role.Admin))
            return Forbid();

        var user = _userService.GetById(id);

        if (user == null)
            return NotFound();

        return Ok(user);
    }
}

Service used for authentication and authorization:

public interface IUserService
{
    User Authenticate(string username, string password);
    IEnumerable<User> GetAll();
    User GetById(int id);
}

public class UserService : IUserService
{
    // users hardcoded for simplicity, store in a db with hashed passwords in production applications
    private List<User> _users = new List<User>
    {
        new User { Id = 1, FirstName = "Admin", LastName = "User", Username = "admin", Password = "admin", Role = Role.Admin },
        new User { Id = 2, FirstName = "Normal", LastName = "User", Username = "user", Password = "user", Role = Role.User }
    };






    public User Authenticate(string username, string password)
    {
        var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);



        var secret = "THIS IS Ughjgjhgjhghgighiizgzigiz";



        // return null if user not found
        if (user == null)
            return null;

        // authentication successful so generate jwt token
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(secret);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim(ClaimTypes.Name, user.Id.ToString()),
                new Claim(ClaimTypes.Role, user.Role)
            }),
            Expires = DateTime.UtcNow.AddDays(7),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        user.Token = tokenHandler.WriteToken(token);

        return user.WithoutPassword();
    }

    public IEnumerable<User> GetAll()
    {
        return _users.WithoutPasswords();
    }

    public User GetById(int id)
    {
        var user = _users.FirstOrDefault(x => x.Id == id);
        return user.WithoutPassword();
    }
}

enter image description here

like image 691
Adin Sijamija Avatar asked Jan 28 '20 18:01

Adin Sijamija


People also ask

What is JWT how we implement JWT with Webapi?

JWT stands for JSON Web Token digitally signed using a secret key by a token provider. It helps the resource server to verify the token data using the same secret key. JWT consists of three parts: Header: encoded data of the token type and the algorithm used to sign the data.

Can I use JWT as bearer token?

JSON Web Token (JWT, RFC 7519) is a way to encode claims in a JSON document that is then signed. JWTs can be used as OAuth 2.0 Bearer Tokens to encode all relevant parts of an access token into the access token itself instead of having to store them in a database.


Video Answer


1 Answers

Those methods should be called in reversed order:

app.UseAuthentication();
app.UseAuthorization();

First middleware should authenticate user, and only then next one - authorize. Unfortunately not all MS docs pay attention on this detail.

like image 150
Alexander Goldabin Avatar answered Oct 22 '22 20:10

Alexander Goldabin