Dynamic User Claims in ASP.NET Identity EF - c#

I'm working on an authentication system that uses ASP.NET Identity with Entity Framework, and I want to have a few claims that are computed values instead of being hardcoded into the claims table.
When a user logs in, how can I add dynamic claims to that login session without actually adding them to the claims table?
For example, I may want to store each user's DOB, but I want add IsBirthday as a claim if the login date matches the user's DOB. I don't want to have to store a "IsBirthday" claim for each user since it changes daily for everyone.
In my code, I use this to log in:
var signInResult = await SignInManager.PasswordSignInAsync(username, password, false, false);
After this is called I can reference the ClaimsPrincipal, but the Claims property is an IEnumerable, not a List, so I can't add to it.
EDIT: I should also mention I am using the Microsoft.AspNet.Identity.Owin libraries.

OK, everyone, I did a bit of digging into the classes provided in ASP.NET Identity and found the one I needed to override. The SignInManager class has a CreateUserIdentityAsync method that does exactly what I was wanting. The following code added the IsBirthday claim to my identity but didn't store it in the database.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public override async Task<System.Security.Claims.ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
var identity = await base.CreateUserIdentityAsync(user);
identity.AddClaim(new System.Security.Claims.Claim("IsBirthday", user.DOB.GetShortDateString() == DateTime.Now.GetShortDateString()));
return identity;
}
// ... EXCLUDING OTHER STUFF LIKE CONSTRUCTOR AND OWIN FACTORY METHODS ...
}

Related

Blazor WASM application ClaimsPrinicpal and roles claim

