How to retrieve access token from ClaimsTransformationContext - asp.net-core

I am using Identity Server 4 to secure an mvc app and all peripheral APIs. During principal creation I'm using the claims transformation middleware, in which I'd like to make a call to a secured API for additional information.
The claims transformation middleware is wired up in the Configure method like so:
app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));
And the ClaimsTransformer class looks like:
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
// meat and potatoes goes here...
return context.Principal;
}
I've tried getting the access token from the ClaimsTransformationContext with the following code within TransformAsync, however this seems to result in an infinite loop back to the TransformAsync method on the ClaimsTransformer class.
var accessToken = context.Context.Authentication.GetTokenAsync("access_token");
Is it possible to retrieve the access token during the .net core claims transformation?
Or is this specific API call something htat shouldn't be done on behalf of the user and better handled one step up and suited for a more server to server approach (client credentials grant)?

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

How do I use HttpClientFactory with Impersonation? Or find another way to get a JWT token from a service based on a Windows Identity?

I have a regular ASP.Net Core web site that users access using Windows Authentication to determine which users can access which pages.
In order to render a page for the user, the site needs to call in to a series of web services to fetch various bits of data. These web services don't use Windows Authentication. Instead, they require the user's JWT Token.
So, our WebSite needs to exchange the user's Windows token for a JWT token. We have a special ExchangeToken web service that accepts a request using Windows Authentication, and returns the user's JWT Token.
The difficulty comes when I want WebSite to call this ExchangeToken web service. I need to call it using Impersonation, so that I get the user's JWT Token back. However, it doesn't appear to be possible to use HttpClient with Impersonation.
Initially, I had planned to do this in WebSite:
Repeatedly...
Impersonate the user
Instantiate an HttpClient
Call the TokenExchange service to get the JWT Token
Dispose the HttpClient
Stop impersonation
Return the token
However, according to what I've read, re-creating an HTTP client for every call is bad practice, and I should be using HttpClientFactory instead.
However, I don't see how this approach can work with Impersonation.
I tried this:
Use HttpClientFactory to create an HttpClient
Repeatedly...
Impersonate the user
Call the TokenExchange service to get the JWT Token
Stop impersonation
Return the token
However, what happens is that, despite the impersonation, all calls to the TokenExchange service are made with the same windows credentials - the credentials of the user who happens to access the web site first. AFAIK, this stems from the way that Windows Authentication works - it performs a token exchange the first time you use an HttpClient, and from then on, all calls for that client use the same token.
One option would be to create a separate client for each user... but I have about 7,000 users, so that seems a bit excessive!
Another option would be to trust the WebSite to fetch the tokens on behalf of the user, using its own account. The problem with this is that it entails trusting the WebSite. If it is compromised by an attacker, then I can't stop the attacker stealing JWT tokens for arbitrary user. Whereas, with the impersonation, the attacker still can't get a user's JWT token without first obtaining their Windows token.
So, is there a way to do impersonation + IHttpClientFactory together? Or is there a better way to approach all this?
(If it matters, my company has its own Windows servers - we're not in the cloud, yet)
To demonstrate the problem with the second approach, I made a test application. It doesn't actually use HttpClientFactory, but it does demonstrate the problem.
I started with a web site that just returns the user who made a call:
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class WhoController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
return User.Identity.Name;
}
}
My client code works like this:
private void CallClient(HttpClient httpClient, string username, string password)
{
LogonUser(username, "MYDOMAIN", password, 2, 0, out IntPtr token);
var accessTokenHandle = new SafeAccessTokenHandle(token);
WindowsIdentity.RunImpersonated(
accessTokenHandle,
() =>
{
string result = httpClient.GetStringAsync("http://MyServer/api/who").Result;
Console.WriteLine(result);
});
}
And my test code invokes it like this:
public void Test()
{
var httpClient = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true });
CallClient(httpClient, "User1", "Password1");
CallClient(httpClient, "User2", "Password2");
}
As described above, I get the following written to the console:
User1
User1
What I want is:
User1
User2
TL;DR: NET Core is doing a lot to fight you on this approach under the hood.
Not entirely an answer on what to do, but hopefully helpful background on the HttpClientFactory approach, based on my understanding of the components.
First, from the ASP NET Core docs in regards to impersonation:
ASP.NET Core doesn't implement impersonation. Apps run with the app's
identity for all requests, using app pool or process identity. If the
app should perform an action on behalf of a user, use
WindowsIdentity.RunImpersonated in a terminal inline middleware in
Startup.Configure. Run a single action in this context and then close
the context.
RunImpersonated doesn't support asynchronous operations and shouldn't
be used for complex scenarios. For example, wrapping entire requests
or middleware chains isn't supported or recommended.
As you call out, there's a lot of progress NET Core has made around how HttpClient instances are handled to resolve socket exhaustion and the expensive operations around the underlying handlers. First, there's HttpClientFactory, which in addition to supporting creating named/typed clients with their own pipelines, also attempts to manage and reuse a pool of primary handlers. Second, there's SocketsHttpHandler, which itself manages a connection pool and replaces the previous unmanaged handler by default and is actually used under the hood when you create a new HttpClientHandler. There's a really good post about this on Steve Gordon's Blog: HttpClient Connection Pooling in NET Core. As you're injecting instances of HttpClient around from the factory, it becomes way safer to treat them as scoped and dispose of them because the handlers are no longer your problem.
Unfortunately, all that pooling and async-friendly reuse makes your particular impersonation case difficult, because you actually need the opposite: synchronous calls that clean up after themselves and don't leave the connection open with the previous credentials. Additionally, what used to be a lower-level capability, HttpWebRequest now actually sits on top of HttpClient instead of the other way around, so you can't even skip it all that well by trying to run the requests as a one off. It might be a better option to look into using OpenID Connect and IdentityServer or something to centralize that identity management and Windows auth and pass around JWT everywhere instead.
If you really need to just "make it work", you might try at least adding some protections around the handler and its connection pooling when it comes to the instance that is getting used to make these requests; event if the new clients per request are working most of the time, deliberately cleaning up after them might be safer. Full disclaimer, I have not tested the below code, so consider it conceptual at best.
(Updated Switched the static/semaphore to a regular instance since the last attempt didn't work)
using (var handler = new SocketsHttpHandler() { Credentials = CredentialCache.DefaultCredentials, PooledConnectionLifetime = TimeSpan.Zero, MaxConnectionsPerServer = 1 })
using (var client = new HttpClient(handler, true))
{
return client.GetStringAsync(uri).Result;
}

Better way to implement authentication in microservice architecture using vert.x

Someone have idea how to implement authentication and autorization process in vert.x microservice architecture thru API Gateway service and standalone user service, where it has own custom Auth provider? Should it use shared data (hazelcast map) to store some user information like name, roles by token or something else? I,m breaking mind..
As part of a microservices architecture, I'd not implement the authentication directly in my vert.x microservice but I'd go through with an API management gateway like tyk or kong for example.
Then I'd only check the permissions of the user over the data in my microservice using any database you want.
You can also dig this track if you really want to handle authentication directly in your microservice.
Having a user service, I wanted all authentication and authorization operations to be performed through it.
In the end, I did this:
jdbcAuth.rxAuthenticate(authInfo)
.map(user -> user.principal().getString("username"))
.flatMap(username -> roleRepository.findByUsername(username).map(roles -> {
JWTOptions jwtOptions = new JWTOptions().setAlgorithm("RS256");
roles.forEach(jwtOptions::addPermission);
LOGGER.info("Token successfully created");
String token = jwtAuth.generateToken(new JsonObject().put("username", username), jwtOptions);
return new JsonObject().put("accessToken", token).put("tokenType", "Bearer");
}))
.subscribe(SingleHelper.toObserver(handler));
For user authentication I use JDBCAuth (and solt hash generation for new user creation)
If there is such user we are getting roles, generate a token with optional claims like "username" and JWTOptions with perrmissions.
Putting into request header Authorization: Bearer --token--
On other microservices we put global JWTAuth handler with pulicKey like:
JWTAuthHandler.create(JWTAuth.create(vertx.getDelegate(), new JWTAuthOptions().addPubSecKey(new PubSecKeyOptions()
.setAlgorithm("RS256")
.setPublicKey(<PUBLIC_KEY>))));
This handler sets the user to the context, and we can get it through context.user () and check the permissions. By the way, we can put this handler along with the addAuthority() method on the route for access checking.

Asp.net 5 web api bearer authentication and multiple areas

I have a webapi backend that several client applications are using. The api is secured with jwt authentication, it is based upon the following example: https://github.com/mrsheepuk/ASPNETSelfCreatedTokenAuthExample. Since I am not yet very comfortable with all the concepts of token based authentication I could use some guidance in this. My issue is that I need my applications to utilize the same api but to limit access for each application to a specific area or controller.
According to the example I can protect methods within an area with:
[Authorize("Api")]
A policy is added in startup with
authOptions.AddPolicy("Api", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) // "Bearer" scheme
.RequireAuthenticatedUser().Build());
For secured requests from the client I typically have an angular 2 app that simply adds the jwt in the headers like so:
headers.append('Authorization', 'Bearer ' + jwt);
I don't know of all the mechanics here but I am assuming that when a secure method is requested, the "Api" attribute decoration is what decides/limits which policy is to be used with a certain route in the api.
What is best practice and how do I extend this to work with individually accessible sections?
You can create an ActionFilterAttribute for Authorization and use it on all the actions.
You can implement the FrameworkAuthorise filter methods as per your requirements.
Global.ApiKey is the unique code for your application to identity you have access to that application or not.
[FrameworkAuthorise(Global.ApiKey, AuthorisationType.None)]
public async Task<IHttpActionResult> Get()
{
// code goes here
}
[FrameworkAuthorise(Global.ApiKey, AuthorisationType.Bearer)]
public async Task<IHttpActionResult> Post()
{
// code goes here
}

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"];