Adding UserClaims During Startup.cs Seed Database - asp.net-core

If a user signs in thru my GUI, all is well.
I use SignInManager to sign them in.
I create some claims.
I create a JwtSecurityToken with the claims attached.
I return JWT to client and they use it in header of future Http requests.
I have created a 'Provider' that accesses some of those claims on behalf of backend services. Those backend services are injected with the provider. When the service wants to know some info about the claim it asks the provider, which accesses the HttpContext, extracts claims and provides the requested value to the backend service. It works well.
My challenge is that I have now added a SeedData routine that is called during startup. It will create a user and then seed some business data (in the context of that new user).
The problem I have is that because this has not come from a client request, my HttpContext is NULL during SeedData routine called from startup.cs.
I have tried (within SeedData) to
// Sign in User
SignInResult signInResult = await signInManager.PasswordSignInAsync("username", "password", false, false);
// Create Claim
CustomClaim claim = new CustomClaim();
claim.ValueForBackend = "Foo";
// Add to Claims List
List<Claim> claims = new List<Claim>();
claims.Add(new Claim("custom-claim", JsonSerializer.Serialize(claim)));
// Create Claims Identity
ClaimsIdentity claimsId = new ClaimsIdentity(claims);
// Add the Claims Identity to current ClaimsPrincipal
HttpContextAccessor.HttpContext.User.AddIdentity(claimsId);
thinking that this would put the claims on the context so my provider can extract "Foo" when asked by the backend service.
However, I am getting error:
System.AggregateException: One or more errors occurred. (HttpContext must not be null.)
---> System.InvalidOperationException: HttpContext must not be null.
at Microsoft.AspNetCore.Identity.SignInManager`1.get_Context()
which appears to be thrown as soon as I try the initial sign in during start-up. My take-away from this is the there is not an HttpContext during startup.cs execution.
Is there a way during startup to:
Create initial user
Sign In as that user
Add some claims to that user
Perform seeding (calling backend services) in the context of that user
I could hack around it by creating a special provider of "Foo" that does not get the value from a claim, but is instead hard-fed directly from startup.cs, but wondered if there is a way to set up an HttpContext with claims during startup.cs.

Related

How to handle requirements based resource authorization when there are many resource permissions with respect to performance?

I've got a web service which makes use of JWT based authentication. In some samples i have seen that permissions are added to the claims identity and then queried in the AuthorizationHandler.
Is this the way to go for production level solutions too? Since the permissions are encoded in the JWT token i am concerned with the performance implications of having a very big JWT token.
Given that at some point you could end up with a JWT token of 1mb+ and above, this may lead to very bad performance if the web api client has insufficient upload speed.
Are there best practices to deal with a large amount of permissions? (Currently i am thinking that the way to go probably would be doing cached db queries to access permission grants)
As you've noticed, shoving too many claims into a JWT brings problems with it. Some servers will stop parsing the headers if they exceed a certain size. In our tests, IIS stopped accepting requests after ~150 scope claims (~2KB of JWT), so you don't have a lot of room to play with.
You should limit the usage of JWT to authentication. If the application has access to authorization data, you can use the token to fetch the permission claims for that user from the database or some authorization/policy service.
ASP.NET Core provides an interface for these kinds of scenarios. If you implement an IClaimsTransformation and register it, ASP.NET Core will call it when it authenticates the user. Then you'll have a chance to populate the ClaimsPrincipal with the authorization claims for the user.
One thing you need to look out for is that it is called every time an authentication occurs, so you need to check if you've already populated the claims to prevent duplicating claims. If you do it right, you'll need to perform a single query for every request to fetch the authorization claims for the user.
A sample implementation:
public class LoadUserClaimsTransformer : IClaimsTransformation
{
private AppDbContext _db;
public LoadUserClaimsTransformer(AppDbContext db)
{
_db = db;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
// check if we've already populated the claims
if (principal.HasClaim(c => c.Type == "permission"))
{
return principal;
}
if (principal.Identity is ClaimsIdentity identity)
{
var userId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
var permissions = await _db.Set<Permission>().Where(it => it.UserId == userId).ToListAsync();
foreach (var permission in permissions)
{
identity.AddClaim(new Claim("permission", permission.Key));
}
}
return principal;
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IClaimsTransformation, LoadUserClaimsTransformer>();
// ...
}
Once the claims are added to ClaimsPrincipal, you can use [Authorize] annotations for declarative checks, or IAuthorizationService for imperative ones, or for resource-based authorizations.
Resources:
https://sdoxsee.github.io/blog/2020/01/06/stop-overloading-jwts-with-permission-claims
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-5.0
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.iclaimstransformation?view=aspnetcore-5.0
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-5.0

Custom flow - delegation

I was wondering if it is possible to implement with openiddict a delegation grant type similar to the one implemented here with Identity Server.
var result = await _validator.ValidateAccessTokenAsync(userToken);
if (result.IsError)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
Is there any equivalent method to ValidateAccessTokenAsync in openiddict in order to validate the token and access some of its properties?
Implementation of standard token exchange is tracked by https://github.com/openiddict/openiddict-core/issues/1249.
In the meantime, you can override the default ValidateTokenParameter handler to work with your custom grant and extract the access token from the customer parameter you use:
https://github.com/openiddict/openiddict-core/blob/422d8979adb8cdebc6c8c8e14faa1d736208271e/src/OpenIddict.Server/OpenIddictServerHandlers.cs#L168
Then, you can call the IOpenIddictServerDispatcher.DispatchAsync() method with an instance of ProcessAuthenticationContext to trigger an authentication event. If IsRejected is true, this means the token is not valid. Otherwise, you'll be able to access its claims principal.

Authorize user based on API-key supplied in request header in ASP.NET Core

I'm trying to rewrite some authorization I currently have for ASP.NET 4.6 in ASP.NET Core.
I understand that Authorization has changed a bit, and I find it difficult to implement my very simple auth strategy in ASP.NET Core.
My requirements:
Every request to the server should include a header called "key". Based on the value of that key, I will be able to query the database and check whether that key represents a regular user or an admin user. If the request does not contain a valid key, the request is not authorized.
How would I implement this in ASP.NET Core? Every example I find seems totally overkill for my needs.
In ASP.NET 4.6 I used my own custom AuthorizeAttributes to use on my controllers, e.g.
[User]
public IHttpActionResult DoSomethingOnlyUsersCanDo() {}
and
[Admin]
public IHttpActionResult DoSomethingOnlyAdminsCanDo() {}
Can I do the same in ASP.NET Core?
In ASP.NET Core, it is recommended that you do not inherit from AuthorizeAttribute. Instead, you can make custom authorization policies: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims.
You will need to have an authentication handler that creates a ClaimsIdentity for the user based on the header. Then you can make policies that assert the existence of certain claims on the user.
You can find an implementation of Basic authentication here: https://github.com/blowdart/idunno.Authentication.
Note Barry's comment there of course:
It is meant as a demonstration of how to write authentication middleware and not as something you would seriously consider using.
Its core is in BasicAuthenticationHandler, which inherits from AuthenticationHandler<BasicAuthenticationOptions>.
The principal in this implementation is created in the developer-made event callback, in the sample it is here:
if (context.Username == context.Password)
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.Username, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.Username, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
The authentication ticket is then created in the handler after calling this callback based on the principal:
var ticket = new AuthenticationTicket(validateCredentialsContext.Principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
I also made an article on implementing custom authentication schemes: Creating an authentication scheme in ASP.NET Core 2.0.

Custom Auth request in ServiceStack for multi-tenancy

I am already using a custom authentication provider in my ServiceStack based web services application.
I'm overriding the Authenticate method, and validating my user against one of multiple backend tenant databases. I currently determine the tenant database by matching an API key to a database string.
public override object Authenticate(
IServiceBase authService,
IAuthSession session,
Auth request) // <- custom object here, MyCustomAuth request
{
// ...
}
This works when each application is for a single tenant (a tenant/customer can build their own application and use that API key). Moving forward I want to build a multi-tenant mobile application. Thus the API key method cannot be used because I can't expect each user to type it in, hence I can't determine which tenant is using the application.
I wanted to alter the Auth object so that I could include the TenantId (provided by the user on login). However, I can't see how I can customize that object.
Is there anyway to customize that Auth object, or do I have to find an alternative solution?
You can't modify the built-in Authenticate Request DTO used, but you can use its Dictionary<string, string> Meta property to send additional metadata with the Authenticate request, e.g:
client.Post(new Authenticate {
...
Meta = new Dictionary<string,string> {
{"TenantId", tenantId},
}
}
Alternatively you can send additional info in the QueryString or HTTP Headers and access the IRequest with:
var tenantId = authService.Request.QueryString["TenantId"];

How to store custom information in SecurityContext of spring-security?

In my application I'm using LDAP authentication. But i'm also have 2 remote services which requires authentication via method login(username, password). The method returns security token which makes me able to invoke another methods, i.e. I should pass security token to service methods as first argument.
So I'd like to get these security tokens immediately after successful login using LDAP and store them in SecurityContext. I tried to use authentication-success-handler-ref of form-login element. Using the handler I replace Authentication object in the SecurityContext with custom AuthenticationToken that holds not only password but also security tokens. But in this case I have an exception that no authentication provider supports this class of token.
I know it's also possible to store tokens in the HTTP session but in this case I have to pass session to service object, so I'd like to store the tokens in SecurityContext.
What is the best approach to handle service security token?
I often use the Authentication.getDetails() object to store additional info that may not be directly linked to the user per say. So you can store any object you want in that field (a HashMap for instance) and it shares the Authentication object life cycle.
HashMap<String, Object> info = new HashMap<String, Object>();
info.put("extraInfo", "info");
auth.setDetails(info);
...
Map<String, Object> i = (Map<String, Object>)SecurityContextHolder.getContext().getAuthentication.getDetails();
Your implementation of 'UserDetails' may hold any additional data. This is what gets stored in the SecurityContext which is later accessible after successful login.
You can later access it as (Assumes MyUserDetails implements UserDetails)
Object principal = SecurityContextHolder.getContext().getAuthentication();
if (principal instanceof MyUserDetails) {
MyUserDetails mud = (MyUserDetails) principal;
mud.getMyData(); //Extract your additional data here
}