Integrating Azure Active Directory Security Into NancyFx (part II)

Introduction

This is the second in a series of posts focusing on two .Net development technologies: Azure Active Directory (AAD) and NancyFx (aka Nancy). Nancy has emerged as a scalable web-development framework affording what it labels the "Super Duper Happy Path" (SDHP) for simplistic creation of web applications and APIs; AAD is a Azure/cloud-hosted directory service affording native client and web client application authentication as a service (SaaS). We are securing a Nancy web application with AAD; the code presented in this series is available here.

Part II: AAD From a Nancy Web Application

This post covers the creation of a new Nancy web application that will leverage the AAD configurations we created in part I to authorize users to access secured resources.

Make a Nancy Web Application

nancyFx

  1. Within Visual Studio create a Console Application named "WebApp".
  2. Install the Nancy.Hosting.Self nuget package.
  3. Add a reference to Nancy.Hosting.Self and within Program.Main add the following:

    using (var host = new NancyHost(new Uri("http://localhost:1234")))
    {
       host.Start();
       Console.ReadLine();
    }
    
  4. Add a new class named SampleModule.cs to handle an HTTP request to the default route:

    public class SampleModule : Nancy.NancyModule
    {
       public SampleModule()
       {
          Get["/"] = _ => "Hello World!";
       }
    }   
    

    This class inherits NancyModule -- the mechanism by which Nancy handles HTTP verbs to different places. Our project now looks like this within Visual Studio:

Nancy .csproj

F5 to launch the console application and navigate to http://localhost:1234/ to see Nancy say "Hello World!"

Nancy Hello World

Integrating AAD into our Nancy Web Application

Now that we have AAD configured and a functional web application it's time to integrate them.

Create a new class within WebApp.csproj named AADHelper containing the following boiler plate code that will be used to leverage AAD from within our web application:

public static class AADHelper
{
  public static string GetAuthorizationURL()
  {
    string authorizationUrl = string.Format("https://login.windows.net/{0}/"+
    "oauth2/authorize?api-version=1.0&response_type=code&client_id={1}&"+
    "resource={2}&redirect_uri={3}",
    AAD.TENANT_ID,
    AAD.CLIENT_ID,
    AAD.APP_ID_URI,
    AAD.REPLY_URL);
    return authorizationUrl;
  }          
  public struct AAD
  {
    public static readonly string TENANT_ID  
    = "????????-????-????-????-????????????"
    public static readonly string CLIENT_ID  
    = "???????????????????????????????";
    public static readonly string APP_ID_URI 
    = "https://?????.onmicrosoft.com/WebAppResource";
    public static readonly string REPLY_URL  
    =  "http://localhost:1234/Authenticated";
    public static readonly string CLIENT_KEY 
    = "???????????????????????????????";
  }        
}

Before proceeding populate the variables within the "AAD" struct to match those of your own AAD configuration. Here's where to locate them:

  • TENANT ID - an Azure Active Directory "tenant" (aka Domain) which is identified by us as a key (which can be found via) :
    • Azure Management Portal ->
      • Active Directory ->
        • Applications ->
          • View Endpoints (at the bottom, center of the screen) the TENANT_ID appears sandwiched within the various end point urls as a guid-like key
  • CLIENT ID - this is the Client ID for the WebApp (we configured within AAD in Part I).
  • APP ID URI - this is the APP ID URI for WebAppResource (we configured within AAD in Part I).
  • REPLY URL - this is the Reply URL for the WebApp
  • CLIENT KEY - This is the 'secret' configured within AAD to associate the calling code with the configured application. We configured this in part I within the "keys" section of our WebApp application.

Create a Secure Nancy Module

A good first step in securing access to our web application is to block unknown parties from accessing private information. Let's do this within WebApp.csproj by creating a new NancyModule with some simple security logic to accomplish this. Create a new class named SecureModule.cs :

    using Nancy;
    using Nancy.Responses;
    using System;

    public class SecureModule : NancyModule
    {
        Before += ctx =>
        {
            return ctx.CurrentUser == null ||
                   String.IsNullOrWhiteSpace(ctx.CurrentUser.UserName)
                ? new RedirectResponse("/login")
                // else allow request to continue unabated
                : null;
        };

        public SecureModule()
        {
            Get["/Private"] = _ =>
            {
                return "Secret stuff!";
            };
        }
    } 

We created a new HTTP route, "/Private" that will execute when a user navigates to http://localhost:1234/Private -- but notice the code block beforehand starting with Before +=. This code is invoked before each HTTP request within this NancyModule (only) reaches its destination. It redirects requests from unidentified users to a "/login" route. Only if the initiating request is associated with a valid user it is allowed to continue to its destination.

We haven't yet coded support for a /Login route that unauthenticated users are redirected to; let's do so now because this is where the rubber hits the road between our Nancy Web Application and AAD.

Invoke AAD Login Screen From our Web App

Now our application supports redirecting an unidentified client to /login. Let's take it a step further and utilize AAD to present a login dialog to authenticate users against those AAD configurations we previously defined.

Add a new HTTP route within the SampleModule.cs file we created within WebApp.cs (below Get["/"] ... "Hello World!";):

    Get["/login"] = _ =>
    {
      return new Nancy.Responses.
      RedirectResponse(AADHelper.GetAuthorizationURL());
    };

Now when a user is redirected to this /login route the web browser will redirect a client to be authorized by the AAD tenant we point it to. F5 the application and navigate to http://localhost:1234/login to see what happens. If everything is configured correctly you will be prompted with a web dialog page asking you to sign in.

AAD login dialog

There are a couple of things to notice about this web dialog.

  1. The address for this page is served from login.microsoftonline.com ; this isn't anything from within our WebApp or Nancy - this page is served as a callback by AAD to our browser redirection within /login.
  2. Multiple account credentials are visible. My hotmail address is there in addition to a user I have for an AAD tenant. If I login with either of these credentials I will not be provided authorization to the WebApp. This is because these two accounts/users do not exist within our WebApp tenant on AAD. They are outside of our application's ecosystem.

Try to sign in using various usernames and passwords; you will notice that AAD prohibits you from proceeding further until you enter a valid login for a user and password authorized for the WebApp (that we configured within our tenant in part I). Only after logging in with "Test@YOURTENANTNAME.onmicrosoft.com" and the associated password will you be allowed to continue (be sure to logout of any other associated microsoft accounts that might be cached within your browser).

security blockade

This is great! AAD is basically playing the part of a guard dog on our behalf. It stonewalls any attempt to access our web application until a user proves he should be permitted access by providing valid user credentials associated with the applicable AAD tenant's configuration.

After providing correct credentials for the user we previously configured, you will be redirected to the following screen:

Nancy Authenticated 404

What looks like an error actually isn't - this is success! It means that AAD effectively authenticated a user and redirected him to the REPLY_URL configured for our WebApp (see part I); unfortunately we haven't configured a route within our SecureModule.cs that matches "/Authenticated" -- ergo Nancy's cute 404 monster cartoon. Take a look at something else -- AAD sent us an authorization "code" as a query parameter as well.

Things are starting to get fun here - what is this mysterious code? What will we do with it? What part does Nancy play?

Conclusion

In this post we created a NancyFx web application, referenced AAD tenant configuration we initialized in part I and leveraged AAD to authorize clients. What comes next is to more fully integrate security behavior between Nancy and AAD. We do that next in part III.