Acquiring Windows user in NancyFx context - c#

I'm being tasked with the creation of an intranet web application. It will eventually consist of a SPA Frontend (probably Angular) sitting on top of a NancyFx API.
Frankly I have absolutely zero experience using Windows users for Auth, and since it's generally not recommended (rightly so), it can be hard to find concrete information on this.
I want to authorize users based on the user that is sending each request. However, I have no idea how I can access user information from each request context in Nancy. I've had a look at doing:
public class IndexModule : NancyModule
{
public IndexModule()
{
Get["/"] = parameters =>
{
var User = this.Context.CurrentUser;
return View["index"];
};
}
}
Unfortunately, this.Context.CurrentUser never seems to be populated and is always null.
How can I populate each request context with a Windows User?
Alternatively, am I doing it wrong? What is the recommended way of dealing with Windows Users in Nancy? I've been researching this for over two days, and I'm quite nervous about telling my boss that I don't know how to handle this rather essential issue.

Nancy provides the NancyContext type to provide you with context within which the call takes place.
Unfortunatelty Nancy does not contain the windows user out of the box. In fact, Nancy has no native knowledge of the IUserPrincipal type, which is what is typically used to represent windows identities.
The good news is that Nancy does integrate with Owin, and Owin provides it's own context which does contain this information, and additionally this will get injected into the Nancy context at no extra cost!
What you need to do to make this happen is install the Nancy.Owin nuget package and then make a call to the extention IAppBuilder.UseNancy() from the Owin "Startup" class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// other owin config here...
// Register Nancy with Owin middleware...
app.UseNancy();
}
}
This will then enable you to access the Owin environment from the NancyContext like this:
var owinEnv = context.GetOwinEnvironment(); // context = your NancyContext
var windowsUser = (IPrincipal) owinEnv["server.User"];
Alternatively, if you don't fancy Owin, you could do this instead: https://stackoverflow.com/a/28976742/569662

Related

How to look up information based Azure AD User in ASP.NET Core MVC App

I use Azure Authentication in ASP.NET Core MVC and would like to lookup information based on this and use it in the whole app.
Edit:
What I basically want can be described as follows:
Users logs in with Azure Auth
The app extracts the preferred_username
An object is created that uses preferred_username to look up more information from the database
this object can be used for DI in order to
create IAuthorizationRequirement in order to use [Authorize(Policy = ("IsRequirementBasedOnLookedUpInformation"))]
can be uses in the views, for example to hide html code in razor like this #if (Object.IsRequirementBasedOnLookedUpInformation)
End Edit
Authentication is set up like this:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureB2C"));
This allows me to use the ClaimsPrincipal User throughout the whole application.
Now I want to look up some information in a database and make it available for the whole app.
public class UserWithPhoneNummer (dbContext _dbc, ClaimsPrincipal cp)
{
// do stuff with cp.Identity.Name aso
}
Then I would like to inject it via DI in a controller like this
public CashFlowAnalysisController(dbContext _dbc, UserWithPhoneNummer _uwpn)
{
dbc = _dbc;
uwpn = _uwpn;
}
I've found plenty of tutorials and guides, but these all aim at scenarios with EF Core and sophisticated user management.
When I try the IUserClaimsPrincipalFactory I get an error:
services.AddScoped<Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<mvcUser>,UserClaimsPrincipalFactory>();
Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[Mvc.Models.mvcUser]' while attempting to activate 'Mvc.Authorization.UserClaimsPrincipalFactory'.
I am happy to share more code, if needed and would be grateful for any pointers on how to achieve this.

Do API controllers use a different instance of a service than Blazor components?

