Identity Authorize Attribute Roles with Web API - asp.net

I have a small Web API application that uses Identity to manage users using Owin Bearer Tokens. The basics of this implementation work fine: I can register a user, login a user and access Web API end points that are marked with [Authorize].
My next step is to limit Web API endpoints using roles. For example, a controller that only users in the Admin role can access. I've created the Admin user as below and I add them to the Admin role. However when I update my existing controllers from [Authorize] to [Authorize(Roles = "Admin")] and try to access it using the Adim account, I get a 401 Unauthorized.
//Seed on Startup
public static void Seed()
{
var user = await userManager.FindAsync("Admin", "123456");
if (user == null)
{
IdentityUser user = new IdentityUser { UserName = "Admin" };
var createResult = await userManager.CreateAsync(user, "123456");
if (!roleManager.RoleExists("Admin"))
var createRoleResult = roleManager.Create(new IdentityRole("Admin"));
user = await userManager.FindAsync("Admin", "123456");
var addRoleResult = await userManager.AddToRoleAsync(user.Id, "Admin");
}
}
//Works
[Authorize]
public class TestController : ApiController
{
// GET api/<controller>
public bool Get()
{
return true;
}
}
//Doesn't work
[Authorize(Roles = "Admin")]
public class TestController : ApiController
{
// GET api/<controller>
public bool Get()
{
return true;
}
}
Q: What is the correct way to set up and use roles?

How do you set the claims for the users when they login I believe you are missing this line of code in method GrantResourceOwnerCredentials
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
identity.AddClaim(new Claim(ClaimTypes.Role, "Supervisor"));
And if you want to create the identity from DB use the below:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
Then in GrantResourceOwnerCredentials do the below:
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);

Related

Cannot access HttpContextAccessor User Claims on OnActionExecuting

I'm using .net core with JWT for authentication. And I'm trying to access user claims from HttpContextAccessor in my custom attribute. From within the app, I have a UserService.cs (code below) where I can do this. But on OnActionExecuting in my custom attribute, the claims come as an empty error. Even if I call the function from my user service, the claims aren't there.
My ultimate objective is to get the user's id to check if the user has admin access. I don't wanna store the admin access status on the token.
UserService.cs
public AuthenticatedUserClaims AuthenticatedUser()
{
var userClaims = new AuthenticatedUserClaims();
var claims = _contextAccessor.HttpContext.User.Claims;
var enumerable = claims as Claim[] ?? claims.ToArray();
var userId = enumerable.SingleOrDefault(x => x.Type == "UserId")?.Value;
userClaims.UserName = enumerable.SingleOrDefault(x => x.Type == "UserName")?.Value;
userClaims.FullName = enumerable.SingleOrDefault(x => x.Type == "FullName")?.Value;
if (userId != null && !string.IsNullOrEmpty(userId)) userClaims.UserId = int.Parse(userId);
return userClaims;
}
My Custom Attribute
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
public class PermissionsRequiredAttribute: ActionFilterAttribute
{
private readonly IHttpContextAccessor _contextAccessor;
public PermissionsRequiredAttribute(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var claims = _contextAccessor.HttpContext.User.Claims;
var claimsList = claims as Claim[] ?? claims.ToArray();
// claimsList = Claims[0]??
// context.Result = new UnauthorizedResult();
base.OnActionExecuting(context);
}
}
Attribute Usage
[HttpGet("{id}")]
[ServiceFilter(typeof(PermissionsRequiredAttribute))]
public async Task<ActionResult<Beneficiary>> GetBeneficiary([FromRoute] int id) { //... }
ConfigureServices on Startup.cs
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<PermissionsRequiredAttribute>();
Thanks in advance :)
I found a "hackaround" based on this answer, but I still don't know why my user HttpContext is empty.
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
public class PermissionsRequiredAttribute: ActionFilterAttribute
{
private readonly IUser _user;
public PermissionsRequiredAttribute(IUser user)
{
_user = user;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
bool headers = context.HttpContext.Request.Headers.TryGetValue("Authorization", out var tokens);
string token = tokens.FirstOrDefault()?.Split(" ")[1];
if (string.IsNullOrEmpty(token))
context.Result = new UnauthorizedResult();
var handler = new JwtSecurityTokenHandler();
JwtSecurityToken securityToken = (JwtSecurityToken) handler.ReadToken(token);
IEnumerable<Claim> claims = securityToken.Payload.Claims;
string userId = claims.SingleOrDefault(c => c.Type == "UserId")?.Value;
if (!string.IsNullOrEmpty(userId))
context.Result = new UnauthorizedResult();
var currentUser = await _user.GetUser(int.Parse(userId ?? throw new UnauthorizedAccessException()));
if (!currentUser.isAdmin)
context.Result = new UnauthorizedResult();
await base.OnActionExecutionAsync(context, next);
}
}
EDIT: 16-10-2021
I found a solution I'm happy with, it looks like I just needed to add the line in Startup.cs in ConfigureServices(IServiceCollection services)
services.AddHttpContextAccessor();
And now the line below has values Claims
var claims = _contextAccessor.HttpContext.User.Claims;

