Using Active Directory Federation Services to Authenticate / Authorize Node.js Apps in Windows Azure

It’s gotten easy to publish web applications to the cloud, but the last thing you want to do is establish unique authentication schemes for each one. At some point, your users will be stuck with a mountain of passwords, or, end up reusing passwords everywhere. Not good. Instead, what about extending your existing corporate identity directory to the cloud for all applications to use? Fortunately, Microsoft Active Directory can be extended to support authentication/authorization for web applications deployed in ANY cloud platform. In this post, I’ll show you how to configure Active Directory Federation Services (ADFS) to authenticate the users of a Node.js application hosted in Windows Azure Web Sites and deployed via Dropbox.

[Note: I was going to also show how to do this with an ASP.NET application since the new “Identity and Access” tools in Visual Studio 2012 make it really easy to use AD FS to authenticate users. However because of the passive authentication scheme Windows Identity Foundation uses in this scenario, the ASP.NET application has to be secured by SSL/TLS. Windows Azure Web Sites doesn’t support HTTPS (yet), and getting HTTPS working in Windows Azure Cloud Services isn’t trivial. So, we’ll save that walkthrough for another day.]

2013.04.17adfs03

Configuring Active Directory Federation Services for our application

First off, I created a server that had DNS services and Active Directory installed. This server sits in the Tier 3 cloud and I used our orchestration engine to quickly build up a box with all the required services. Check out this KB article I wrote for Tier 3 on setting up an Active Directory and AD FS server from scratch.

2013.04.17adfs01

AD FS is a service that supports identity federation and supports industry standards like SAML for authenticating users. It returns claims about the authenticated user. In AD FS, you’ve got endpoints that define which inbound authentication schemes are supported (like WS-Trust or SAML),  certificates for signing tokens and securing transmissions, and relying parties which represent the endpoints that AD FS has a trust relationship with.

2013.04.17adfs02

In our case, I needed to enabled an active endpoint for my Node.js application to authenticate against, and one new relying party. First, I created a new relying party that referenced the yet-to-be-created URL of my Azure-hosted web site. In the animation below, see the simple steps I followed to create it. Note that because I’m doing active (vs. passive) authentication, there’s no endpoint to redirect to, and very few overall required settings.

2013.04.17adfs04

With the relying party finished, I could now add the claim rules. These tell AD FS what claims about the authenticated user to send back to the caller.

2013.04.17adfs05

At this point, AD FS was fully configured and able to authenticate my remote application. The final thing to do was enable the appropriate authentication endpoint. By default, the password-based WS-Trust endpoint is disabled, so I flipped it on so that I could pass username+password credentials to AD FS and authenticate a user.

2013.04.17adfs06

Connecting a Node.js application to AD FS

Next, I used the JetBrains WebStorm IDE to build a Node.js application based on the Express framework. This simple application takes in a set of user credentials, and attempts to authenticate those credentials against AD FS. If successful, the application displays all the Active Directory Groups that the user belongs to. This information could be used to provide a unique application experience based on the role of the user. The initial page of the web application takes in the user’s credentials.

div.content
        h1= title
        form(action='/profile', method='POST')
              table
                  tr
                    td
                        label(for='user') User
                    td
                        input(id='user', type='text', name='user')
                  tr
                    td
                        label(for='password') Password
                    td
                        input(id='password', type='password', name='password')
                  tr
                    td(colspan=2)
                        input(type='submit', value='Log In')

This page posts to a Node.js route (controller) that is responsible passing those credentials to AD FS. How do we talk to AD FS through the WS-Trust format? Fortunately, Leandro Boffi wrote up a simple Node.js module that does just that. I grabbed the wstrust-client module and added it to my Node.js project. The WS-Trust authentication response comes back as XML, so I also added a Node.js module to convert XML to JSON for easier parsing. My route code looked like this:

//for XML parsing
var xml2js = require('xml2js');
var https = require('https');
//to process WS-Trust requests
var trustClient = require('wstrust-client');

exports.details = function(req, res){

    var userName = req.body.user;
    var userPassword = req.body.password;

    //call endpoint, and pass in values
    trustClient.requestSecurityToken({
        scope: 'http://seroternodeadfs.azurewebsites.net',
        username: userName,
        password: userPassword,
        endpoint: 'https://[AD FS server IP address]/adfs/services/trust/13/UsernameMixed'
    }, function (rstr) {

        // Access the token
        var rawToken = rstr.token;
        console.log('raw: ' + rawToken);

        //convert to json
        var parser = new xml2js.Parser;
        parser.parseString(rawToken, function(err, result){
            //grab "user" object
            var user = result.Assertion.AttributeStatement[0].Attribute[0].AttributeValue[0];
            //get all "roles"
            var roles = result.Assertion.AttributeStatement[0].Attribute[1].AttributeValue;
            console.log(user);
            console.log(roles);

            //render the page and pass in the user and roles values
            res.render('profile', {title: 'User Profile', username: user, userroles: roles});
        });
    }, function (error) {

        // Error Callback
        console.log(error)
    });
};

