JWT request can't go through Authorize policy - c#

I am implementing JWT Authorization on Asp.Net but I am having a trouble here. When I am trying to access the Dashboard/Home controller's method I am always recieving '401 Unauthorized' response. According to my research I can say that the request can not go through
[Authorize(Policy = "ApiUser")]
But when comment that statement I recieve an error here
var userId = _caller.Claims.Single(c => c.Type == "id");
Which states that sequence conatains no appropriate elements, and if I will print the
_httpContextAccessor.HttpContext.User.Claims
I basically recieve a '[]'.
Also I want you to notice that the token is
correct
I am testing the application with help of postman
I stacked on this error for two days and really rely on your help. Tell me if some additional code is needed.
Code:
Dashboard Controller:
[Authorize(Policy = "ApiUser")]
[Route("api/[controller]/[action]")]
public class DashboardController : Controller
{
private readonly ClaimsPrincipal _caller;
private readonly BackendContext _appDbContext;
private readonly IHttpContextAccessor _httpContextAccessor;
public DashboardController(UserManager<AppUser> userManager, BackendContext appDbContext, IHttpContextAccessor httpContextAccessor)
{
_caller = httpContextAccessor.HttpContext.User;
_appDbContext = appDbContext;
_httpContextAccessor = httpContextAccessor;
}
// GET api/dashboard/home
[HttpGet]
public async Task<IActionResult> Home()
{
// retrieve the user info
//HttpContext.User
//return new OkObjectResult(_httpContextAccessor.HttpContext.User.Claims);
var userId = _caller.Claims.Single(c => c.Type == "id");
var customer = await _appDbContext.Customers.Include(c => c.Identity).SingleAsync(c => c.Identity.Id == userId.Value);
return new OkObjectResult(new
{
Message = "This is secure API and user data!",
customer.Identity.FirstName,
customer.Identity.LastName,
customer.Identity.PictureUrl,
customer.Identity.FacebookId,
customer.Location,
customer.Locale,
customer.Gender
});
}
}
Startup:
public class Startup
{
private const string SecretKey = "iNivDmHLpUA223sqsfhqGbMRdRj1PVkH"; // todo: get this from somewhere secure
private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<BackendContext>()
.AddDefaultTokenProviders();
services.AddDbContext<BackendContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly("Backend")));
services.AddSingleton<IJwtFactory, JwtFactory>();
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions =>
{
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
// add identity
var builder = services.AddIdentityCore<AppUser>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
builder.AddEntityFrameworkStores<BackendContext>().AddDefaultTokenProviders();
services.AddAutoMapper();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddDbContext<BackendContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly("Backend")));
services.AddTransient<IStoreService, StoreService>();
services.AddMvc();//.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc();
}
}
Constants (used in startup):
public static class Constants
{
public static class Strings
{
public static class JwtClaimIdentifiers
{
public const string Rol = "rol", Id = "id";
}
public static class JwtClaims
{
public const string ApiAccess = "api_access";
}
}
}

For your issue is caused by the mismatch configuration between token generation and validation for Issuer and Audience.
In Startup.cs, you configure ValidateIssuer = true and ValidateAudience = true, but for your provided token, there is no iss and aud.
Here are two ways for a try:
Disable to validate Issuer and Audience
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidIssuer = "Issuer",
ValidateAudience = false,
ValidAudience = "Audience",
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
Or, provide the Issuer and Audience while generating the token.
public IActionResult Login()
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("iNivDmHLpUA223sqsfhqGbMRdRj1PVkH"));
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var tokeOptions = new JwtSecurityToken(
issuer: "Issuer",
audience: "Audience",
claims: new List<Claim>() { new Claim("rol", "api_access") },
expires: DateTime.Now.AddMinutes(25),
signingCredentials: signinCredentials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
return Ok(new { Token = tokenString });
}
Here is a working demo CoreJwt.

It has passed about 4 months since this question has been asked, but I hope it will help the future visitors, I had the same problem and it was because I had missed to add the app.UseAuthentication(); to my Configure method:
app.UseAuthentication();
//////////////////////////
app.UseHttpsRedirection();
app.UseStaticFiles();
Also I had missed to add the JwtIssuerOptions to my appsettings.json and appsettings.Development.json:
"JwtIssuerOptions": {
"Issuer": "webApi",
"Audience": "http://localhost:5000/"
},

Related

ASP.NET Core 5 JWT Authentication fails with response code 401

