I would like to enable authentication in SignalR when the server was hosted in ASP.NET WebAPI, which I use for OAuth Bearer authentication, and the client is AngularJS.
On the client side, I initially pass the media token through the HTTP header, and it works well with WebAPI. But since SignalR JavsScript does not support adding HTTP headers to connection(because WebSocket does not support specifying HTTP headers), I need to pass the carrier token through the query string using code likeself.connection.qs = { Bearer: 'xxxxxx' };
The problem is that on the WebAPI side my SignalR always returned 401 Unauthorized.
Below is what I did on the WebAPI side.
1, I pointed OAuthBearerAuthenticationOptions.Providerto QueryStringEnabledOAuthBearerAuthenticationProvider, which is a class I created inherited from OAuthBearerAuthenticationProvider, which can retrieve a token from the query string. Code as below.
public class QueryStringEnabledOAuthBearerAuthenticationProvider: OAuthBearerAuthenticationProvider
{
private readonly string _name;
public QueryStringEnabledOAuthBearerAuthenticationProvider ()
: this (OAuthDefaults.AuthenticationType)
{
}
public QueryStringEnabledOAuthBearerAuthenticationProvider (string name)
{
_name = name;
}
public override Task RequestToken (OAuthRequestTokenContext context)
{
// try to read token from base class (header) if possible
base.RequestToken (context) .Wait ();
if (string.IsNullOrWhiteSpace (context.Token))
{
// try to read token from query string
var token = context.Request.Query.Get (_name);
if (! string.IsNullOrWhiteSpace (token))
{
context.Token = token;
}
}
return Task.FromResult (null);
}
}
And registered, as shown below, when starting WebAPI.
var options = new OAuthBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = AuthenticationType,
Provider = new QueryStringEnabledOAuthBearerAuthenticationProvider (),
AccessTokenFormat = _accessTokenFormat,
};
config.SuppressDefaultHostAuthentication ();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
app.UseOAuthBearerAuthentication(options);
2, SignalR authorize, . .
public class BearerAuthorizeAttribute : AuthorizeAttribute
{
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
return base.AuthorizeHubConnection(hubDescriptor, request);
}
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
return base.AuthorizeHubMethodInvocation(hubIncomingInvokerContext, appliesToMethod);
}
}
, WebAPI .
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
EnableJavaScriptProxies = false
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
// Require authentication for all hubs
var authorizer = new BearerAuthorizeAttribute();
var module = new AuthorizeModule(authorizer, authorizer);
GlobalHost.HubPipeline.AddModule(module);
});
, SignalR QueryStringEnabledOAuthBearerAuthenticationProvider.RequestToken, -. , SignalR BearerAuthorizeAttribute.AuthorizeHubConnection , request.User . , 401.
- , , .