I have a got an MVC application with forms based authentication with custom Principal. Before using application user must log in. After this I want to use SignalR, the issue is that Context.User.Identity.Name is always empty string.
CustomPrincipal.cs
public class CustomPrincipal : IPrincipal
{
public CustomPrincipal(IIdentity identity)
{
Identity = identity;
}
public IIdentity Identity { get; }
public bool IsInRole(string role)
{
return true;
}
}
CustomIdentity.cs
public class CustomIdentity : IIdentity
{
public CustomIdentity(EmployeeModel user)
{
Name = user.Username;
Id = user.Id;
}
public string AuthenticationType => "Custom";
public bool IsAuthenticated => !string.IsNullOrEmpty(Name);
public int Id { get; set; }
public string Name { get; }
}
BaseController.cs (from which I derive all my MVC controllers)
protected override void OnAuthorization(AuthorizationContext context)
{
if (SessionPersister.User != null && !string.IsNullOrEmpty(SessionPersister.User.Username))
{
context.HttpContext.User = new CustomPrincipal(new CustomIdentity(SessionPersister.User));
}
base.OnAuthorization(context);
}
SessionPersister here is just a static class to store logged-in users
So, everything in my MVC app is working great. The issue that when the user is logged-in and I want to send a message to another user that is logged in via SignalR, Identity.User.Name is an empty string in my Hub class:
public override Task OnConnected()
{
string name = Context.User.Identity.Name; // it's empty
return base.OnConnected();
}
Is there is any way to pass my MVC IPrincipal to SignalR or configure it to use my custom authentication, that I use in MVC?
Thanx in advance
So, slight logical error:
BaseController.OnAuthorization
only triggers when a Controller is being executed. When a SignalR request is coming through, that method will never be called for that request.
So, a way around that would be to move the code from the Controller to a more Global scope. For example, you could use the Global.asax.cs
and add it, likeso:
protected void Application_PostAuthenticateRequest( object sender, EventArgs e )
{
//do your custom principal setting here.
this.Context.User = new CustomPrincipal( new CustomIdentity( 10, "test" ) );
}
Then, in your hub, you would be able to see the identity like so:
public String Hello(String hello)
{
//no need to actually cast if you don't need the non-iidentity properties
//var identity = (CustomIdentity) this.Context.User.Identity;
//identity.Id == 10
//identity.Name == "test"
return hello;
}
Alternatively, instead of the Global.asax, I'm sure you could throw it in the OWIN pipeline after user authentication. However, someone else would need to provider an exact example.
EDIT: Just to clarify, I changed the constructor for your CustomIdentity since I didn't have all of your classes. This examples above are just proof of concepts.