I'm developing a web application and I want to be able to access currently logged in user through my IUserContext interface. It looks like so:
public interface IUserContext
{
int UserId { get; }
string UserName { get; }
}
This interface can be consumed in my command handlers and query handlers.
Web application uses ASP.NET Identity library version 2.2.1. So, I want to implement the interface using IOwinContext. I have three layers where I need access to owin context:
For web forms pages and MVC actions, I can write custom middleware to populate IUserContext properties (using code from this doc):
public interface IOwinContextAccessor
{
IOwinContext CurrentContext { get; }
}
public class CallContextOwinContextAccessor : IOwinContextAccessor
{
public static AsyncLocal<IOwinContext> OwinContext = new AsyncLocal<IOwinContext>();
public IOwinContext CurrentContext => OwinContext.Value;
}
public class OwinUserContext : IUserContext
{
public int UserId => CallContextOwinContextAccessor.OwinContext.Value.Authentication.User.Identity.GetUserId<int>();
public string UserName => CallContextOwinContextAccessor.OwinContext.Value.Authentication.User.Identity.Name;
}
// inside my Startup.cs Configuration method
app.Use(async (context, next) => {
CallContextOwinContextAccessor.OwinContext.Value = context;
await next();
});
For signalr hub I can create HubPipelineModule
which will populate CallContextOwinContextAccessor.OwinContext
:
public class OwinContextPipelineModule : HubPipelineModule
{
protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context)
{
var owinContext = context.Hub.Context.Request.GetHttpContext().GetOwinContext();
CallContextOwinContextAccessor.OwinContext.Value = owinContext;
return base.OnBeforeIncoming(context);
}
}
Questions:
CallContextOwinContextAccessor
inside middleware and hub pipeline module? I've read documentation about AsyncLocal
type and it seems like variable's value persists across methods calls down the call stack. I worry about possible race conditions when one user calls MVC action and at the same time another user calls signalr hub method or vice versa.Solution:
As it turned out, it's much easier to use command object and initialize current user property using appropriate mvc/signalr User
properties.
E.g.:
public class MyCommand
{
public string Payload { get; set; }
public IPrincipal CurrentUser { get; set; } // or int UserId, or string UserName
}
and then initialize command object:
// mvc action
new MyCommand
{
Payload = "foo"
CurrentUser = User // User is a Controller.User property
};
// signalr hub
new MyCommand
{
Payload = "bar"
CurrentUser = Context.User
};
This way, I don't need IUserContext
at all.
Thanks @Nkosi for figuring that out.