How do I make and use JWT role based authentication?

I follow the tutorial link below.
https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login
I am trying to understand how it works and I want to use role-based authentication using this token. so I made another policy in the Startup.cs file as below.
And I tried to use it like [Authorize(Policy = "admin")] or [Authorize(Policy = "ApiUser")]in the controller but every time I try I get unauthenticated using postman.
What am I missing? how to make roles-based authentication based on the tutorial?
Startup
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
services.AddAuthorization(options =>
options.AddPolicy("admin", policy => policy.RequireRole("admin"))
);
Auth Controller
// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
//return null;
return BadRequest(Error.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
var id = identity.Claims.Single(c => c.Type == "id").Value;
var user = await _userManager.FindByIdAsync(id);
IList<string> role = await _userManager.GetRolesAsync(user);
var jwt = await Tokens.GenerateJwt(identity, role[0], _jwtFactory, credentials.UserName, _jwtOptions, new JsonSerializerSettings { Formatting = Formatting.Indented });
return new OkObjectResult(jwt);
}
I tried with all method and none of them working
[Authorize(Policy = "ApiUser")]
[HttpGet("getPolicy")]
public string GetPolicy()
{
return "policyWorking";
}
[Authorize(Roles = "admin")]
[HttpGet("getAdmin")]
public string GetAdmin()
{
return "adminWorking";
}
[Authorize ]
[HttpGet("getAuthorize")]
public string GetAuthorize()
{
return "normal authorize Working";
}
Firstly,be sure you have add json string in your appsettings.json otherwise you would always get 401 unauthorized:
"JwtIssuerOptions": {
"Issuer": "webApi",
"Audience": "http://localhost:5000/"
}
What am I missing? how to make roles-based authentication based on the tutorial?
1.If you want to use the following way to register the service:
services.AddAuthorization(options =>
options.AddPolicy("admin", policy => policy.RequireRole("admin"))
);
The authorize attribute should be like below:
[Authorize(Policy = "admin")]
2.If you want to use the following way:
[Authorize(Roles = "admin")]
You need to remove the service from Startup.cs:
//services.AddAuthorization(options =>
// options.AddPolicy("admin", policy => policy.RequireRole("admin"))
//);
Then,don't forget to add claim with role in JwtFactory.GenerateEncodedToken like below:
public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Rol),
identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Id),
new Claim(ClaimTypes.Role,"admin")
};
//...
}
I've made mine using this tutorial.
It also works with .Net Core 3.1.
#Update
I'm using policies for that:
services.AddAuthorization(options => {
options.AddPolicy(Policies.RequireAny, policy =>
policy.RequireClaim(JwtClaimTypes.Role, Enum.GetNames(typeof(AvailableRoles))));
options.AddPolicy(Policies.RequireAdmin, policy =>
policy.RequireClaim(JwtClaimTypes.Role,
AvailableRoles.Admin.ToString()));
});
And in Controller
[HttpDelete("{id:int:min(1)}")]
[Authorize(Policy = Policies.RequireAdmin)]
public async Task<IActionResult> Delete(int id) {
// CODE
return Ok();
}
and in creating JWT Token:
// Other claims and code...
claims.Add(new Claim(JwtClaimTypes.Role, roles[0]))
var token = new JwtSecurityToken(
issuer:_appSettings.AppUrl,
audience:_appSettings.AppUrl,
claims: claims,
expires: expires,
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);

