I am developing a prototype of an AngularJS, Web API, SignalR application as a potential starting point for a new project in VS 2013.
At this stage, I'm pretty much using the canned code that visual studio generates for Individual User Accounts.
There's a line in the StartUp.Auth.cs code that looks like this.
app.UseOAuthBearerTokens(OAuthOptions);
With this in place, I can add the [Authorize] attribute to controllers and it works fine.
Incidentally, with angular I was able to add a standard header containing the token in the JavaScript as follows.
$http.defaults.headers.common.Authorization = 'bearer ' + access_token;
Then I added SignalR to the project.
It supports it's own version of the [Authorize] attribute but there is no way to pass custom headers when using SignalR.
It's a limitation on the browser side.
The documentation said you could pass the token as part of the query string.
I added that code on the JavaScript side. My SignalR code now included this.
I passed the token as 'bearer_token'.
this.connection = $.hubConnection("/TestHub", { useDefaultPath: false, qs: "bearer_token=" + token });
So my issue was how to make OWIN recognize the token now it was no longer in the header.
After a number of searches, I ended up adding code that moved the token from the querystring into the header.
For my prototype, I just added a little code above the original line in StartUp.Auth.cs.
So, now it looked like this:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new OAuthBearerAuthenticationProvider()
{
OnRequestToken = context =>
{
if (context.Request.Path.Value.StartsWith("/TestHub"))
{
string bearerToken = context.Request.Query.Get("bearer_token");
if (bearerToken != null)
{
string[] authorization = new string[] { "bearer " + bearerToken };
context.Request.Headers.Add("Authorization", authorization);
}
}
return Task.FromResult(context);
}
}
});
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
The code above is rough but this is a prototype so really I just wanted to see if it worked which it did.
Finally getting to the question:
Is this the right pattern for integrating bearer token authorization with SignalR and the OWIN pipeline.
I couldn't seem to find much good information on the right way to do this.
I use a class like this:
public class OAuthTokenProvider : OAuthBearerAuthenticationProvider
{
private List<Func<IOwinRequest, string>> _locations;
private readonly Regex _bearerRegex = new Regex("((B|b)earer\\s)");
private const string AuthHeader = "Authorization";
/// <summary>
/// By Default the Token will be searched for on the "Authorization" header.
/// <para> pass additional getters that might return a token string</para>
/// </summary>
/// <param name="locations"></param>
public OAuthTokenProvider(params Func<IOwinRequest, string>[] locations)
{
_locations = locations.ToList();
//Header is used by default
_locations.Add(x => x.Headers.Get(AuthHeader));
}
public override Task RequestToken(OAuthRequestTokenContext context)
{
var getter = _locations.FirstOrDefault(x => !String.IsNullOrWhiteSpace(x(context.Request)));
if (getter != null)
{
var tokenStr = getter(context.Request);
context.Token = _bearerRegex.Replace(tokenStr, "").Trim();
}
return Task.FromResult<object>(null);
}
}
Which instead of just passing on the token to the header, parses it and sets it on the context.
Then it could be used in your app configuration like this:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new OAuthTokenProvider(
req => req.Query.Get("bearer_token"),
req => req.Query.Get("access_token"),
req => req.Query.Get("token"),
req => req.Headers.Get("X-Token"))
});
Then the following styles of requests would have their token un-encrypted, for use with authentication and authorization:
GET https://www.myapp.com/authorized/endpoint?bearer_token=123ABC HTTP/1.1
GET https://www.myapp.com/authorized/endpoint?access_token=123ABC HTTP/1.1
GET https://www.myapp.com/authorized/endpoint?token=123ABC HTTP/1.1
GET https://www.myapp.com/authorized/endpoint HTTP/1.1
X-Token: 123ABC
GET https://www.myapp.com/authorized/endpoint HTTP/1.1
Authorization: 123ABC