I am trying to build a Blazor WASM application that can display different UI elements depending on the value(s) in the roles claim of the token received from Azure AD.
I have a simple debug view where I iterate all claims:
#foreach(var claim in #context.User.Claims)
{
#claim
<br/>
}
Here I can clearly see the user has the following claim:
roles: ["Developer"]
This is received from the AAD app registration where I have assigned the role Developer to my own user.
I would expect any of these statements to then return true:
context.User.HasClaim(ClaimTypes.Role, "Developer") // false
context.User.IsInRole("Developer") // false
So I wrote a custom implementation and parse the claim myself:
#if(context.User.Identity?.IsAuthenticated)
{
var rolesValue = context.User.Claims.Where(c => c.Type == "roles").First().Value; // The roles claim value is an array as a string
// Deserialize the string into a list
var roles = JsonSerializer.Deserialize<List<string>>(rolesValue);
// Print out all roles
Console.WriteLine("Roles:");
foreach(var role in roles!)
{
Console.WriteLine(role); // Prints 'Developer'
}
}
All of these snippets are run inside the following Blazor components:
<AuthorizeView>
<Authorized>
</Authorized>
</AuthorizeView>
The entire application is set up using the following tutorial.
How come I have to do this custom claim interpretation when I clearly have the claim for the user? Just Googling this issue returns so many results but I still haven't been able to solve it. The documentation here uses the method in a slightly different way, but why can I enumerate the claim in my context but still not use any of the utility methods on the User?
What am I missing here?
EDIT:
Implemented the sample from docs:
#using Microsoft.AspNetCore.Authorization
#using Microsoft.AspNetCore.Components.Authorization
#inject IAuthorizationService AuthorizationService
protected async override Task OnInitializedAsync()
{
var user = (await authenticationStateTask).User;
if (user.IsInRole("Developer"))
{
Console.WriteLine("Developer role from OnInitialized");
} else
{
Console.WriteLine("No role from OnInitialized"); // Always gets here, even after logging/in out or using private browsing
}
}
The enumerated claims still list the developer role in the roles claim: roles: ["Developer"].
Seems like there is a ton of ways to solve this depending on your auth scenario. For my scenario, I needed to know the role when authenticating in a SPA against Azure AD. The following instructions solved that.
I only made one modification to those instructions, and that was to the CustomAccountFactory. I set the role claim that enabled me to use to built in User.IsInRole
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
CustomUserAccount account,
RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
if (initialUser.Identity.IsAuthenticated)
{
var userIdentity = (ClaimsIdentity)initialUser.Identity;
foreach (var role in account.Roles)
{
userIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
return initialUser;
}
The problem with using User.IsInRole initially seems like I was receiving an array of roles as a string.
This could also work, but would be fragile:
User.IsInrole(#"[""Developer""]")

Accessing user information via `IHttpContextAccessor` from project created with `dotnet new react -au Individual`?

Background
I've been following the documentation for using IdentityServer4 with single-page-applications on ASP.NET-Core 3.1 and as such created a project via the dotnet new react -au Individual command.
This creates a project which uses the Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet package.
So far it's been really great and it got token-based authentication for my ReactJS application working without any pain!
From my ReactJS application, I can access the user information populated by the oidc-client npm package such as the username.
Also, calls to my Web APIs with the [Authorize] attribute work as expected: only calls with a valid JWT access token in the request header have access to the API.
Problem
I'm now trying to access basic user information (specifically username) from within a GraphQL mutation resolver via an injected IHttpContextAccessor but the only user information I can find are the following claims under IHttpContextAccessor.HttpContext.User:
nbf: 1600012246
exp: 1600015846
iss: https://localhost:44348
aud: MySite.HostAPI
client_id: MySite
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: (actual user GUID here)
auth_time: 1600012235
http://schemas.microsoft.com/identity/claims/identityprovider: local
scope: openid
scope: profile
scope: MySite.HostAPI
http://schemas.microsoft.com/claims/authnmethodsreferences: pwd
The same issue happens for Web API controllers as well.
Details
MySite is the namespace of my solution and is also what I have defined as a client in my appsettings.json file:
{
"IdentityServer": {
"Clients": {
"MySite": {
"Profile": "IdentityServerSPA"
}
}
}
}
My web application project's name is MySite.Host so MySite.HostAPI the name of the resource and scope that are automatically generated by calling AuthenticationBuilder.AddIdentityServerJwt().
... this method registers an <<ApplicationName>>API API resource with IdentityServer with a default scope of <<ApplicationName>>API and configures the JWT Bearer token middleware to validate tokens issued by IdentityServer for the app.
Research
According to a few answers on Stack Overflow, adding IdentityResources.Profile() resource via IIdentityServerBuilder.AddInMemoryIdentityResources() should do the trick but it looks like it's already available via the claims I posted above (scope: profile).
I nevertheless tried it but the result is that the authentication flow becomes broken: the redirect to the login page does not work.
All of the answers I've found also make a reference to a Config class like in this demo file which holds configurations that are mainly fed to IIdentityServerBuild.AddInMemory...() methods.
However, it seems that Microsoft.AspNetCore.ApiAuthorization.IdentityServer does most of this in its implementation and instead offers extendable builders to use.
From the IdentityServer documentation, I don't believe I need to add a Client because the access token already exists. The client ReactJS application uses the access_token from oidc-client to make authorised calls to my Web APIs.
It also doesn't appear like I need to add a Resource or Scope for the username information because I believe these already exist and are named profile. More to this point is that the documentation for "IdentityServerSPA" client profile states that:
The set of scopes includes the openid, profile, and every scope defined for the APIs in the app.
I also looked at implementing IProfileService because according to the documentation this is where additional claims are populated. The default implementation is currently being used to populate the claims that are being requested by the ProfileDataRequestContext.RequestedClaimTypes object and this mechanism already works because this is how the ReactJS client code receives them. This means that when I'm trying to get the user claims from ASP.NET-Core Identity, it's not properly populating ProfileDataRequestContext.RequestedClaimTypes or perhaps not even calling IProfileServices.GetProfileDataAsync at all.
Question
Considering that my project uses Microsoft.AspNetCore.ApiAuthorization.IdentityServer, how can I view the username from my ASP.NET-Core C# code, preferably with IHttpContextAccessor?
What you need to do is to extend the default claims requested by IdentityServer with your custom ones. Unfortunately, since you're using the minimalistic IdentityServer implementation by Microsoft, the correct way of making the client request the claims isn't easy to find. However, assuming you have only one application (as per the template), you could say that the client always wants some custom claims.
Very important first step:
Given your custom IProfileService called, say, CustomProfileService, after these lines:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
you have to get rid of the implementation used in the scaffolded template, and use your own:
services.RemoveAll<IProfileService>();
services.AddScoped<IProfileService, CustomProfileService>();
Next, the actual implementation of the custom IProfileService isn't really hard if you start from Microsoft's version:
public class CustomProfileService : IdentityServer4.AspNetIdentity.ProfileService<ApplicationUser>
{
public CustomProfileService(UserManager<ApplicationUser> userManager,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory) : base(userManager, claimsFactory)
{
}
public CustomProfileService(UserManager<ApplicationUser> userManager,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
ILogger<ProfileService<ApplicationUser>> logger) : base(userManager, claimsFactory, logger)
{
}
public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
string sub = context.Subject?.GetSubjectId();
if (sub == null)
{
throw new Exception("No sub claim present");
}
var user = await UserManager.FindByIdAsync(sub);
if (user == null)
{
Logger?.LogWarning("No user found matching subject Id: {0}", sub);
return;
}
var claimsPrincipal = await ClaimsFactory.CreateAsync(user);
if (claimsPrincipal == null)
{
throw new Exception("ClaimsFactory failed to create a principal");
}
context.AddRequestedClaims(claimsPrincipal.Claims);
}
}
With those two steps in place, you can start tweaking CustomProfileService's GetProfileDataAsync according to your needs. Notice that ASP.NET Core Identity by default already has the email and the username (you can see these in the claimsPrincipal variable) claims, so it's a matter of "requesting" them:
// ....
// also notice that the default client in the template does not request any claim type,
// so you could just override if you want
context.RequestedClaimTypes = context.RequestedClaimTypes.Union(new[] { "email" }).ToList();
context.AddRequestedClaims(claimsPrincipal.Claims);
And if you want to add custom data, for example, the users first and last name:
// ....
context.RequestedClaimTypes = context.RequestedClaimTypes.Union(new[] { "first_name", "last_name" }).ToList();
context.AddRequestedClaims(claimsPrincipal.Claims);
context.AddRequestedClaims(new[]
{
new Claim("first_name", user.FirstName),
new Claim("last_name", user.LastName),
});
User information can be retrieved via the scoped UserManager<ApplicationUser> service which is set up by the project template. The users's claims contains "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" (ClaimTypes.NameIdentifier) whose value is the user identifier. UserManager<>.FindByIdAsync() can then be used to retrieve the ApplicationUser associated with the user and which contains additional user information.
Note that this contacts the user store each time it's invoked. A better solution would be to have the extra user information in the claims.
First, explicitly add the IHttpContextAccessor service if you haven't already by calling services.AddHttpContextAccessor();
From within an arbitrary singleton service:
public class MyService
{
public MyService(
IHttpContextAccessor httpContextAccessor,
IServiceProvider serviceProvider
)
{
var nameIdentifier = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
using (var scope = serviceProvider.CreateScope())
{
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var user = await userManager.FindByIdAsync(nameIdentifier);
// Can access user.UserName.
}
}
}
UserManager<ApplicationUser> can be accessed directly within Razor pages and Controllers because these are already scoped.

JWT "self" authentication in Net Core 2.2 MVC WebApi

I want to improve my API's security with some sort of "self" policy to validate the call to some user actions (like DELETE user) is made by the same user the token was issued to. Is there a way to do this in a similar way to the policy based authorization?
I have a .Net Core 2.2 with MVC WebAPI running on Kestrel. I have users, roles and user-roles and I have token-based authentication with roles enabled. I can issue tokens and validate then with the "Authorize" attribute in the controllers. However, I've been looking for a way to validate that some actions to users are made only by the users itself, a "self" authentication policy to validate that, for example, user 3 is trying to delete user 3 and only user 3. I've dug up to the claims and everything and I know I can make a simple service passing the claims and the validating it but I wanted to do it in a smoother way similar to the policy-based or role-based authentication. I don't know if I can make it with some sort of middleware or something but it would be great to be able to make it as clean as possible.
[Edit]
The main purpose is to avoid users to delete resources created by other users and make them be able only to delete resources created by themselves.
[Edit2 - Solution]
Thanks to Paul Lorica's Answer I can now describe how I did it.
The first thing is to create a Requirement and a Handler similar to the examples provided by Microsoft in the docs. What we do is to add a Claim to the token generation method/service we have and add the ID as NameIdentifier. After that, we inject in the IHttpContextAccessor in the handler. And then we can validate if the ID in the request is the same than the Id in the Claim. So it was very easy.
I'm adding examples of logic to make it work.
PS: Inject IHttpContextAccessor as a singleton in the startup clas or it won't work.
Handler:
public class SelfUserHandler: AuthorizationHandler<SelfUserRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public SelfUserHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SelfUserRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.NameIdentifier))
{
return Task.CompletedTask;
}
var nameIdentifier = context.User.FindFirst(c => c.Type == ClaimTypes.NameIdentifier).Value;
if (_httpContextAccessor.HttpContext.Request.Path.ToString().ToUpper().Contains(nameIdentifier.ToUpper()))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
Requirement
public class SelfUserRequirement : IAuthorizationRequirement
{
public SelfUserRequirement() { }
}
Additional info:
Nate Barbettini Answer here
Joe Audette Answer here
First off, when your code validates against the policy, the policy has no understanding, and does not need to know, what you are doing.
I suppose you can retrieve the context via URL. So say if its a DELETE user/3
then you can create a policy that would check the user's claims that it has an ID == 3.
See the docs here on creating policies and accessing the httpContext
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2
Its a bit of a naive check, I would rather just place that logic within the method of the controller.

