I'm using asp.net identity with ClaimsPrincipal and ClaimsIdentity. The identity is created by a custom usermanager and sent to the clients as a bearer token.
All works fine, but I would like to instruct asp.net to use a custom claim type for role, instead of the standard System.Security.Claims.ClaimTypes.Role (http://schemas.microsoft.com/ws/2008/06/identity/claims/role).
Is it possibile?
Edit: I would like to use the standard Authorize attribute with role constructor, ex: Authorize(Roles="staff") and let the framework to check the claims, but my custom role claim type ("myapproleclaim") instead of the standard one.
You can do things like,
public class CustomPrinciple : ClaimsPrincipal
{
public CustomPrinciple(ClaimsIdentity identity) : base(identity)
{
}
public override bool IsInRole(string role)
{
return HasClaim("myRoleClaimType", role);
}
}
[TestClass]
public class CustomRoleTest
{
[TestMethod]
public void testing_custom_role_type()
{
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim("myRoleClaimType", "role1"));
var principle = new CustomPrinciple(identity);
Assert.IsTrue(principle.IsInRole("role1"));
Assert.IsFalse(principle.IsInRole("role2"));
}
}
Related
I have a Blazor client (WASM) app that integrates with AAD B2C for authentication.
After authentication, I want to call my own API for further authorisation information. The reason I want to do this rather than getting B2C to call my API is I will have series of different apps using the same B2C, with different claims, roles and other information etc.
I've tried every tutorial I can find, but nothing seems to wire up.
My Program.cs has this:
builder.Services.AddMsalAuthentication(options =>
{
var settings = config.AzureAdB2C;
var authentication = options.ProviderOptions.Authentication;
authentication.Authority = $"{settings.Instance}{settings.Domain}/{settings.SignInPolicy}";
authentication.ClientId = settings.ClientApplicationId;
authentication.ValidateAuthority = false;
options.ProviderOptions.DefaultAccessTokenScopes.Add($"{settings.ServerAppId}/{settings.DefaultScope}");
//options.ProviderOptions.Cache.CacheLocation = "localStorage";
});
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
And for example, I've tried this:
builder.Services.AddScoped<IClaimsTransformation, UserInfoClaims>();
public class UserInfoClaims : IClaimsTransformation
{
private static IEnumerable<SimpleClaim> roles;
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
...
But it doesn't get hit.
Is it possible to rewrite claims in WASM after B2C authentication?
And if not, is there an event I can wire up to after successful authentication to just manage my own role-like alternative?
This can be done by implementing your own AccountClaimsPrincipalFactory
public class ExampleClaimsPrincipalFactory<TAccount> : AccountClaimsPrincipalFactory<TAccount>
where TAccount : RemoteUserAccount
{
public ExampleClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor)
: base(accessor)
{
//Any dependency injection or construction of objects
//inside this constructor usually leads to wasm memory exceptions
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(TAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
if (account != null)
{
//Add logic here to get custom user information
//Add Claims to the user identity like so
var identity = user.Identity as ClaimsIdentity;
identity.AddClaim(new Claim("type", "value"));
}
return user;
}
}
Then on start up when adding authentication you do the following
builder.Services.AddMsalAuthentication()
.AddAccountClaimsPrincipalFactory<ExampleClaimsPrincipalFactory<RemoteUserAccount>>();
I love the way asp.net identity lets you neatly manage role based authorizations by just adding an annotation at the top of your controller method. But what if you are not using entity framework? What if you are using ADO.NET with ASP.NET Core API? How do you manage Role Based Authorization?
[Authorize(Roles = Role.Admin)]
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
Perhaps we could mimic this by creating ActionFilter and checking what is the value of global variable that holds user's role? What are my options?
This is a great case for policy-based authorization. You'll use a custom IAuthorizationHandler and IAuthorizationRequirement. See the documentation here: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.0
I've done this in a project recently. You create named policies, in your case something like "Admin". Your AuthorizationHandler will access the current user identity and can look up their current roles by injecting your services into the handler. Then you decorate your controller / actions with [Authorize(Policy = "Admin")].
In my project, my AuthorizationHandler and AuthorizationRequirement look like this:
public class CreateClientHandler : AuthorizationHandler<CreateClientRequirement, CreateClientRequest>, IAuthorizationRequirement
{
private readonly IStudioService _studioService;
public CreateClientHandler(
IStudioService studioService
)
{
_studioService = studioService;
}
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CreateClientRequirement requirement,
CreateClientRequest resource
)
{
var userIdClaim = context.User.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Name);
if (userIdClaim == null)
{
context.Fail();
return Task.CompletedTask;
}
if (resource != null)
{
var userId = int.Parse(userIdClaim.Value);
if (!UserIsAdmin(userId))
{
context.Fail();
return Task.CompletedTask;
}
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class CreateClientRequirement : IAuthorizationRequirement {}
In my ConfigureServices, in Startup.cs:
services.AddAuthorization(options =>
{
options.AddPolicy("CreateClient", policy =>
policy.Requirements.Add(new CreateClientRequirement()));
});
Once everything is configured, I can decorate controller actions with [Authorize(Policy = "CreateClient")].
I have an application that is written using C# on the top of the ASP.NET MVC 5 framework.
Instead of using the built-in ClaimsPrincipal implementation of the IPrincipal interface, I want to used MyOwnClaimsPrincipal implementation. My MyOwnClaimsPrincipal class implements the IPrincipal interface.
In other words, I want to be able to access MyOwnClaimsPrincipal instance from inside of AuthorizeAttribute class or as the User Property in the controller and the views.
here is an example of what I want to be able to do
public class TestAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// This does not work
MyOwnClaimsPrincipal user1 = filterContext.HttpContext.User as MyOwnClaimsPrincipal;
// Yet, this works which tells me that my mplementation is not being used
ClaimsPrincipal user2 = filterContext.HttpContext.User as ClaimsPrincipal;
}
}
I tried to utilize Application_BeginRequest method in the Global.asax.cs to set the Thread.CurrentPrincipal and HttpContext.Current.User at the begining of each request like so
public void Application_BeginRequest()
{
var user = new MyOwnClaimsPrincipal();//...
Thread.CurrentPrincipal = user;
HttpContext.Current.User = user;
}
Unfortunately, that did not work. The filterContext.HttpContext.User is still set to ClaimsPrincipal and when I cast it as MyOwnClaimsPrincipal a null is returned.
How can I correctly set my one implementation of the IPrincipal to be used in the application?
I think you are to late according to this article Application_BeginRequest is the first event.
Just guessing: It seems the default authentification mechanism isn't checking if already an IPrincipal is assigned, so it overrides yours.
I have done the same using the Application_PostAuthenticateRequest Handler and it works fine:
protected void Application_PostAuthenticateRequest(object sender, EventArgs eventArgs)
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, "42");
claims.Add(new Claim(ClaimTypes.Name, "Hello World"));
var claimsIdentity = new ClaimsIdentity(claims, "MylAuthentificationSheme");
Context.User = new ClaimsPrincipal(claimsIdentity);
}
I made my own custom Role table in my database and I wanted to also create a custom authorize attribute along with it.
Here is what I have so far, but I'm not really sure how to proceed:
private List<RoleModel> Roles;
private IRoleRepository repo;
private ICustomerRepository cust;
public bool CheckRoles(string UserId)
{
try
{
Roles = repo.GetAll().ToList();
CustomerModel Customer = cust.Get(UserId);
int CustomerRole = Customer.RoleId;
RoleModel role = Roles.First(x => x.id == CustomerRole);
}
catch(Exception e)
{
return false;
}
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
string UserId = filterContext.HttpContext.User.Identity.GetUserId();
}
If anyone can help me finish this I would greatly appreciate it.
Thanks!
I think that create a custom AuthorizeAttribute is not a good idea. It is a good practice to use the standard AuthorizeAttribute.
It is a common case to have its own Role table. It is better to override how to set the roles of the identity of your principal and to use the Roles property of AuthorizeAttribute. Set the role as a claim once when the user is logging; it will be better than retrieve the role from database in the custom Authorize attribute at each request.
Set your claim CalimTypes.Role, and then protect your controllers/actions with :
[Authorize(Roles = "admin")]
I created a custom principal class
public class FacebookPrincipal : ClaimsPrincipal
{
public JObject Data { get; set; }
}
And I want to use it. When the user logs in, I tried to set
var fbP = new FacebookPrincipal { Data = user.Data };
Thread.CurrentPrincipal = fbP;
AuthenticationManager.User = fbP;
HttpContext.User = fbP;
It works right after I set it, but when I go ho home/index the value is lost
var user = HttpContext.GetOwinContext().Authentication.User;
var bbbb = this.User;
var cccc = ClaimsPrincipal.Current;
All the above methods return a Principal of type ClaimsPrincipal and casting to FacebookPrincipal returns null.
How do I set a custom principal?
ASP.NET Identity uses default ClaimsIdentityFactory to create before assigning ClaimsIdentity to User and Thread. You should create your own ClaimsIdentityFactory where you can add or manage additional information.
UserManager<IdentityUser> userManager
= new UserManager<IdentityUser>(new UserStore<IdentityUser>());
userManager.ClaimsIdentityFactory
= new MyClaimsIdentityFactory<IdentityUser>();
And the following code to create your implementation for ClaimsIdentity or its subclass.
public class MyClaimsIdentityFactory<IUser> : ClaimsIdentityFactory<IUser> where IUser : IdentityUser
{
public MyClaimsIdentityFactory(): base()
{
}
public override System.Threading.Tasks.Task<System.Security.Claims.ClaimsIdentity> CreateAsync(UserManager<IUser> manager, IUser user, string authenticationType)
{
// Override Creation of ClaimsIdentity and return it.
}
}
Make sure you absolutely need to subclass ClaimsIdentity. You can add additional info as Claims.
You shall use base.CreateAsync and merge the Claims to your created ClaimsIdentity.
•Make sure you absolutely need to subclass ClaimsIdentity. You can add additional info as Claims.
You should be careful about adding additional claims for supplementary information as a side effect can be a change to how the authorization policy will make decisions.
As suggested by #marisks, you can use IUserClaimsStore to store claims issued from third-party for your user. only if the custom claims access is the problem.
Moreover, to persist the identity between two requests, use following code.
// One can create one's own Identity here.
var identity = await
UserManager.CreateIdentityAsync(user,
DefaultAuthenticationTypes.ApplicationCookie);
// Or add custom claims. Claims Stored in IUserClaimStore
// are already populated by above creation.
identity.AddClaim(new Claim("ProfileDATA", "VALUE"));
AuthenticationManager.SignIn(new AuthenticationProperties()
{ IsPersistent = isPersistent }, identity);