I used this Best way to share data between two child components in Blazor to update a blazorcomponent from a service class to add notifications.
I added this to the POST of an API controller and also injected the notify service into the constructor.
And then display into the Blazor component like this:
But for some reason, this only works with services.AddSingleton, and not services.AddScoped.
But, services.AddScoped does work when you use the notify service between 2 blazor components like this. (This is a different component)
So, does a API controller use a different instance of the notify service? Or am I doing something wrong?
Thank you in advance. if you need more information, please let me know.
Do API controllers use a different instance of a service than Blazor components?
The answer is definitely yes for scoped services, no for singletons, but it gets complicated. But I suspect that's not your real question, and the real question is how to store user-specific data for both. Or modify a specific user's data from an external service
Why the services are different
A new controller instance is created to handle each HTTP request. The HTTP request defines a scope as far as DI is concerned, so a new service instance is created for each HTTP request and dispose after the request is processed.
This is A Good Thing, as it means expensive scoped services like a DbContext are disposed even if an error occurs. In the case of a DbContext, this give transaction-per-request semantics out of the box, without requiring any extra code.
Things are more complicated with Blazor Server. In this case, Blazor Server defines a scope per "user circuit" which is essentially a single tab. This means that scoped objects remain active as long the tab is active when the navigation happens on the client. The MVC part behaves the same way it did with ASP.NET Core MVC/Web API
This is similar to how a desktop application behaves and can cause nasty surprises when people assume that scoped services like a DbContest will be disposed "automagically". When you call SaveChangesAsync you may end up persisting changes you thought were discarded.
So even if you try to use a scoped service in Blazor Server, you may end up with a long-lived service in the Blazor components and a short-lived service in the Web API controllers.
In fact, since different scopes are used, the two services can't even find each other.
How DI and Scope work in Blazor Server
This is documented in ASP.NET Core Blazor dependency injection. The difference in a scope's lifetime is explained in Service Lifetime :
The Blazor Server hosting model supports the Scoped lifetime across HTTP requests but not across SignalR connection/circuit messages among components that are loaded on the client.
The Razor Pages or MVC portion of the app treats scoped services normally and recreates the services on each HTTP request when navigating among pages or views or from a page or view to a component.
Scoped services aren't reconstructed when navigating among components on the client, where the communication to the server takes place over the SignalR connection of the user's circuit, not via HTTP requests.
In the following component scenarios on the client, scoped services are reconstructed because a new circuit is created for the user:
The user closes the browser's window. The user opens a new window and navigates back to the app.
The user closes the last tab of the app in a browser window. The user opens a new tab and navigates back to the app.
The user selects the browser's reload/refresh button.
Notifying a user's tabs after an external call
From the comments :
I have an Azure queue serverless trigger. Inside that function I parse a file and I want to send the status to my blazor project. Once it gets in the API controller, i want to update the UI.
That's tricky. In this case the Web API is essentially acting as another user. The Blazor Server tabs need to be updated in response to what is essentially an external event.
Since it's Blazor Server though, assuming no load balancing is used, all the user circuits are running in the same process that handles API requests. One could raise an "event" in the API controller and have the Blazor components listen to it.
Luckily, that's exactly what libraries like Blazor.EventAggregator have implemented.
The Event Aggregator service is a singleton, registered with :
public void ConfigureServices(IServiceCollection services)
{
services.AddEventAggregator();
}
Assuming the message only accepts a file name and a status:
public record FileEvent(string file,string status);
The Web API Controller will act as a publisher :
class MyController:ControllerBase
{
private IEventAggregator _eventAggregator { get; set; }
public MyController(private IEventAggregator eventAggregator)
{
_eventAggregator=eventAggregator;
}
...
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Post(BlobInfo blobInfo)
{
await _eventAggregator.PublishAsync(new FileEvent(blogInfo.BlobName,"OK");
return Ok();
}
}
The Blazor Server components that care can inject IEventAggregator, listen for events, specify the events they care about and handle them.
In each component's code-behind, the service can be injected with :
[Inject]
private IEventAggregator _eventAggregator { get; set; }
The class also needs to implement the IHandler<T> interface for the events it cares about :
public class MyComponent : ComponentBase, IHandle<FileEvent>
{
...
List<string> _messages=new List<string>;
public async Task HandleAsync(FileEvent message)
{
_messages.Add($"{message.Name} worked");
await InvokeAsync(StateHasChanged());
}
...
}
The Razor code doesn't really need to change :
#foreach(var value in _messages)
{
<p>#value</p>
}

autofac resolve issue for keyed values

I am currently working on a feature and added the builder code like this in the Autofac
builder.RegisterType<ILTLoPublisher<ScheduleUpdateEvent>>()
.AsImplementedInterfaces()
.InstancePerRequest()
.Keyed<IILTLoPublisher<ScheduleUpdateEvent>>(AuditType.Schedule);
builder.RegisterType<ILTLoPublisher<ScheduleUpdatePart>>()
.AsImplementedInterfaces()
.InstancePerRequest()
.Keyed<IILTLoPublisher<ScheduleUpdatePart>>(AuditType.Part);
builder.RegisterType<ILTLoPublisher<ScheduleUpdateTest>>()
.AsImplementedInterfaces()
.InstancePerRequest()
.Keyed<IILTLoPublisher<ScheduleUpdateTest>>(AuditType.Test);
This code is run as a console app service and the call to this is made from an api service.I want it to be called as below
AutoFacModule autofac = new AutoFacModule();
var builder = new ContainerBuilder();
autofac.LoadBuilder(builder);
Container = builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
var _publisher1 = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdateEvent>>(AuditType.Schedule);
var _publisher2 = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdatePart>>(AuditType.Part);
var _publisher2 = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdateTest>>(AuditType.Test);
}
When i am trying to resolve it using the below code in my implementation class
var _publisher = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdateEvent>>(AuditType.Schedule);
I am getting the following error
Unable to resolve the type Apiconnector.Integrations.Vilt.Service.Providers.Custom.Publish.ILTLoPublisher`1[LMS.ILT.ScheduleUpdateEvent]' because the lifetime scope it belongs in can't be located
You can't use InstancePerRequest unless the object being resolved is part of a web request (as noted by the comments on the question). More specifically:
The executing application must be a web application.
The executing application needs to have the Autofac web integration in place.
The resolution must be happening in that web application as part of a response to an inbound web request - for example, as part of an MVC controller or ASP.NET Core middleware.
The "per request" semantics have nothing to do with the client making the request - it's about the server handling the request.
You might want to spend some time with the documentation on the topic. There is a section in there about how to implement custom per-request semantics for your app.
If what you are creating is a console app that takes in requests from clients (e.g., a self-hosted web application) then you need to:
Add the existing Autofac web integration for your app type (we do support ASP.NET Web API and ASP.NET Core self hosted scenarios); OR
Implement something custom if you're not using ASP.NET (see that doc I linked).
If what you are creating is a console app that issues requests as a client then you should ignore InstancePerRequest. Instead:
Create a new lifetime scope around each request (like you're doing) and treat that as a unit of work.
Register components as InstancePerLifetimeScope so there will be just one for the duration of that lifetime scope.
That said, without a minimal repro it's hard to see what you're doing beyond that to provide any sort of guidance.
Since you mentioned you're pretty new to all this, it would be very worth your time checking out the Autofac documentation to start understanding concepts like this as well as looking in the Examples repo where there are working examples of many different application types to show you how things work.

Register controller in all apps that use an assembly

Is this possible to achieve?
I want to make a library (lets call it Foolib) that will automatically add an mvc controller with a fix route to all mvc applications that have a dependency on Foolib.
Foolib is to be used in several intranet applications that we are developing and I want to make sure that all applications that use of Foolib have an standard ability to receive a configuration object.
In Foolib there will be a controller something like:
public FooController
{
[Route("/Foo")]
public Post(object obj)
{
}
}
Would it be possible to register the controller automatically to all web applications that uses Foolib?
Normally the calls to register controllers are made in the Startup class, how would I hook in this extra controller, hopefully without having to burden the other mvc application developers with an "just add this line to your startup" solution?
According to the docs
"By default MVC will search the dependency tree and find controllers (even in other assemblies)."
So it should just work.

ASP.Net Core dependency injection | How to access user or route data upon creation

Scenario
I am working to follow the IoC pattern and use the Microsoft DI Framework, but I'm hitting a wall and can't decide if it's my mindset, my approach, or I'm just doing it wrong.
I have a multi-tenant application that utilizes a Utility class to handle isolation and accessing the data of the tenant based on their unique configuration. The tenant is identified during authentication, but the tenant data has to be accessible and handled based on the request. This Utility class is registered under ConfigureServices in the Startup.cs and the constructor requires two parameters - a TenantDbContext and a Tenant.
public class TenantUtility{
public TenantUtility(TenantDbContext context, Tenant tenant){/*...*/}
}
Problem
Realizing that I'm probably pushing the limits of the DI Framework, I'm trying to build an implementationFactory in the Startup.cs. I've tried two approaches for accessing the tenant Id to build the Tenant object: one is using a User Claim, the second is a Route parameter.
services.AddTransient<TenantUtility>((svc)=> {
var tenantContext = svc.GetService<TenantDbContext>();
var accessor = svc.GetService<IHttpContextAccessor>();
var httpContext = accessor.HttpContext;
//httpContext is NULL...
//How do I get access to the tenant?
Common.Tenant t = new Common.Tenant();
//Set Tenant Identifier in t (once I get it)
return new StudentDataManager(tenantContext, t);
});
In both situations, when I setup the ImplementationFactory inside the Startup.cs, I don't have access to the user and I don't have access to the RouteData (or can't figure out how to get it) -- I even tried using IHttpContextAccessor, but the HttpContext property is null.
Am I approaching the pattern incorrectly? Should I be able to use DI for this level of detail be injected into the Utility before the Utility class is passed into the Controller?
You shouldn't pass in non-DI-managed types into services you want the container to provide for you (in this case, your Tenant parameter). Only request services that the container can provide. If you need a tenant, perhaps another service like an ITenantAccessor could be injected and would be able to get one.
I suspect your implementation could be done in middleware (if not using MVC) or as a filter (if using MVC). In either case, you can use DI from the filter or middleware class to inject your tenant service. In the class's invoke method, you will have access to the current context and request and should be able to do the things you need to. Remember that ConfigureServices runs before the app has started, so there is no context and no request is yet being made.
If you want to see some examples of filters, and especially how to do DI into filters, check out:
https://github.com/ardalis/GettingStartedWithFilters
and
http://ardalis.com/real-world-aspnet-core-mvc-filters
If you want to do it in middleware, then these might help:
https://github.com/ardalis/NotFoundMiddlewareSample
and
http://ardalis.com/using-custom-middleware-to-record-and-fix-404s-in-aspnet-core-apps
It seems to me that what you are injecting in the TenantUtility as a frist parameter is a valid abstraction (though it's probably better to use some interface), but the other one is a value-object. You don't generally inject value objects as they are not abstraction of some operations that you need to perform, but data. So I would pass the Tenant as a parameter to an operation on the TenantUtility class.
Another thing that I can suggest is to use a full-blown DI container like SimpleInjector (there are many others like Ninject, CastlWindsor, etc.). They are much more advanced as far as I know and can easily be integrated. Many of them have .Net Core integration already.

Categories

Resources