Reputation: 23384
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?
Upvotes: 1
Views: 168
Reputation: 23384
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
var identity = (ClaimsIdentity)Context.User.Identity;
var tmp = identity.FindFirst(ClaimTypes.NameIdentifier);
return tmp.Value;
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:
public class ZumoUserIdProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
if (request.User != null && request.User.Identity != null)
{
var identity = (ClaimsIdentity)request.User.Identity;
var identifier = identity.FindFirst(ClaimTypes.NameIdentifier);
if (identifier != null)
{
return identifier.Value;
}
}
return null;
}
}
and registered it before anything else in Startup.cs
public void Configuration(IAppBuilder app)
{
var userIdProvider = new ZumoUserIdProvider();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => userIdProvider);
ConfigureMobileApp(app);
}
and like magic, I can now hubContext.Clients.User(sid).refresh(). Hope this helps someone out there.
Upvotes: 1