OAuth bearer token validation by Authorize Attribute

I'm stuck with OAuth token authorization. I have configured OAuth and I have my own OAuth server provider.
Configuration code:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AuthorizeEndpointPath = new PathString("/authorize"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
Server provider:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
When I'm sending: grand_type=password, username=MyUserName, password=MyPassword to the OAuth token endpoint "localhost/token", it's nicely creating my OAuth bearer token. But from here, I have no idea how to use this generated token, where it is stored (how to get it), and how to make a success validation using [Authorize] attribute on ASP.NET MVC controller. I just simply want to use my generated token, when I'm going from one view to another, that has [Authorize] attribute, and pass through it successfully. How can I achive this ?
Add new overriden function in "SimpleAuthorizationServerProvider" class.
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
Then you can get token in json object.
Implement the following workflow :
Retrieve an access token via password grant-type
Get user information by passing this access token. Link : http://openid.net/specs/openid-connect-core-1_0.html#UserInfo
Store the claims into the cookie

how to use SignInAsync in webapi

I am trying to login user through webapi.
My apicontroller function is:
public async Task<IHttpActionResult> Login(string email, string password)
{
ApplicationDbContext ctx = new ApplicationDbContext();
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(ctx);
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
var user = await UserManager.FindAsync(email, password);
if (user != null)
{
await SignInAsync(user, true); // The name 'SignInAsync' does not exist in current context
return Ok("OK");
}
return Ok("Error");
}
I want to write methods of signup, login, and logout in webapi but i am stuck at SignInAsync. Am I missing library reference? Or how to use this in webapi?
SignInAsync is a method of SignInManager class not controller class write this instead:
await HttpContext.Current.GetOwinContext()
.Get<ApplicationSignInManager>().SignInAsync(user, true, false);

How to add roles to users in ASP.NET with OWIN

I'm new in ASP.NET and I'm making a simple project to practice. I started with a simple MVC project without authentication because it adds many code that I didn't understand.
But now I want to add a membership to my sistem so I followed this guide:
http://benfoster.io/blog/aspnet-identity-stripped-bare-mvc-part-1
http://benfoster.io/blog/aspnet-identity-stripped-bare-mvc-part-2
But I don't know where I can add a role to the user entity..
My Startup class is this:
public class Startup
{
public static Func<UserManager<Usuario>> UserManagerFactory { get; private set; }
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie"
});
UserManagerFactory = () =>
{
var usermanager = new UserManager<Usuario>(new UserStore<Usuario>(new Vetpet3Context()));
usermanager.UserValidator = new UserValidator<Usuario>(usermanager)
{
AllowOnlyAlphanumericUserNames = false
};
return usermanager;
};
}
}
And I'm creating (for now) my users with this Action:
[HttpPost]
public async Task<ActionResult> Register(RegisterModel model)
{
if (!ModelState.IsValid)
{
return View();
}
var user = new Usuario
{
UserName=model.correo,
correo = model.correo
};
var result = await userManager.CreateAsync(user, model.password);
if (result.Succeeded)
{
await SignIn(user);
return RedirectToAction("index", "Home");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
return View();
}
So everything works fine at this point, but I need to add different roles to the users , and this is the question, how I implement this part? I've read many guides but everyone does different things and I'm not sure how to add a role to my new users and how to claim these roles when they log in
to add role to the user after he successfully register :
if (result.Succeeded)
{
var roleResult = userManager.AddToRole(user.Id, "Admin");
await SignIn(user);
return RedirectToAction("index", "Home");
}
to check if the user in role :
userManager.IsInRole(user.Id, "Admin");
or even simpler in any ASP.NET MVC [Authorized] controller :
User.IsInRole("roleName");

Resources