I'm trying to implement JWT based authentication in my ASP.NET Core 5 Web API. However, I always end up with the response code 401 when using my APIs marked with the [Authorize] attribute.
Here's what I have so far. First, my AccountController issues a JWT if the user provides a valid username and password:
[Authorize]
[ApiController]
[Route("api/" + Constants.ApiVersion + "/Accounts")]
public class AccountController : ControllerBase
{
private readonly UserManager<AppUser> _userManager;
private readonly IPasswordHasher<AppUser> _passwordHasher;
public AccountController(UserManager<AppUser> userManager, IPasswordHasher<AppUser> passwordHasher)
{
_userManager = userManager;
_passwordHasher = passwordHasher;
}
[AllowAnonymous]
[HttpPost]
[Route("Token")]
public async Task<IActionResult> Login([FromForm]LoginBindingModel model)
{
if(model == null)
{
return BadRequest();
}
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
AppUser user = await _userManager.FindByNameAsync(model.UserName);
if(user == null || !await _userManager.CheckPasswordAsync(user, model.Password))
{
return Unauthorized();
}
SymmetricSecurityKey encryptionKey = new(Encoding.UTF8.GetBytes("TODO_Find_better_key_and_store_as_secret"));
JwtSecurityTokenHandler jwtTokenHandler = new();
SecurityTokenDescriptor tokenDescriptor = new()
{
Subject = new ClaimsIdentity(new[] { new Claim("UserName", user.UserName) }),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(encryptionKey, SecurityAlgorithms.HmacSha256Signature)
};
SecurityToken jwtToken = jwtTokenHandler.CreateToken(tokenDescriptor);
string token = jwtTokenHandler.WriteToken(jwtToken);
return Ok(token);
}
[HttpPost]
[Route("ChangePassword")]
public async Task<ActionResult> ChangePassword([FromBody]ChangePasswordBindingModel model)
{
if(model == null)
{
return BadRequest();
}
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
AppUser user = await _userManager.GetUserAsync(User);
if(user == null)
{
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
IdentityResult result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
return GetHttpResponse(result);
}
...
}
This code seems to work as it should. It returns a token that is successfully parsed by jwt.io and contains the username I put into it.
Next, the Startup class looks as follows:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration
{
get;
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ApplicationSettings>(Configuration.GetSection(nameof(ApplicationSettings)));
services.AddIdentityCore<AppUser>(options =>
{
Configuration.GetSection(nameof(IdentityOptions)).Bind(options);
});
services.AddScoped<IPasswordHasher<AppUser>, Identity.PasswordHasher<AppUser>>();
services.AddTransient<IUserStore<AppUser>, UserStore>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "whatever",
ValidateAudience = true,
ValidAudience = "whatever",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("TODO_Find_better_key_and_store_as_secret"))
};
});
services.AddMvc();
services.AddControllers();
}
// 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();
}
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I'm sending an HTTP POST request to the Token route which returns me the JWT. After that I'm sending an HTTP POST request with the necessary JSON data in the request body and Authorization: Bearer <the JWT> in the header to the ChangePassword route.
However, that always returns me response code 401 without any additional information or exception.
I'm unaware what the magic in Startup.ConfigureServices is actually supposed to do behind the scenes. Anyway, it obviously doesn't work. Does anyone know what is going on and what to do to make it work?
However, that always returns me response code 401 without any
additional information or exception.
That is because you set ValidateIssuer and ValidateAudience true but there is no Issuer and Audience in the generated token.
One way is that you can set Issuer and Audience in code:
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor()
{
Issuer= "whatever",
Audience= "whatever",
Subject = new ClaimsIdentity(new[] { new Claim("UserName", user.Name) }),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(encryptionKey, SecurityAlgorithms.HmacSha256Signature)
};
Another way is that you can set ValidateIssuer and ValidateAudience false:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false, //change here..
ValidIssuer = "whatever",
ValidateAudience = false, //change here..
ValidAudience = "whatever",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("TODO_Find_better_key_and_store_as_secret"))
};
});

.NET Core 3.1 role-based authorization with JWT returns 403 forbidden

