In a project I have a SignalR hub which can do authorization. The simplified version of the hub looks like this.
public class WeatherHub : Hub
{
public async Task SubscribeForWeatherChange(string city)
{
// Will never throw. Based on authentication state it will evaluate to true or false regardless of Azure
// SignalR service is being used or not.
bool authenticated1 = this.Context.User.Identity.IsAuthenticated;
bool authenticated2 = this.Context.GetHttpContext().User.Identity.IsAuthenticated;
// Will throw when Azure SignalR service is being used.
string accessToken = await this.Context.GetHttpContext().GetTokenAsync("cookies", "access_token");
this.EnsureIsAuthorized(accessToken); // Executes HTTP call and throws based on response (!= 200).
await this.Groups.AddToGroupAsync(this.Context.ConnectionId, city);
}
public async Task SendWeatherChange(string city, string weather)
{
await this.Clients.Group(city).SendAsync("WeatherChanged", city, weather);
}
}
This code runs perfectly fine. The access token is being extracted and can be used when executing HTTP calls. It is getting problematic when we want to use Azure SignalR service. In this case reading out the access token does not work.
My investigation shows that when SignalR service is being used the extension method GetTokenAsync(this HttpContext context, string scheme, string tokenName) is throwing an ArgumentNullException because the context.RequestServices
is null.
Is my way of doing authorization wrong? Is there a better approach? Why is the IServiceProvider null?
My service configuration looks like this.
public void ConfigureServices(IServiceCollection services)
{
services
.AddSignalR()
.AddAzureSignalR();
services
.AddAuthentication(options =>
{
options.DefaultScheme = "cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("cookies", options =>
{
// cookie configuration
})
.AddOpenIdConnect("oidc", options =>
{
options.SaveTokens = true;
// oidc configuration
});
services.AddControllers();
}