in src/Microsoft.Azure.SignalR.AspNet/Middleware/NegotiateMiddleware.cs [163:264]
private Task ProcessNegotiationRequest(IOwinContext owinContext, HostContext context)
{
var claims = BuildClaims(owinContext, context.Request);
var dispatcher = new HubDispatcher(_configuration);
try
{
dispatcher.Initialize(_configuration.Resolver);
if (!dispatcher.Authorize(context.Request))
{
string error = null;
if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
{
// If the user is authenticated and authorize failed then 403
error = "Forbidden";
context.Response.StatusCode = 403;
}
else
{
// If failed to authorize the request then return 401
error = "Unauthorized";
context.Response.StatusCode = 401;
}
Log.NegotiateFailed(_logger, error);
return context.Response.End(error);
}
}
catch (Exception e)
{
Log.NegotiateFailed(_logger, e.Message);
context.Response.StatusCode = 500;
return context.Response.End("");
}
IServiceEndpointProvider provider;
try
{
// Take the service endpoints for the app
provider = _endpointManager.GetEndpointProvider(_router.GetNegotiateEndpoint(owinContext, _endpointManager.GetEndpoints(_appName)));
// When status code changes, we consider the inner router changed the response, then we stop here
if (context.Response.StatusCode != 200)
{
// Inner handler already write to context.Response, no need to continue with error case
return Task.CompletedTask;
}
// Consider it as internal server error when we don't successfully get negotiate response
if (provider == null)
{
var message = "Unable to get the negotiate endpoint";
Log.NegotiateFailed(_logger, message);
context.Response.StatusCode = 500;
return context.Response.End(message);
}
}
catch (AzureSignalRNotConnectedException e)
{
Log.NegotiateFailed(_logger, e.Message);
context.Response.StatusCode = 500;
return context.Response.End(e.Message);
}
// Redirect to Service
var clientProtocol = context.Request.QueryString["clientProtocol"];
string originalPath = null;
string queryString = null;
// add OriginalPath and QueryString when the clients protocol is higher than 2.0, earlier ASP.NET SignalR clients does not support redirect URL with query parameters
if (!string.IsNullOrEmpty(clientProtocol) && Version.TryParse(clientProtocol, out var version) && version >= ClientSupportQueryStringVersion)
{
var clientRequestId = _connectionRequestIdProvider.GetRequestId("");
if (clientRequestId != null)
{
// remove system preserved query strings
queryString = "?" +
string.Join("&",
context.Request.QueryString.Where(s => !PreservedQueryParameters.Contains(s.Key)).Concat(
new[]
{
new KeyValuePair<string, string>(
Constants.QueryParameter.ConnectionRequestId,
clientRequestId)
})
.Select(s => $"{Uri.EscapeDataString(s.Key)}={Uri.EscapeDataString(s.Value)}"));
}
originalPath = GetOriginalPath(context.Request.LocalPath);
}
var url = provider.GetClientEndpoint(null, originalPath, queryString);
try
{
return GenerateClientAccessTokenAsync(provider, context, url, claims);
}
catch (AzureSignalRAccessTokenNotAuthorizedException e)
{
Log.NegotiateFailed(_logger, e.Message);
context.Response.StatusCode = 500;
return context.Response.End("");
}
}