See that I’m providing a “scope” (which maps to the relying party identifier), an endpoint (which is the public location of my AD FS server), and the user-provided credentials to the WS-Trust module. I then parse the results to grab the friendly name and roles of the authenticated user. Finally, the “profile” page takes the values that it’s given and renders the information.

div.content
        h1 #{title} for #{username}
        br
        div
            div.roleheading User Roles
            ul
                each userrole in userroles
                    li= userrole

My application was complete and ready for deployment to Windows Azure.

Publishing the Node.js application to Windows Azure

Windows Azure Web Sites offer a really nice and easy way to host applications written in a variety of languages. It also supports a variety of ways to push code, including Git, GitHub, Team Foundation Service, Codeplex, and Dropbox. For simplicity sake (and because I hadn’t tried it yet), I chose to deploy via Dropbox.

However, first I had to create my Windows Azure Web Site. I made sure to use the same name that I had specified in my AD FS relying party.

2013.04.17adfs07

Once the Web Site is set up (which takes only a few seconds), I could connect it to a source control repository.

2013.04.17adfs08

After a couple moments, a new folder hierarchy appeared in my Dropbox.

2013.04.17adfs09

I copied all the Node.js application source files into this folder. I then returned to the Windows Azure Management Portal and chose to Sync my Dropbox folder with my Windows Azure Web Site.

2013.04.17adfs10

Right away it starts synchronizing the application files. Windows Azure does a nice job of tracking my deployments and showing the progress.

2013.04.17adfs11

In about a minute, my application was uploaded and ready to test.

Testing the application

The whole point of this application is to authenticate a user and return their Active Directory role collection. I created a “Richard Seroter” user in my Active Directory and put that user in a few different Active Directory Groups.

2013.04.17adfs12

I then browsed to my Windows Azure Website URL and was presented with my Node.js application interface.

2013.04.17adfs13

I plugged in my credentials and was immediately presented with the list of corresponding Active Directory user group membership information.

2013.04.17adfs14

Summary

That was fun. AD FS is a fantastic way to extend your on-premises directory to applications hosted outside of your corporate network. In this case, we saw how to create  Node.js application that authenticated users against AD FS. While I deployed this sample application to Windows Azure Web Sites, I could have deployed this to ANY cloud that supports Node.js. Imagine having applications written in virtually any language, and hosted in any cloud, all using a single authentication endpoint. Powerful stuff!

Author: Richard Seroter

Richard Seroter is currently the Chief Evangelist at Google Cloud and leads the Developer Relations program. He’s also an instructor at Pluralsight, a frequent public speaker, the author of multiple books on software design and development, and a former InfoQ.com editor plus former 12-time Microsoft MVP for cloud. As Chief Evangelist at Google Cloud, Richard leads the team of developer advocates, developer engineers, outbound product managers, and technical writers who ensure that people find, use, and enjoy Google Cloud. Richard maintains a regularly updated blog on topics of architecture and solution design and can be found on Twitter as @rseroter.

14 thoughts

  1. I haven’t even read your content yet but I have to say, I’m pretty impressed that you use GIFs instead of JPGs so you can animate some of the steps. Kudos to you sir!

  2. Hey Richard,
    The link for the ADFS configuring might have changed.Can you please update. This was the link [http://help.tier3.com/entries/22636576-Using-SAML-for-Single-Sign-On-to-the-Tier-3-Control-Portal] .Love your work on gif’s.

    Regards
    Ram

  3. Hey,
    I have a question in regards to ADFS Endpoint address. Do we need to provide the IP or will the machine name also work. can you please update.

    Thanks
    Ram

      1. I am with in an organizational domain and I when I use servername.domainname.com for address ,I can’t connect to ADFS Server. Can you please suggest .

        1. Sounds like you have DNS resolution issues. If you can reach it via IP address, then you know the source machine isn’t able to resolve the domain name. Then it’s about troubleshooting when it won’t resolve.

  4. Hey Richard,
    I have a client who has used optional encryption certificate and its they company policy to have it .so that, even the tokens are signed. How can I access the token. Can you please provide a code sample or steps. I have tried adding certificateReference with in App.config

    This did not help.

    Thanks
    Ram

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.