Understanding MVC5 UserClaim Table

I have been doing a lot of research but none resulted in helping me understand what is the point of UserClaim Table.
When you create a MVC5 project, there are some default tables created upon your database being registered. I understand the purpose of all of them except UserClaim.
From my understanding, User Claims are basically key pair values about the user. For example if I want to have a FavouriteBook field, I can add that field to the user table and access it. Actually I already have something like that built in. Each of my users have "Custom URL" And so I have created a claim in the following way:
public class User : IdentityUser
{
public string CustomUrl { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.AddClaim(new Claim("CustomUrl", CustomUrl));
return userIdentity;
}
}
public static class UsersCustomUrl
{
public static string GetCustomUrl(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("CustomUrl");
return (claim != null) ? claim.Value : string.Empty;
}
}
Above basically allows me to access the CustomUrl by simply calling User.Identity.GetCustomUrl()
The above code won't write to the UserClaims table as the value exists in the Users Table. So what is the point of this table?
I am speculating that maybe I should add CustomUrl to UserClaims and somehow bind that to identity and that may what it is for? I would love to know the answer!
Claims are really useful in cases where you present multiple ways in which your users can register / sign on with your website... in particular, I'm talking about third-party authentication with organisations such as Google, Facebook and Twitter.
After a user has authenticated themselves through their chosen third party, that third party will disclose a set of claims to you, a set of information that describes the user in a way that you can identify them.
What information the claims will contain varies from provider to provider. For example, Google will share the users email address, their first name, their last name but compare that to Twitter... Twitter doesn't share any of that, you receive the identifier of their Twitter account along with their access tokens.
Claims based authentication provides a simple method to facilitate all this information, whilst the alternative may very well have meant creating tables in your database for each individual provider you worked with.

