JWT Authentication in ASP.NET 5 using OAuthBearerAuthentication - asp.net

I am working on an ASP.NET 5 app and I would like to use JWTs to secure certain endpoints in the application. For the time being we have decided that we (as opposed to a third party) will issue the JWTs, as all of our clients are 'owned' by the application, i.e. we have no 'external' clients. In the example, I have an endpoint which creates and returns a JWT using the jwt-dotnet library as follows (I appreciate that this is a basic example, with no expiration time and a single subject claim etc.):
...
// include a single subject claim (user id)
var claims = new Dictionary<string, object>() { { "sub", "1234" } };
var key = "EXAMPLE_SECRET_KEY_TO_SIGN_JWT";
var token = JWT.JsonWebToken.Encode(claims, key, JWT.JwtHashAlgorithm.HS256);
...
// return JWT
I can encode, and decode this JWT using the same key as one would expect. In my Startup.cs file, I am using Microsoft.AspNet.Authentication.OAuthBearer middleware to Authorize the relevant routes in my controllers which have the [Authorize] attribute specified. However, after looking at a number of posts including here and here I cannot seem to find an example of how to supply this signing key to the OAuth middleware in the same fashion. The code in my Startup.cs file looks as follows:
public class Startup
{
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseErrorPage();
app.UseOAuthBearerAuthentication();
app.UseMvc();
}
...
public void ConfigureServices(IServiceCollection services)
{
services.Configure<OAuthBearerAuthenticationOptions>(bearer =>
{
bearer.AutomaticAuthentication = true;
bearer.TokenValidationParameters.ValidAudience = "Example audience";
bearer.TokenValidationParameters.ValidIssuer = "Example issuer";
bearer.TokenValidationParameters.ValidateAudience = true;
bearer.TokenValidationParameters.ValidateIssuer = true;
bearer.TokenValidationParameters... // how do I set the signing key as a string literal?
});
services.AddMvc();
}
}
My assumption has been that I should be able to simply supply the same string literal key to the middleware so it can validate the token signature. However this does not seem to be the case, as the examples discuss using RSA keys or certificates as opposed to providing a single key/string literal.
I appreciate that I may be missing something here, or indeed that this may be the wrong approach and I should't be able to do this!

