I have an ASP.NET WebAPI project. I'm using Bearer Tokens to authenticate my users. Some of my controller actions are marked with [Authorized]
filter. In the client side, the client gets his token by invoking http://foo.bar/Token
and then adding that token as an Authorization
header to its requests.
There is no problem up to this point and everything is working as it should with these settings in my Startup.Auth.cs
class:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
Now I have added some SignalR Hubs to my project and I'd like to authenticate the users in the hubs as well. There are some other questions that deal with how to clients can add bearer token to SignalR connection. Summary:
OAuth Provider
and passing the token as query string. This approach is also discouraged because it can become a security issue. But I'd still want to use this method because I don't want Long Polling.So I created this class to retrieve my access token from query string.
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
public override Task RequestToken(OAuthRequestTokenContext context)
{
var value = context.Request.Query.Get("access_token");
if (!string.IsNullOrEmpty(value))
{
context.Token = value;
}
return Task.FromResult<object>(null);
}
}
And then on the client-side, I do this to pass my access token:
var connection = $.hubConnection();
var hub = connection.createHubProxy('fooHub');
connection.qs = { 'access_token': myAccessToken};
// Init connection
This is the idea but the problem arises when I want to register my QueryStringOAuthBearerProvider
in the Startup.Auth.cs
class.
Here is how I changed my Startup.Auth.cs class:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Enable the application to retrieve tokens from query string to authenticate users
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
So I thought right now I can use:
When I'm connecting to my hubs I can retrieve access token from query string and users are authenticated. But when I try to invoke a WebAPI controller action I get System.InvalidOperationException
saying Sequence contains more than one element
. I think it's because I'm registering two ways to authenticate my users through bearer tokens and OWIN doesn't like that.
How can I have both methods to get the bearer token?
QueryStringOAuthBearerProvider
(Query string) for SignalR Hubs.Please note that I don't want to pass access tokens as query string to my WebApi actions, but only to SignalR Hubs.
The token is a text string, included in the request header. In the request Authorization tab, select Bearer Token from the Type dropdown list. In the Token field, enter your API key value. For added security, store it in a variable and reference the variable by name.
Tokens can be generated in one of two ways: If Active Directory LDAP or a local administrator account is enabled, then send a 'POST /login HTTP/1.1' API request to retrieve the bearer token. If Azure Active Directory (AAD) is enabled, then the token comes from AAD.
To get this token, you call the Microsoft Authentication Library (MSAL) AcquireTokenSilent method (or the equivalent in Microsoft. Identity. Web). Call the protected API, passing the access token to it as a parameter.
While blindly searching for an answer, I encountered a post somewhat similar to my question and way down in the comments someone named mahmoud said:
I found the problem. Instead of using
app.UseOAuthBearerTokens(OAuthOptions)
applyapp.UseOAuthAuthorizationServer(OAuthOptions)
.
It worked in my case and now I can use both methods to get bearer tokens. Here is my modified Startup.Auth.cs
class:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
//app.UseOAuthBearerTokens(OAuthOptions); // Commented this line.
app.UseOAuthAuthorizationServer(OAuthOptions); // Added this line
// Enable the application to retrieve tokens from query string to authenticate users
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With