C# IUserStore implementation GetPasswordHash

By default, MVC 5 Single Page Application uses EntityFramework to store users and passwords for authentication.
In my scenario, I must use an existing homemade AuthenticationService.
I decided to create a custom IUserStore. I then must implement the GetPasswordHashASync to validate credentials.
Our architect considers this as a security breach but I do not agree with this. I then would like to get your opinion about this.
What is the difference between getting the PasswordHash for the database of another service on the same server node. In my opinion, I dont this it is a security breach...
Here's some code to demonstrate how it works.
The user logs in with his credentials so it calls the Login Method of my AccountController. Then, it calls the UserManager FindUserAsync:
var user = await UserManager.FindAsync(model.Email, model.Password);
Since I create my own IUserStore, I call our service (WFC) like this:
if (client.IsUsernameExists(userName, remoteInfo, out messages))
{
user = new ApplicationUser() { Email = userName, Username = userName};
}
Under the hood it then calls the GetPasswordHashAsync. My implementation then call our service again:
passwordHash = client.GetPasswordHash(user.Username, RemoteInfo, out messages);
Any thoughts?
The interface you are looking to implement is the IUserPasswordStore. Nothing wrong with that.
This is the correct way of implementing the IdentityStores of OWIN (and probably other authentication frameworks).
I've implemented my own UserStore for a MongoDB implementation of OWIN. Here is my implementation of the IUserPasswordStore
public Task SetPasswordHashAsync(TUser user, string passwordHash)
{
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
public Task<string> GetPasswordHashAsync(TUser user)
{
return Task.FromResult(user.PasswordHash);
}
The password hash is stored in the DB, so when you pull the user from the DB, it has a property which is the hash. So the appropriate implementation for GetPasswordHashAsync, is to return the hash from the user object.

Categories