1

I am trying to unlock my endpoints (Controllers) with my JWT token that is sent to the user when they login. At the moment the register and login works and the user is sent a JWT token. However when I use postman or my Mobile application to send the JWT to the API I get a 401 Unauthorized error. I am using Asp.net 6 web API. I have added my Authentication Controller and my program.cs. I have my JWT key in my appsettings.json as well as the issuer and Audience. I am sure my error is in my program.cs

AuthController

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using VelocityNetAPI.Models;
using System.Security.Cryptography;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using VelocityNetAPI.Data;
using Microsoft.AspNetCore.Authorization;

namespace VelocityNetAPI.Controllers
{
    
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        public static User user = new User();
        private readonly IConfiguration configuration;
        private readonly VelocityNetAPIContext context;
        public AuthController(IConfiguration configuration, VelocityNetAPIContext context)
        {
            this.configuration = configuration;
            this.context = context;
        }

        [HttpPost("Register")]
        public async Task<ActionResult<User>> Register(UserDto request)
        {
            CreatePasswordHash(request.Password, out byte[] passwordHash, out byte[] passwordSalt);
            user.Name = request.Username;
            user.PasswordHash = passwordHash;
            user.PasswordSalt = passwordSalt;
            user.Role = "User";
            context.User.Add(user);
            await context.SaveChangesAsync();
            return Ok(user);
        }

        [HttpPost("Login")]
        public async Task<ActionResult<string>> Login(UserDto request)
        {
            //search for user
            var user = context.User.FirstOrDefault(u => u.Name == request.Username);
            if (user == null)
            {
                return BadRequest("User not found");
            }
            if(!VerifyPasswordHash(request.Password, user.PasswordHash, user.PasswordSalt))
            {
                return BadRequest("Wrong Password");
            }
            string token = CreateToken(user);
            return Ok(token);
        }


        private string CreateToken(User user)
        {
            List<Claim> claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.Name),
                new Claim(ClaimTypes.Role, user.Role),
            };
            var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(configuration["Jwt:key"]));
            var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha512);

            var token = new JwtSecurityToken(
                claims: claims,
                expires: DateTime.Now.AddDays(1),
                signingCredentials: cred);

            var jwt = new JwtSecurityTokenHandler().WriteToken(token);
            return jwt;
        }

        private void CreatePasswordHash(String password, out byte[] passwordHash, out byte[] passwordSalt)
        {
            using (HMACSHA512 hmac = new HMACSHA512())
            {
                passwordSalt = hmac.Key;
                passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
            }
        }

        private bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
        {
            using (HMACSHA512 hmac = new HMACSHA512(passwordSalt))
            {
                var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
                return computedHash.SequenceEqual(passwordHash);
            }
        }
    }
}

Program.cs

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using VelocityNetAPI.Data;
using Microsoft.IdentityModel.Tokens;
using System.Configuration;
using Swashbuckle.AspNetCore.SwaggerGen;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authorization;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<VelocityNetAPIContext>(options =>
    
    options.UseSqlServer(builder.Configuration.GetConnectionString("VelocityNetAPIContext")));
var conf = builder.Configuration;
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
    x.RequireHttpsMetadata = true;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = conf["Jwt:Issuer"].ToString(),
        ValidAudience = conf["Jwt:Audience"].ToString(),
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(conf["Jwt:Key"]))
    };
});

//Configuration.GetSection("AppSettings:Token").Value)

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();

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

Let me know if you need more info Cheers Everyone

Rena
  • 30,832
  • 6
  • 37
  • 72
Riley-Howley
  • 67
  • 2
  • 12
  • Hi @Riley-Howley, you set `ValidateIssuer` and `ValidateAudience ` true. But in your `CreateToken` method you do not use Issuer and Audience to create token. So a simple way is to change `ValidateIssuer` and `ValidateAudience ` false. – Rena Dec 14 '21 at 02:44
  • Hey Rena! Thankyou so much for replying. I have changed both Issuer and Audience to false as you suggested. I'm very lost within this area haha. However I still am receiving the 401 error. I'm sure that I'm missing something but unsure what it could be. when I implemented your change and ran my code It worked I got excited and then realized Allow Anonymous was set to the Get Function. Thankyou though hopefully I can get a solution. – Riley-Howley Dec 14 '21 at 02:53

1 Answers1

0

You set ValidateIssuer and ValidateAudience true. But in your CreateToken method you do not use Issuer and Audience to generate token.

You can change your CreateToken method like below:

var token = new JwtSecurityToken(configuration["Jwt:Issuer"],
    configuration["Jwt:Audience"],
    claims: claims,
            expires: DateTime.Now.AddDays(1),
            signingCredentials: cred);
Rena
  • 30,832
  • 6
  • 37
  • 72