EDIT: symmetric keys are now natively supported in the RC2 nightly builds:
var key = Convert.FromBase64String("base64-encoded symmetric key");
app.UseJwtBearerAuthentication(options => {
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.Authority = Configuration["jwt:authority"];
options.Audience = Configuration["jwt:audience"];
options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(key);
});
You can't, at least not without a bit of plumbing: the OAuth2 bearer middleware relies on IdentityModel 5, that doesn't support symmetric keys like the one you're using in your first snippet.
Of course, symmetric keys will be eventually supported (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/250), but in the meantime, using an asymmetric key (like a RSA key) is recommended.
You can also implement symmetric keys support yourself (see https://gist.github.com/sandorfr/4039d540b6b552154522), but using a RSA key is definitely a better option.

Related

Port over existing MVC user authentication to Azure functions

I have an old web application which is using ASP.net with the build in cookie based authentication which has the standard ASP.net SQL tables for storing the users credentials.
This is currently running as an Azure web app, but I was toying with the idea of trying to go serverless as per this example creating a ReactJs SPA hosting on blob storage to try and keep costs down and also improve performance without breaking the bank.
https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/serverless/web-app
I was wondering if it is possible to port over the existing ASP.net authentication to Azure functions, to instead return a JWT (JSON Web Token) which could be passed back in the headers to handle authenticated requests.
When I have tried this in the past I have failed misserably, so I was wondering if anyone knows if it is possible?
I've seen this article, which seems to talk about Azure functions doing authentication, but with Azure AD, which I don't think is right for what I need.
https://blogs.msdn.microsoft.com/stuartleeks/2018/02/19/azure-functions-and-app-service-authentication/
The answer is kind of. What I mean by this is that you can use your existing database and many of the same libraries, but you can't port over the code configuration. The default authentication for Functions is either 1) The default API tokens or 2) one of the EasyAuth providers baked into App Services which is in the guide you linked. Currently, any other solution you'll need to setup yourself.
Assuming you go with the JWT option, you'll need to turn off all of the built-in authentication for Functions. This includes setting your HttpRequest functions to AuthorizationLevel.Anonymous.
At a basic level You'll need to create two things. A function to issue tokens, and either a DI service or a custom input binding to check them.
Issuing tokens
The Functions 2.x+ runtime is on .NET Core so I'm gong to borrow some code from this blog post that describes using JWTs with Web API. It uses System.IdentityModel.Tokens.Jwt to generate a token, which we could then return from the Function.
public SecurityToken Authenticate(string username, string password)
{
//replace with your user validation
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
// return null if user not found
if (user == null)
return null;
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
return tokenHandler.CreateToken(tokenDescriptor);
}
Validating Tokens
There are several guides out there for validating JWT within Azure Functions. I like this one from Ben Morris: https://www.ben-morris.com/custom-token-authentication-in-azure-functions-using-bindings/ (source code). It describes authenticating with either a custom input binding or with DI. Between the two, DI is the preferred option, unless there is a specific reason you need to use a binding. Here again, its the Microsoft.IdentityModel.JsonWebTokens and System.IdentityModel.Tokens.Jwt libraries that you'll need to do the bulk of the work.
public class ExampleHttpFunction
{
private readonly IAccessTokenProvider _tokenProvider;
public ExampleHttpFunction(IAccessTokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
[FunctionName("ExampleHttpFunction")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "example")] HttpRequest req, ILogger log)
{
var result = _tokenProvider.ValidateToken(req);
if (result.Status == AccessTokenStatus.Valid)
{
log.LogInformation($"Request received for {result.Principal.Identity.Name}.");
return new OkResult();
}
else
{
return new UnauthorizedResult();
}
}
}

How do I configure ASP.NET WebApi to validate bearer tokens against an OpenID Connect server?

