I am retrieving the sid in my WebApi controller using
private string GetAzureSID()
{
var principal = this.User as ClaimsPrincipal;
var nameIdentifier = principal.FindFirst(ClaimTypes.NameIdentifier);
if (nameIdentifier != null)
{
var sid = nameIdentifier.Value;
return sid;
}
return null;
}
And I get a non-null value. However, when I try to call specific hub clients using
hubContext.Clients.User(sid).refresh()
the expected clients do not respond. Actually no clients respond. That said
hubContext.Clients.All.refresh()
does call everyone. I have not done anything like
var idProvider = new PrincipalUserIdProvider();
GlobalHost.DependencyResolver.Register (typeof(IUserIdProvider), () => idProvider);
But I think that should be the default right? What am I missing? Perhaps there is some way of checking what userIds are in Clients?
Update. I found this Context.User.Identity.Name is null with SignalR 2.X.X. How to fix it? which talks about having signalr before webapi, which I tried to no avail. I am using authentication from Azure though, so that could be the issue. HEre is what my ConfigureMobileApp looks like
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
// Use Entity Framework Code First to create database tables based on your DbContext
// Database.SetInitializer(new MobileServiceInitializer());
var migrator = new DbMigrator(new Migrations.Configuration());
migrator.Update();
MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();
if (string.IsNullOrEmpty(settings.HostName))
{
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
// This middleware is intended to be used locally for debugging. By default, HostName will
// only have a value when running in an App Service application.
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
}
app.MapSignalR();
app.UseWebApi(config);
}
It could be that the problem is that the authentication is somehow coming after from Azure? I tried calling the Hub from my Client
[Authorize]
public class AppHub : Hub
{
public string Identify()
{
return Context.User.Identity.Name;
}
}
but the result is 'null' so I think that Signalr is unable to get the User correctly.
Update 2. Could be I need to create a UseOAuthBearerAuthentication that reads [x-zumo-auth]
Update 3. I added some more functions into my Hub
public string Identify()
{
return HttpContext.Current.User.Identity.Name;
}
public bool Authenticated()
{
return Context.User.Identity.IsAuthenticated;
}
public string Bearer()
{
return Context.Headers["x-zumo-auth"];
}
and the results are
null
true
the correct bearer token
Not sure if this helps, but the sid from WebApi look like sid:8ba1a8532eaa6eda6758c3e522f77c24
Update 4. I found the sid! I changed my hub code to
public string Identify()
{
// return HttpContext.Current.User.Identity.Name;
var identity = (ClaimsIdentity)Context.User.Identity;
var tmp = identity.FindFirst(ClaimTypes.NameIdentifier);
return tmp.Value;
}
and I got the sid. Not sure how Context.User.Identity.Name is different than this, but this does work. Now the question is, how can I use a given sid to call
hubContext.Clients.User(...???...).refresh()
if I know the NameIdentifier of the user?
Special thanks to @davidfowler for the remarkably annoying and yet astute "why would it not be null :smile:". Once I finally accepted that Context.User.Identity.Name would always be null, I was able to get the hub to retrieve the sid using
which led me to look through the signalr code for User.Identity.Name ultimately landing on PrincipalUserIdProvider. Surprise, surprise, it assigns GetUserId based on User.Identity.Name. I created a new IUserIdProvider:
and registered it before anything else in Startup.cs
and like magic, I can now hubContext.Clients.User(sid).refresh(). Hope this helps someone out there.