Context.User.Identity.Name is null with SignalR 2.X.X. How to fix it?
Asked Answered
T

6

53

This is driving me insane.

I'm using latest signalR release (2.0.2). This is my hub code (OnConnected)

        public override Task OnConnected()
        {
            //User is null then Identity and Name too.
            Connections.Add(Context.User.Identity.Name, Context.ConnectionId);
            return base.OnConnected();
        }

And this is my Controller's login method:

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
              var user = await UnitOfWork.UserRepository.FindAsync(model.UserName,  model.Password);

                if (user != null)
                {
                    await SignInAsync(user, model.RememberMe);

                    return RedirectToLocal(returnUrl);
                }
            }

            TempData["ErrorMessage"] = Resources.InvalidUserNameOrPassword;

            // If we got this far, something failed, redisplay form
            return RedirectToAction("Index","Home");
        }

I found that some people are having this issue on OnDisconnected, I don't even make it there.

I'm using MCV5 template.

Do you have any idea what is wrong?

Twobyfour answered 25/2, 2014 at 0:24 Comment(2)
Seems like a tough question even for SO... where are the SignalR owners.. I have been reading that version 2.0.3 might solve something similar when OnDisconnected, however no news for a long time. Something that is crucial to work like security is not working fine. Bad Luck me.Twobyfour
For some reason if the computer clock is not on correct time (e.g one month back), Context.User will be null.Lucy
T
100

I found the final solution, this is the code of my OWIN startup class:

        public void Configuration(IAppBuilder app)
        {
        app.MapSignalR();

        // Enable the application to use a cookie to store information for the signed i user
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Home/Index")
        });

        // Use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        app.UseMicrosoftAccountAuthentication(new MicrosoftProvider().GetAuthenticationOptions());
        app.UseTwitterAuthentication(new TwitterProvider().GetAuthenticationOptions());
        app.UseFacebookAuthentication(new FacebookProvider().GetAuthenticationOptions());
        app.UseGoogleAuthentication(new GoogleProvider().GetAuthenticationOptions());    
    }

Making myself some coffee, I thought "What about mapping SignalR AFTER the authentication, and voila! Now it's workign as expected.

        public void Configuration(IAppBuilder app)
        {
        // Enable the application to use a cookie to store information for the signed i user
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Home/Index")
        });

        // Use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        app.UseMicrosoftAccountAuthentication(new MicrosoftProvider().GetAuthenticationOptions());
        app.UseTwitterAuthentication(new TwitterProvider().GetAuthenticationOptions());
        app.UseFacebookAuthentication(new FacebookProvider().GetAuthenticationOptions());
        app.UseGoogleAuthentication(new GoogleProvider().GetAuthenticationOptions());

        app.MapSignalR();    
    }
Twobyfour answered 25/2, 2014 at 23:14 Comment(4)
man, this is crazy to get to it by your own with no info provided. thanks. in my template it has to be like that: public void Configuration(IAppBuilder app) { ConfigureAuth(app); app.MapSignalR(); }Victorinavictorine
@RonenFestinger I struggled with this like 10 days, no info, when I found it was like solving Fermats last theorem. Crazy :|Twobyfour
This did not work for me. I am using OwinContext and OwinContextIdentity.Montano
"Making myself some coffee" - was very helpful for me)Penance
P
5

If you're using Web Api and SignalR in the same project, you have to map SignalR before registering Web Api.

Change this:

app.UseWebApi(GlobalConfiguration.Configuration);
app.MapSignalR();

To this:

app.MapSignalR();
app.UseWebApi(GlobalConfiguration.Configuration);
Peipeiffer answered 11/6, 2015 at 19:37 Comment(3)
This is the answer provided a year and a half.-Twobyfour
@Twobyfour the answer you provided is similar but different. Your answer is to move app.MapSignalR() below Authentication. Mine is to move it above app.UseWebApi(). Someone using both Web Api and SignalR may find this information useful, as it's not intuitive.Peipeiffer
Thank you @JoshNoe I spent way too much time on this. This solved my issue.Dacha
N
3

just make sure auth. configuration is called befor start app.MapMignalrR()

i changed this

 public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR();
        ConfigureAuth(app);



    }
}

to this

 public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
        app.MapSignalR();


    }
}

hugs ..

Node answered 31/12, 2017 at 12:11 Comment(0)
R
2

If you're mapping /signalr as a 'branched pipeline' you need to do this. Make sure to use bp.UseCookieAuthentication and not app:

app.Map("/signalr", bp =>
{
   bp.UseCookieAuthentication(new CookieAuthenticationOptions
   {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/store/youRaccount/login")
   });

Tip: I deliverately changed the casing so when I see youRaccount in the URL bar I know it worked :-)

Rose answered 14/12, 2017 at 8:41 Comment(0)
R
1

.NET Core SignalR only

For the newer .NET Core SignalR the full instructions explain that when using websockets you need to manually pull out the accessToken from the query string. This is easy to miss.

https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-2.2

Basically where you call AddAuthentication() you need to add AddJwtBearer() and then set a handler for the OnMessageReceived handler.

Search for 'OnMessageReceived' in the above link for the code. It's a bit gnarly in the sense that you even have to add this yourself - but that's why it's easy to miss too.

Rose answered 6/7, 2019 at 0:40 Comment(0)
R
0

I was doing everything right like above mentioned answers but still it didn't resolve mine.

Below solution solved my issue.

I used the http context to get the access_token from the query params and decoded the token and from that I was able to access all the claims.

public override Task OnConnectedAsync()
{
    var httpContext = Context.GetHttpContext();
    if (httpContext != null)
    {
        var jwtToken = httpContext.Request.Query["access_token"];
        var handler = new JwtSecurityTokenHandler();
        if (!string.IsNullOrEmpty(jwtToken))
        {
            var token = handler.ReadJwtToken(jwtToken);
            var tokenS = token as JwtSecurityToken;

           // replace email with your claim name
            var jti = tokenS.Claims.First(claim => claim.Type == "email").Value;
            if (jti != null && jti != "")
            {
                Groups.AddToGroupAsync(Context.ConnectionId, jti);
            }
        }
    }
    return base.OnConnectedAsync();
}
Remark answered 29/7, 2021 at 15:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.