Autofac Injecting ClaimsPrincipal in Blazor Application - c#

I'm facing issues with registering a service with Autofac in a Blazor application. Deep in a dependency I have a UserService which needs access to the current ClaimsPrincipal. I've wrapped that with IPrincipalProvider to avoid registering the ClaimsPrincipal directly.
My issue stems from the fact that in Core the current principal isn't set on program start and the way to get the principal is to register AuthenticationStateProvider which is set by ASP.NET Core. I've gone with ServerAuthenticationStateProvider:
builder.RegisterType<ServerAuthenticationStateProvider>()
.As<AuthenticationStateProvider>().InstancePerLifetimeScope();
This allows access to the logged on user and I can display their name on the website easily.
So to get this principal registered the next entry must use a delegate in order to get the ClaimsPrincipal from the state provider using GetAuthenticationStateAsync(). Being a Task based method I figured I would just mark the lambda with async:
builder.Register(
async c =>
{
var authStateProvider = c.Resolve<AuthenticationStateProvider>();
var authState = await authStateProvider.GetAuthenticationStateAsync()
.ConfigureAwait(false);
return new PrincipalProvider(authState.User);
}).As<IPrincipalProvider>();
but I get the following error:
System.ArgumentException: 'The type 'System.Threading.Tasks.Task`1[Perigee.Framework.Services.Security.PrincipalProvider]' is not assignable to service 'Perigee.Framework.Base.Services.IPrincipalProvider'.'
Given I'm newing up the PrincipalProvider explicitly I'm not really sure why it thinks it's Task. I have to await the authStateProvider.GetAuthenticationStateAsync() call so definitely need the async modifier. If I remove the async and only return new PrincipalProvider(new ClaimsPrincipal()); then the registration works and the lambda resolves at runtime (I've checked with a breakpoint in the lambda) so I know the form of what I'm doing is correct.
Can someone please point out what I'm missing in relation to making the instantiation of the PrincipalProvider not be treated as a Task?
UPDATE:
Well I found a way of doing this using an async task within the Registration body:
builder.Register(
c =>
{
var authStateProvider = c.Resolve<AuthenticationStateProvider>();
var authStateTask = Task.Run(async () => await authStateProvider
.GetAuthenticationStateAsync().ConfigureAwait(false));
var authState = authStateTask.GetAwaiter().GetResult();
return new PrincipalProvider(authState.User);
}).As<IPrincipalProvider>();
Problem is:
System.InvalidOperationException
HResult=0x80131509
Message=GetAuthenticationStateAsync was called before SetAuthenticationState.
Source=Microsoft.AspNetCore.Components.Server
I'm confused about why this wouldn't be available. The site is building the injectable service and passed to the [Inject] property on the page being loaded.
Anyone know how to make this work? I'd would like to put AuthenticationStateProvider as my injected service which is deep in my other dependency, but that library isn't related to ASP.NET at all. It's a CQRS based library for doing all sorts of things, mainly used for Entity Framework queries and commands so putting a dependency to an ASP.NET type makes little sense.
UPDATE AGAIN:
Thinking further registering a different implementation of IPrincipalProvider that received AuthenticationStateProvider on creation seems to be a working solution:
public class DeferredPrincipalProvider : IPrincipalProvider
{
private readonly AuthenticationStateProvider _authenticationStateProvider;
public DeferredPrincipalProvider(
AuthenticationStateProvider authenticationStateProvider)
{
_authenticationStateProvider = authenticationStateProvider;
}
public ClaimsPrincipal ClaimsPrincipal
{
get
{
var authStateTask = Task.Run(async () => await _authenticationStateProvider
.GetAuthenticationStateAsync()
.ConfigureAwait(false));
var authState = authStateTask.GetAwaiter().GetResult();
return authState.User;
}
}
}
Which is wired up with:
builder.RegisterType<ServerAuthenticationStateProvider>().As<AuthenticationStateProvider>()
.InstancePerLifetimeScope();
builder.RegisterType<DeferredPrincipalProvider>().As<IPrincipalProvider>();
I'm not too happy with the Task within the getter, so will switch it to an async method. GetResult() isn't intended to be called from non .NET Framework code anyway according to MS doco.

Related

Need to create service at runtime using ServiceProvider in an Azure Function

I have a tricky requirement where I need create copy of a service that has been created via Constructor DI in my Azure Function
public MyFunction(IMyService myService,
IServiceProvider serviceProvider,
ServiceCollectionContainer serviceCollectionContainer)
{
_myService = tmToolsService;
_serviceProvider = serviceProvider;
_serviceCollectionContainer = serviceCollectionContainer;
}
[FunctionName("diagnostic-orchestration")]
public async Task DiagnosticOrchestrationAsync(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
}
This service has a lot of dependencies so I dont really want go down the manual Activator.CreateInstance route
I have tried 2 different approaches
Approach 1
I have ServiceCollectionContainer. This is filled in Configure of the startup and simply holds the services
public override void Configure(IFunctionsHostBuilder builder)
{
base.Configure(builder);
var services = builder.Services;
services.AddSingleton(s => new ServiceCollectionContainer(services));
}
In my function I call
var provider = _serviceCollectionContainer.ServiceCollection.BuildServiceProvider();
if (provider.GetService<IMyService>() is IMyService myService)
{
await myService.MyMathodAsync();
}
This throws the error
System.InvalidOperationException: 'Unable to resolve service for type
'Microsoft.Azure.WebJobs.Script.IEnvironment' while attempting to activate
'Microsoft.Azure.WebJobs.Script.Configuration.ScriptJobHostOptionsSetup'.'
I believe this could be because although the service collection looks fine (276 registered services) I have seen references online that say that Configure may be unreliable
Approach 2
The second approach is the more conventional one, I just tried to use the service provider injected without making any changes
if (_serviceProvider.GetService<IMyService>() is IMyService myService)
{
await myService.MyMathodAsync();
}
But if I use this approach I get the error
'Scope disposed{no name} is disposed and scoped instances are disposed and no longer availab
How can I fix this?
I have large date range of data that I am processing. I need to split my date range and use my service to process each date range. My service has repositories. Each repository has a DbContext. Having each segment of dates run in the context of its own service allows me to run the processing in parallel without having DbContext queries being run in parallel which causes issues with Ef Core
This processing is running inside a durable function
I don't know if this holds true for Azure Functions and moreover I am not experienced with durable ones, though as it seems that the main goal is to run parallel queries via ef core through your IMyService then you could in the constructor:
public MyFunction(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
And then in the function call, assuming you have an IEnumerable "yourSegments" of the things you want to process in parallel:
var tasks = yourSegments.Select(async segment =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var IMyService = scope.ServiceProvider.GetRequiredService<IMyService>();
await IMyService.MyMathodAsync(segment);
}
});
await Task.WhenAll(tasks);
I got this from a nice blog post that explains "Since we project our parameters to multiple tasks, each will have it's own scope which can resolve it's own DbContext instance."
You can create a 1:1 copy by using this extension method.
It is a large function, to large for SO, so I've put a pastebin here.
https://pastebin.com/1dKu01w9
Just call _myService.DeepCopyByExpressionTree(); within your constructor.

inject custom class into startup.cs

I am trying to find a way to inject a dependency in Startup.cs, for a aspnet5 application. I am aware of the following previous question, but hopefully I can expand on my particular requirements:
Aspnet core 5.0 inject dependency in startup.cs constructor
I initially needed a way to fire a custom method after OnSignIn with MS Identity Platform (IP) within my app. After posting the following questions, I was quickly pointed in the right direction:
How to fire a custom method after OnSignIn with MS Identity Platform
In short, during ConfigureServices in StartUp.cs, I needed to configure the IP middleware service, to add my own methods to some of its available events. Works a treat!
So I initially tried:
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = async ctex =>
{
// Blah
},
});
As I say, this works great. However, to do anything serious, I need access to my existing code and the DI magic that goes with it. This is where I a hitting this problem. I need my custom logic that requires DI, during ConfigureServices, before I have configured the services for DI.
On the same post, someone pointed out that you can wrap up the configuration, and essentially inject it back in, so you can get DI back. Great! But for some reason, I can only inject the handler, either as a Singleton, or Transient. Not Scoped. I get to some extent why it can't be scoped (if it is has a dependency of its own on a transient service), but then are why both extremes ok? Why is Singleton OK? Why is Transient? It just so happens, but my DI requirements are that I would need to inject scoped services, as that is what I need. I need some of the data to persist across instances, but I can't have a single instances shared across the whole application/user base.
So then I tried:
public class AzureAdOpendIdHandler : IConfigureNamedOptions<OpenIdConnectOptions>
{
public AzureAdOpendIdHandler(Authentication.AuthenticationManager authenticationManager)
{
AuthenticationManager = authenticationManager;
}
public Authentication.AuthenticationManager AuthenticationManager { get; }
public void Configure(string name, OpenIdConnectOptions options)
{
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
//Blah
return Task.CompletedTask;
},
};
}
}
Then called in StartUp.cs like:
services.AddScoped<Authentication.IAuthenticationManager, Authentication.AuthenticationManager>();
services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, AzureAdOpendIdHandler>();
Again, the handler works fine, and everything fires, as long as I don't try and pass "AuthenticationManager" within the constructor. I get an error saying that I can't inject a scoped service into a singleton service.
I keep getting within spitting distance of doing what I need, but there is always another block in the way.
In summary:
To override/extend MS Identity Platform, I need to make changes in ConfigureServices() in StartUp.cs
These changes require my classes to be available via DI.
The DI must allow me to pass them as scoped
So far, I can only meet the first two requirements, I can not find a way to satisfy all three.
My end requirement is to be able to add a call to my AuthenticationManager class from within the following OnTicketReceived Event:
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = async ctex =>
{
//use my AuthenticationManager class!!
},
});
I have tried the direct method, wrapping things up in a handler and injecting, but DI seems to be getting in the way now.
Any help, would be gratefully received.

Access Current OwinContext

I’m working on an OWIN hosted ASP.Net WebApi2 Microservice. We are using Autofac as Dependency Injector.
When my service gets called I can get information about the user by accessing owinContext.Request.User.Identity. My problem is when I have to make a call to another service and pass the user information along. I have to create an authorization token that includes the user information and set that token to the RequestMessage.
I would like to write a DelegatingHandler that I can put in the “HttpClient-MessageHandler-Pipline”. But inside DelegatingHandler.SendAsync() I would need to have access to the current OwinContext.
I thought about different approaches. But I really hope there is something easier and less error prone (or maybe a hint how I could make one of this approach feasible):
With Autofac I can’t easily solve the Captive Dependency Problem: The HttpClient and therefore the DelegatingHandler are registered as SingleInstance. But the OwinContext is registered on a LifeTimeScope. So I can’t just inject the OwinContext to the constructor of the DelegatingHandler.
I could write an Owin-Middleware that sets the OwinContext on a static System.Threading.AsyncLocal variable. Inside the DelegatingHandler I could access that static variable. But mutable global variables just feels really wrong to me.
I don’t write a DelegatingHandler at all. Inside my ServiceRepository (that can also be registered on the LifeTimeScope) I would have to construct all the RequestMessages by hand. Then it would be easy to set the AuthorizationToken onto the RequestMessage. But not using the convenience methods like e.g. httpClient.PostAsJsonAsync() brings a lot of work with it.
public class MyHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
HttpResponseMessage response = task.Result;
IOwinContext owinContext = request.GetOwinContext();
// do something with the response and owinContext
return response;
},
cancellationToken);
}
}

Multithreading issue with Autofac in WPF application

I am working with a WPF based application and using Autofac to resolve the dependency of DbContext of Entityframework. I used the below code to register my data module.
public class DataModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DataContext>()
.As<IDbContext>()
.WithParameter("nameOrConnectionString", "DefaultConnectionString")
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
}
}
This works fine while using in normal scenario but while using TPL, due to simultaneous calls to repository, it creates error stating that "ExecuteReader requires an open and available Connection. The connection's current state is open."
In web application, this can be resolved using InstancePerRequest to resolve dependency per request but in WPF I need to resolve this dependency per Thread request. Is there any way out for this?
I have review InstancePerRequest summary or autofac and it states that this method is used for Web request only:
// Summary:
// Share one instance of the component within the context of a single web/HTTP/API
// request. Only available for integration that supports per-request dependencies
// (e.g., MVC, Web API, web forms, etc.).
Update:
This is a simple async method that I used to get the data:
private async void OnLoadClientDetail()
{
long clientId = SelectedClient != null ? SelectedClient.Id : 0;
var listOfCollection = await _collectionService.GetCollectedCollectionAsync(clientId);
CollectionList = new ObservableCollection<CollectedCollection>(listOfCollection);
}
Here OnLoadClientDetail is bound to selection change event of a combobox. When user change the selection frequently then this method will be called multiple times. The _collectionService is injected in the viewmodel and has InstancePerLifetimeScope define. So how can I get different scope for all this calls?
As far as I can see, you share the _collectionService instance across the different event handlers by injecting it by Constructor Injection.
It probably better to use Method Injection here, so you'll get the instance per call, as you need, resolving it before method:
builder.Register(c =>
{
var result = new MyObjectType();
var dep = c.Resolve<TheDependency>();
result.SetTheDependency(dep);
return result;
});

HttpContext.Current is null inside Identity Framework's methods

I am using ASP.NET MVC 5 and Identity Framework. When I call UserManager.UpdateAsync(...) my eventhandlers on ApplicationDbContext() SaveChanges will run. Here I am using HttpContext.Current for different purposes (logging and auditing) so I must get say current user. However the whole method runs in a worker thread, and here HttpContext.Current is null.
The biggest problem that the UserManager's "sync" methods are only wrappers around the async version, so the calls are serialized, but the methods (and eventhandlers) still run in a different worker thread.
Please note this issue has nothing to do with the async/await context. In the controller after the await (or calling the 'sync' version) I have back the correct HttpContext, even the controller's method is continuing in an other thread. That's fine.
So the problem is inside the async worker which will run in both the "sync" and async versions. I think I am understanding the phenomena (but I am not happy with the fake 'sync' method versions, real sync methods would not exhibit this issue.) I just does not know how to deal/workaround it.
[btw: Would not it be more natural to implement UserManager's operarations as simple pure sync versions, then wrap them by async multithreaded wrappers?. IF we continue this async fashion without thinking we will soon invent the async assignment operator. It costs me dozens of hours (just this issue), and costs worldwide zillion dollars, I am sure in many cases less return than its price.]
Bonus: We are talking about UserManager which's impact pretty marginal, but the same principles and issues can apply any out of the box library (black box for you) which authors do not implement sync versions and or do not care about the controller thread's context. What about EF, it is not so marginal... and what about DI containers instantiation infrastructure like "request scope" or "session scope". Surely they misbehave if resolving occurs in a thread with no HttpContext.Current. Recently I refreshed SendGrid NuGet, and (as a breaking change) Deliver() method gone, and now only DeliverAsync() is existing...
I would like to have a safe reliable way, how can I access the HttpContext inside this worker for logging and audit purposes.
Sample code, the controller 'sync' version:
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Edit(ApplicationUser user)
{
// validation etc
// Update() seems to be only a poor wrapper around the async version, still uses a worker thread.
var result = UserManager.Update(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
Sample code, the controller async version:
[AcceptVerbs(HttpVerbs.Post)]
public virtual async Task<ActionResult> Edit(ApplicationUser user)
{
// validation etc
var result = await UserManager.UpdateAsync(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
and the event handler where HttpContext is null:
public ApplicationDbContext() : base("DefaultConnection", false)
{
InitializeAudit();
}
private void InitializeAudit()
{
var octx = ((IObjectContextAdapter) this).ObjectContext;
octx.SavingChanges +=
(sender, args) =>
{
// HttpContext.Current is null here
};
}
Any ideas?
As you said, this occurs because of threading. The delegate runs in a different thread, making the HttpContext inaccessible.
You can move the variable outside of the delegate, making it a closure.
private void InitializeAudit()
{
var octx = ((IObjectContextAdapter) this).ObjectContext;
HttpContext context = HttpContext.Current;
octx.SavingChanges +=
(sender, args) =>
{
// context is not null
};
}
You are using asp.net identity through owin,
so one instance of the dbcontext is created per request,
and you can get this reference from anywhere in the request pipeline.
nb. this is handy but i think the dbcontext shouldn't be accessed outside the manager.
In asp.net identity design, only the manager should be aware of the store.
I believe the dbcontext is exposed because several asp.net identity middleware have a dependance on it.
But, it could help resolve you problem:
Allow your custom dbcontext handler to be set outside the class:
public EventHandler SavingChangesEventHandler
{
set
{
(((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext).SavingChanges += value;
}
}
Declare a custom ActionFilter class and register it, then override OnActionExecuting:
Filtering in ASP.NET MVC
https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx
public class CustomizeAppDbcontextFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var dbcontext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
var currentuser = HttpContext.Current.User;
dbcontext.SavingChangesEventHandler = (sender, args) =>
{
// use currentuser
};
}
}
you may need these using statements to be able to call the identity.owin extension methods:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
You should be in the controller thread because OnActionExecuting is wrapping the controller action.
I did not test it, so it may need some polishing but the concept should work.

Categories

Resources