I am having an issue, when I am trying to use [Authorize(Roles = "Administrator")] in my controllers, it always returns 403. I am using Identity and JWT tokens. Here's my Startup.cs . I am calling both app.UseAuthentication() and app.UseAuthorization() in ConfigureServices(), but still the same, everytime it returns 403
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentity<User, Role>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredUniqueChars = 0;
options.Password.RequiredLength = 6;
options.ClaimsIdentity.UserIdClaimType = "user_id";
options.ClaimsIdentity.UserNameClaimType = "email";
options.ClaimsIdentity.RoleClaimType = "user_role";
})
.AddRoles<Role>()
.AddEntityFrameworkStores<MyDbContext>()
.AddDefaultTokenProviders();
var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JwtKey"].ToString());
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.SaveToken = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKey = new SymmetricSecurityKey(key),
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
}
};
});
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Here's how I am creating token
private async Task<string> GenerateToken(DataModelUser user)
{
var key = Encoding.UTF8.GetBytes(_appSettings.JwtKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("user_role", user.Role.ToString()),
new Claim("user_id", user.Id.ToString()),
new Claim("email", user.Email)
}),
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(securityToken);
}
And I am adding like this on my controller
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : ControllerBase
{
[HttpGet]
[Authorize(Roles = "Administrator")]
public async Task<IActionResult> Get()
{
return Ok();
}
}
Here's my JWT token payload!
{
"user_role": "Administrator",
"user_id": "5a6333f1-9696-4b1c-a8f8-04619ebd686d",
"name": "Admin Admin",
"completed_profile": "False",
"email": "myadminemail#gmail.com",
"nbf": 1597147248,
"exp": 1597233648,
"iat": 1597147248
}
I had a similar issue, and I resolved it by adding RoleClaimType and NameClaimType to the TokenValidationParameter:
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKey = new SymmetricSecurityKey(key),
ClockSkew = TimeSpan.Zero,
RoleClaimType = IdentityModel.JwtClaimTypes.Role,
NameClaimType = IdentityModel.JwtClaimTypes.Name
};

ASP.Net Core 3 API always returns 401- JwtBearer

I have an ASP .NET Core WebAPI and I generate a JWT token for authorization purposes but whenever I send the request I get 401 - Unauthorized.
The order of operations:
1. GET for token
2. GET for user <-- 401
I checked my token on jwt.io and it was correct.
When I remove [Authorize] attrivute everything works fine
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
var appSettingsSection = Configuration.GetSection("Jwt");
services.Configure<JwtSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<JwtSettings>();
services.AddControllers();
services.AddOptions();
services.AddAuthentication(x =>
{
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x=>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudience = appSettings.Issuer,
ValidIssuer = appSettings.Issuer,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings.Key))
};
}
);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
CreateToken method
public JwtDto CreateToken(string email, string role)
{
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub,email),
new Claim(ClaimTypes.Role, role),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat,now.ToTimestamp().ToString(),ClaimValueTypes.Integer64)
};
var expires = now.AddMinutes(360);
var singingCredentails = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.Key)),SecurityAlgorithms.HmacSha256);
var jwt = new JwtSecurityToken(
issuer: _settings.Issuer,
claims: claims,
notBefore: now,
expires: expires,
signingCredentials: singingCredentails
);
var token = new JwtSecurityTokenHandler().WriteToken(jwt);
return new JwtDto
{
Token = token,
Expiry = expires.ToTimestamp()
};
}
GetToken - API
[HttpGet]
[Route("token")]
public IActionResult GetToken()
{
var token = _jwtHandler.CreateToken("test", "user");
return Json(token);
}
GetUser - API <---------- 401 error
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet("{email}")]
public async Task<UserDto> Get(string email)
{
return await _userService.GetUserAsync(email);
}
I have come across the exact same problem and believe the issue is in the Configure() method in Startup.cs. You have the correct UseAuthentication() and UseAuthorization() calls, in the correct order, which is important, and was the problem I discovered. For you therefore I think the problem is the lack of the UseCors() call. My working Startup class is below:
public class Startup
{
private bool _isDevelopmentEnvironment = true;
public IConfiguration configuration { get; }
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Retrieve App Settings:
var appSettingsSection = configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
// Configure JWT:
var key = Encoding.ASCII.GetBytes(appSettings.JwtSharedSecret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = !_isDevelopmentEnvironment;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = appSettings.JwtValidateIssuer,
ValidateAudience = appSettings.JwtValidateAudience,
ValidateLifetime = appSettings.JwtValidateLifetime,
ClockSkew = TimeSpan.Zero
};
});
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IUserService, UserService>();
services.AddScoped<IHydrator<User, UserModel>, UserModelHydrator>();
}
// 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();
}
else
{
_isDevelopmentEnvironment = false;
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
// TODO: Adjust CORS settings appropriately
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I also confirmed that both the token generation code and the Startup.cs code use the same key from application settings, I can't see how you get that in your CreateToken() method, but I assume it's from the same settings file. Hope this helps!

WebApi core 2.2 JWT Token not been Authorized

I was able to generate the token but if I tried to Authorize in my controller it doesn't work.
I create a class JWT but I didn't set the issuer or audience.
private List<Claim> Claim = new List<Claim>();
public string GetUserToken(string tp,string id)
{
var sck = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")));
var sc = new SigningCredentials(sck, SecurityAlgorithms.HmacSha256Signature);
if(tp == "Host")
{
Claim.Add(new Claim(ClaimTypes.Role, "Host"));
Claim.Add(new Claim(ClaimTypes.Name, id));
}
else
{
Claim.Add(new Claim(ClaimTypes.Role, "Client"));
Claim.Add(new Claim(ClaimTypes.Name, id));
}
var token = new JwtSecurityToken(
expires: DateTime.Now.AddDays(30),
signingCredentials: sc,
claims: Claim
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
and Inside of Startup class :
public void ConfigureServices(IServiceCollection services)
{
var SymmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).
AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = SymmetricSecurityKey
};
});
And in my controller I just put [Authorize(Roles ="Host")]. Even removing the Roles attribute still the same result, 401 Unauthorized
Check your key and jwt configuration, your startup class should looks something like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
//Get the key from configuration file section
var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
//jwt configuration
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
};
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//Configuration of cors to allow request of anothers
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
//Use the authentication service
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
}

