Tuesday, February 21, 2017

Using the Azure Mobile Apps Signing Key with JWT Bearer Authentication in ASP.NET Core

Azure Mobile Apps allow users to quickly get up and running using authentication via 3rd party providers. Once registered with your Azure Mobile Apps instance, you can use the appropriate SDK (i.e. JavaScript SDK) to authenticate users, and in turn get a signed JWT back representing the authenticated user and their claims. This JWT can be turned around and sent with requests to the server to authenticate users when making WebAPI calls.

This was also possible in the predecessor to Azure Mobile Apps, named Azure Mobile Services. The 'master' and 'application' keys were readily available from the Azure dashboard. The server receiving the JWT could use the 'master' key and check if the JWT being sent was issued using the identical key. If it was then that is an indicator that the call is properly authenticated.

With Azure Mobile Apps the concept of the 'master' and 'application' keys have been removed from the portal, as well the JavaScript SDK is no longer required to send the 'application' key in the calls to the server. However the concept of the 'signing' key that created the JWT representing the authenticated user still exists. The following steps will help you get started using Azure Mobile Apps and the JavaScript SDK, to call a WebAPI backend using ASP.NET Core.

1. Get the Signing Key for your Azure Mobile Apps Instance


If you want to use JWT Bearer Authentication on the server, you'll need to configure its settings to contain the Signing Key used to generate the JWT passed back to the authenticated client from Azure. This way you can validate calls on the server and that the user was truly authenticated via your Azure Mobile App instance. The nice thing about JSON Web Tokens is they are structured to contain known information (claims) including being able to verify the signing value used and if it matches. 

The signing value is unfortunantly buried on a page outside of the Azure portal. This wild goose chase I went on trying to find the value was a time dump, so hopefully with this information you'll be able to find this quickly. To get the signing key, go to the following URL (best if already signed into Azure):

https://{yoursite}.scm.azurewebsites.net/env
Replace the {yoursite} portion with the name of your Azure instance. A page will load with a ton of configuration about your site. Do a 'find' for the following value: 
WEBSITE_AUTH_SIGNING_KEY
The signing value will be associated with that key that is used for your Azure Mobile Apps instance. Grab it and save for use in the next step.



2. Configure JwtBearerAuthentication in ASP.NET Core

  1. Pull in the NuGet package via project.json in your WebAPI ASP.NET Core project. This will allow us to have the needed bits to configure the ASP.NET Core middleware for JWT Bearer Authentication:
  2. "Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0-preview1-final"
    
  3. To prevent making Startup.cs too polluted, create a separate partial class (if desired) named something like Startup.auth.cs where we can place the JWT middleware configuration. Then in the Configure method of the main Startup.cs file, add the following to call our new method we'll create below:
  4. ConfigureAuth(app);
    
  5. Add the JWT configuration, and replace the value in the instantiation of SymmetricSecurityKey with the value from your Azure instance we extracted above in Step #1. All of this configuration and setting of the various properties is up to you. At a minimum though I'd recommend making sure that the key was issued from the correct domain, not expired, and signed with the proper key. The key will need to be extracted from its hex value, so the helper is included.
  6. public partial class Startup
    {
    /// <summary>
    /// Sets up JWT middleware configuration for use when authorizing endpoints within this API
    /// </summary>
    /// <param name="app"></param>
    private void ConfigureAuth(IApplicationBuilder app)
    {
     //Todo: place in configuration
     var signingKey = new SymmetricSecurityKey(FromHex("YOUR_WEBSITE_AUTH_SIGNING_KEY_VALUE_HERE"));            
    
     var tokenValidationParameters = new TokenValidationParameters
     {
      RequireSignedTokens = true,
      RequireExpirationTime = true,
      SaveSigninToken = false,
      ValidateActor = false,
    
      // The signing key must match!
      ValidateIssuerSigningKey = true,
      IssuerSigningKey = signingKey,
        
      // Validate the JWT Issuer (iss) claim
      ValidateIssuer = true,
      ValidIssuer = "https://your-site.azurewebsites.net/",
    
      // Validate the JWT Audience (aud) claim
      ValidateAudience = true,
      ValidAudience = "https://your-site.azurewebsites.net/",
    
      // Validate the token expiry
      ValidateLifetime = false,
    
      // If you want to allow a certain amount of clock drift, set that here:
      ClockSkew = TimeSpan.Zero
     };
    
     app.UseJwtBearerAuthentication(new JwtBearerOptions
     {
      AutomaticAuthenticate = true,
      AutomaticChallenge = true,
      TokenValidationParameters = tokenValidationParameters
     });
    
    }
    
    /// <summary>
    /// Decodes a Hex string
    /// </summary>
    /// <param name="hex"></param>
    /// <returns>byte[]</returns>
    private static byte[] FromHex(string hex)
    {
     hex = hex.Replace("-", "");
     byte[] raw = new byte[hex.Length / 2];
     for (int i = 0; i < raw.Length; i++)
     {
      raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
     }
     return raw;
    }
    }
    
  7. Add the usual Authorize attribute to the API controllers where authentication needs to be enabled.
  8. [Authorize]
    
That's the basics of the setup. You'll want to configure the client to store the JWT retrieved after authenticating for the session of the user, and should be sent as a 'Bearer' Authorization header on the request. This will ensure that all of your calls from the JavaScript client to the WebAPI server instance will be authenticated properly. If the JWT passed in the request header for the call doesn't pass all the validations set in the ConfigureAuth method, the call will fail as desired with a 401 Unauthorized to the client.