I am writing a service which receives POSTs from another service, which includes an Authorization header containing a bearer token. This token is obtained independently from an OpenID Connect server (Keycloak in our dev environment, but not necessarily in production). Our service does not need to obtain or issue tokens; it merely needs to be able to validate them.
We are using .NET Framework 4.8 with self-hosted ASP.NET WebApi (OWIN 4, etc).
Configuration-wise, the information we have is:
the URL of the OpenID Connect service, eg. 'http://keycloak:8080/auth/realms/demo/'
the client ID, eg. 'js-client'.
The intent is that we obtain the issuer public key dynamically, from the OpenID server's metadata endpoint 'http://keycloak:8080/auth/realms/demo/.well-known/openid-configuration'. Currently I have something like:
WebApp.Start(startOptions, builder => {
var config = ...
// ... Set up routes etc ...
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
builder.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "js-client",
Authority = "http://keycloak:8080/auth/realms/demo/",
RequireHttpsMetadata = false,
SignInAsAuthenticationType = "Bearer",
});
builder.UseWebApi(config);
}));
The controller action looks like:
[HttpGet]
[HttpPost]
[Authorize]
public IHttpActionResult Receive([FromBody] string dto) => Ok();
Currently, it always returns 401 Unauthorized with a message 'Authorization has been denied for this
request' irrespective of the validity of the token.
Wireshark reveals that our service never tries to contact the Keycloak server for OIDC metadata, so I guess that the authorisation handler is not even finding the token.
I've looked at UseJwtBearerAuthentication and UseOAuthAuthorizationServer too, but those seem to want more information than just an OIDC endpoint (unsurprising, really) or they need custom provider implementations.
This does not seem to be such an unusual use case that I need to implement my own validator, so presumably I'm missing something? Google searches turn up hundreds of examples which seem to relate only to ASP.NET Core or don't cover non-interactive use cases.
I managed to make progress on this by inspecting the source of OpenIdConnectAuthenticationMiddleware.
The JwtBearer middleware handles validation of the issuer, but needs to know the public key. Since I need to avoid configuring this directly, I need to ask the OIDC server for it.
This can be accomplished using a ConfigurationManager, which should deal with caching, etc for us:
private JwtBearerAuthenticationOptions GetJwtBearerTokenAuthenticationOptions(string issuer, IConfigurationManager<OpenIdConnectConfiguration> configurationManager)
{
return new JwtBearerAuthenticationOptions
{
Realm = "demo",
TokenValidationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
// ... etc ...
IssuerSigningKeyResolver = (token, securitytoken, kid, validationparameters) =>
configurationManager.GetConfigurationAsync(CancellationToken.None).GetAwaiter().GetResult().SigningKeys,
ValidIssuer = issuer.TrimEnd('/'),
}
};
}
(The resolver delegate can't be async unfortunately, so I can't await this properly.)
The ConfigurationManager can be constructed like this (based on the internals of OpenIdConnectAuthenticationMiddleware):
private IConfigurationManager<OpenIdConnectConfiguration> GetOIDCConfigurationManager(string issuer)
{
var httpClient = new HttpClient(new WebRequestHandler());
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Demo OpenIdConnect middleware");
httpClient.Timeout = TimeSpan.FromMinutes(1);
httpClient.MaxResponseContentBufferSize = 10485760L;
var httpRetriever = new HttpDocumentRetriever(httpClient) { RequireHttps = false };
return new ConfigurationManager<OpenIdConnectConfiguration>($"{issuer}.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever(), httpRetriever);
}
These can then be used as follows:
const string issuer = "http://keycloak:8080/auth/realms/demo/";
var configurationManager = GetOIDCConfigurationManager(issuer);
builder.UseJwtBearerAuthentication(GetJwtBearerTokenAuthenticationOptions(issuer, configurationManager));
It all seems to work, although I'd very much like to know if there's a simpler way...?
Obviously, anyone using this in production should RequireHttps = true instead.

Validating Node.Js JWT token in asp.net/Authorize

I am in the process of splitting up my asp.net service to multiple micro services. As a process, I have created my identity service using Node.Js and it uses JWT for tokens.
Now i want to use this token in C# so that all my [Authorise] attributes use this token and allow access.
I have looked at many implementations, but could not get this to work. Since JWT is a standard impementation, i do not understand a reason why this would not work.
This is my C# code
public void ConfigureAuth(IAppBuilder app)
{
var issuer = "myorg/identity2";
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode
("xfecrrt7CV");
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
However, I get this error everytime i try to access a protected method.
{"Message":"Authorization has been denied for this request."}
Is there anything i am missing here? How do i add the claim identity to this?
Finally, it was resolved. One of my friends debugged the Identity source code and recommended to increased the key length. After increasing the key length, I was able to validate the token

Adding security to RESTful API [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this question
I am wanting to implement two websites that need to communicate with each other. (Since one of the sites has a separate deployment for each customer, and is spread across many servers, sharing a database or communicating privately is not an option.) So I've been looking into RESTful APIs.
Unfortunately, I'm running into a lot of information that I'm not familiar with. One issue is security. We don't need anything fancy--we're not a bank or anything. I think we can just get away with HTTPS and a basic username and password.
Questions:
How would I pass the username and password to the API? Would they just be passed as bare arguments in the URL?
Does .NET provide any mechanism for authorizing such username and passwords, or do I just manually see if the password is in our database on each and every request? (I would hash for security.)
How would I pass the username and password to the API? Would they just
be passed as bare arguments in the URL?
It can be either in the URL or in the header. If you are using HTTPS, it will all be encrypted so it will not be bare. Please see this for more details.
Does .NET provide any mechanism for authorizing such username and
passwords, or do I just manually see if the password is in our
database on each and every request? (I would hash for security.)
No you do not need to check the database on every request. You can check once, create a token with an expiry and the client can keep sending you the token. This way you do not have to keep checking the database every single time.
Please see see this answer for some helpful information.
I think basic authentication with base64 encoding will be sufficient. If not you can always change it. Here are the different ways to apply it to your backend code:
To apply an authentication filter to a controller, decorate the controller class with the filter attribute. The following code sets the [IdentityBasicAuthentication] filter on a controller class, which enables Basic Authentication for all of the controller's actions.
[IdentityBasicAuthentication] // Enable Basic authentication for this controller.
[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
public IHttpActionResult Get() { . . . }
public IHttpActionResult Post() { . . . }
}
To apply the filter to one action, decorate the action with the filter. The following code sets the [IdentityBasicAuthentication] filter on the controller's Post method.
[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
public IHttpActionResult Get() { . . . }
[IdentityBasicAuthentication] // Enable Basic authentication for this action.
public IHttpActionResult Post() { . . . }
}
To apply the filter to all Web API controllers, add it to GlobalConfiguration.Filters.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new IdentityBasicAuthenticationAttribute());
// Other configuration code not shown...
}
}
Finally here is an example of the implementation, you may change it as you need:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using BasicAuthentication.Results;
namespace BasicAuthentication.Filters
{
public abstract class BasicAuthenticationAttribute : Attribute, IAuthenticationFilter
{
public string Realm { get; set; }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
if (authorization == null)
{
// No authentication was attempted (for this authentication method).
// Do not set either Principal (which would indicate success) or ErrorResult (indicating an error).
return;
}
if (authorization.Scheme != "Basic")
{
// No authentication was attempted (for this authentication method).
// Do not set either Principal (which would indicate success) or ErrorResult (indicating an error).
return;
}
if (String.IsNullOrEmpty(authorization.Parameter))
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
return;
}
Tuple<string, string> userNameAndPasword = ExtractUserNameAndPassword(authorization.Parameter);
if (userNameAndPasword == null)
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Invalid credentials", request);
return;
}
string userName = userNameAndPasword.Item1;
string password = userNameAndPasword.Item2;
IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
if (principal == null)
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
}
else
{
// Authentication was attempted and succeeded. Set Principal to the authenticated user.
context.Principal = principal;
}
}
protected abstract Task<IPrincipal> AuthenticateAsync(string userName, string password,
CancellationToken cancellationToken);
private static Tuple<string, string> ExtractUserNameAndPassword(string authorizationParameter)
{
byte[] credentialBytes;
try
{
credentialBytes = Convert.FromBase64String(authorizationParameter);
}
catch (FormatException)
{
return null;
}
// The currently approved HTTP 1.1 specification says characters here are ISO-8859-1.
// However, the current draft updated specification for HTTP 1.1 indicates this encoding is infrequently
// used in practice and defines behavior only for ASCII.
Encoding encoding = Encoding.ASCII;
// Make a writable copy of the encoding to enable setting a decoder fallback.
encoding = (Encoding)encoding.Clone();
// Fail on invalid bytes rather than silently replacing and continuing.
encoding.DecoderFallback = DecoderFallback.ExceptionFallback;
string decodedCredentials;
try
{
decodedCredentials = encoding.GetString(credentialBytes);
}
catch (DecoderFallbackException)
{
return null;
}
if (String.IsNullOrEmpty(decodedCredentials))
{
return null;
}
int colonIndex = decodedCredentials.IndexOf(':');
if (colonIndex == -1)
{
return null;
}
string userName = decodedCredentials.Substring(0, colonIndex);
string password = decodedCredentials.Substring(colonIndex + 1);
return new Tuple<string, string>(userName, password);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
Challenge(context);
return Task.FromResult(0);
}
private void Challenge(HttpAuthenticationChallengeContext context)
{
string parameter;
if (String.IsNullOrEmpty(Realm))
{
parameter = null;
}
else
{
// A correct implementation should verify that Realm does not contain a quote character unless properly
// escaped (precededed by a backslash that is not itself escaped).
parameter = "realm=\"" + Realm + "\"";
}
context.ChallengeWith("Basic", parameter);
}
public virtual bool AllowMultiple
{
get { return false; }
}
}
}
If you still want to read more then here is a great article which goes into details. I have copied the above code from this article. It has lots of great information.
If you control or exert significant influence on both sides of the connection, client ssl certificates is a really strong and powerful way of doing this. It's attractive to me in this case because it only requires distributing a trusted CA certificate which can be done before the client certificates are created. It's far more secure than any username and password could ever be ( because the password doesn't need to go across the wire).
Any other solution with authentication I can think of, you're going to have to have some sort of data source to verify the credentials. But x509 solves this problem for you. We've done it at work between applications and other than managing the certificates it works really, really well. And it's basically the most secure thing available.
I don't know much about .net in general, but ( not to lmgtfy ) https://support.microsoft.com/en-us/kb/315588 seems like the step by step format you are looking for.
Just a thought, and it really depends on what you meant by "username/password". If this means "authorization"/access to some API call and you want to ensure that the client is "authorized" to make a call to your API (only apps A, B can make api calls to API - and it seems this is what you're looking for based on your comment above):
As in the comment above, authorization header, using JWT. There is an great/easy JWT library in Nuget
it's pretty much something like a "shared secret" used to sign a "payload" (the JWT)
the "sender" will build the JWT and sign it (and add to header or whatever protocol you want - it can be body if prefer it over headers)
the "receiver" will verify the JWT sent
this includes handling/mitigating "replays" - the JWT spec has an "expire" field (exp) that you can have the library validate as well (or not, it's up to you)
The project site is on Github with samples.
Hth.

ASP Identity GenerateUserTokenAsync(string purpose, TKey userId) purpose options

So a UserManager has a function called GenerateUserTokenAsync(string purpose, TKey userId).
What does this do in ASP Identity? Can I use this to generate OAuth Bearer tokens? Also what is the purpose parameter for? What values can this be?
UserManager.GenerateUserTokenAsync(User, TokenProvider, Purpose)
can be used to generate Tokens for purposes that are not implemented be the UserManager.
One example could be an invitation system. In a WebProject you need to create a TokenProvider like this:
public class InvitationTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class
{
public InvitationTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions<InvitationTokenProviderOptions> options, ILogger<DataProtectorTokenProvider<TUser>> logger) : base(dataProtectionProvider, options, logger)
{
}
}
and the InvitationTokenProviderOptions
public class InvitationTokenProviderOptions : DataProtectionTokenProviderOptions
{
}
then you can register it in StartUp.ConfigureServices().
services.AddIdentity<User, Role>(options =>
{
// ...
}).AddEntityFrameworkStores<ApplicationDbContect>()
.AddDefaultTokenProviders()
.AddTokenProvider<InvitationTokenProvider<User>>("Invitation");
Afterwards you can use it with the UserManger like this
// create a token
string token = await _userManager.GenerateUserTokenAsync(user, "Invitation", "Invitation");
// verify it
bool result = await _userManager.VerifyUserTokenAsync(user, "Invitation", "Invitation", token);
If you are going to use the token in URLs, don't forget to make it URL-Safe (it may contain '/' and other symbols.
Also check if trailing '==' is lost on the way through emails and browsers.
Documentation for 'GenerateUserTokenAsync' says
Get a user token for a specific purpose
This method should not be used directly, (no idea why it is public). It is used in generating password reset token (GeneratePasswordResetTokenAsync) and email confirmation tokens (GenerateEmailConfirmationTokenAsync). And it is used like this:
GenerateUserTokenAsync("Confirmation", userId); // email confirmation
GenerateUserTokenAsync("ResetPassword", userId); // password reset
In default implementation of token provider (TotpSecurityStampBasedTokenProvider) purpose is used as some sort of password in cryptographic token generation.
Overall, you don't need to use GenerateUserTokenAsync, just call GeneratePasswordResetTokenAsync or GenerateEmailConfirmationTokenAsync.

Resources