404 Not found while hitting api with Json token using postman

I am following this Tutorial and on Securing a controller with claim based authorization part I am getting a 404 Not Found error in Postman. I have exactly followed the tutorial, i am just running .net core v2.0.0 .
Tutorial's DashboardController.cs is in my project named as BordViewController.cs .
This is my BordViewController.cs . The problem is, control is not going in this class, but when i remove [Authorize(Policy = "ApiUser")] it works.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace DotNetGigs
{
[Authorize(Policy = "ApiUser")]
[Route("api/[controller]")]
public class BordViewController : Controller
{
public BordViewController()
{
}
// GET api/dashboard/home
[HttpGet("home")]
public IActionResult GetHome()
{
return new OkObjectResult(new { Message = "This is secure data!" });
}
}
}
This is my startup.cs file
private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
public Startup()
{
var builder = new ConfigurationBuilder();
builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory().ToString(),"Configuration.json"));
Configuration = builder.Build();
}
public IConfiguration Configuration {get;set;}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDBcontext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),b=>b.MigrationsAssembly("LoginApp")));
services.AddSingleton<IJWTfactory, JWTFactory>();
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);});
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JWTclaimIden.Rol, Constants.Strings.JWTclaims.ApiAccess));
});
services.AddIdentity<AppUser, IdentityRole>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<AppDBcontext>()
.AddDefaultTokenProviders();
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = false,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{ // options.Audience = Configuration.GetSection("TokenProviderOptions:Audience").Value;
//options.Issuer = Configuration.GetSection("TokenProviderOptions:Issuer").Value;
// options.SaveToken = true;
options.TokenValidationParameters = tokenValidationParameters;
options.RequireHttpsMetadata = false;
});
services.AddMvc().AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
services.AddAutoMapper();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc();
}
I am new to c# and .net core, if someone can help me figure out the mistake I am making I would be really grateful. Also please tell me if you want me to add some other files too.
Postman Code snippet
GET /api/boardview/home HTTP/1.1
Host: localhost:5000
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJuaWtoaWxrYTU4NUBnbWFpbC5jb20iLCJqdGkiOiJmMTc1ZjdiNy1iN2ZmLTQxM2UtYjM5Ny0yYTQyYmI3NjMxZmQiLCJpYXQiOjE1MDk1NTY2MzQsInJvbCI6ImFwaV9hY2Nlc3MiLCJpZCI6ImJlOWJmZjc5LThlNDEtNDEwYS1iN2E3LTBjOGQxNDExYjQ5YyIsIm5iZiI6MTUwOTU1NjYzNCwiZXhwIjoxNTA5NTYzODM0LCJpc3MiOiJMb2dpbkFwcCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC8ifQ.NbwKHjH8h
Cache-Control: no-cache
Postman-Token: 18e932c2-ac73-5539-60a1-04b7b981e37a
JwtIssureOptions
"JwtIssuerOptions": {
"Issuer":"LoginApp",
"Audience":"http://localhost:5000/"
}

Categories

Resources