Securing Node.js Applications With OAuth2 and Azure
I’m a big fan of both node.js for building web-enabled servers and of Windows Microsoft Azure for hosting them… and the combination of the two is pretty compelling, if not necessarily widespread (yet ). I recently dug into the gory details of authenticating node applications against Azure Active Directory, and thought I’d walk through how to set this up. Now that it’s done it seems straightforward enough… but digging in on the front end was not for the faint of heart or weak of constitution. Nor was it particularly well-documented. Hopefully this post helps that a bit.
Web applications that expose sensitive data and/or functionality need to authenticate users against some security store. Hundreds of years ago cavemen rubbed sticks together, stored cleartext passwords in their database, and invoked all kinds of unholiness in the name of “security”. We’ve evolved a bit (thank goodness) and anymore this will typically involve federated identity and delegation of authentication details to trusted third parties like Facebook, Google, Twitter, or Microsoft, using identity protocols like WS-Federation, OpenID, and OAuth/OAuth2. We’ll see an example of how this works, below.
In this walkthrough I’ll specifically demonstrate the following:
- setting up a directory (or “tenant”) inside Azure Active Directory
- within that tenant, configuring an application for authentication
- creating a basic node.js app that demonstrates the OAuth authentication handshake with Azure
- provision a MongoDB instance to hold app-related user information
- associating the node app with the Azure application created in step 2
First, Some Assumptions
To make the most of this, you’ll need basic comfort with both node.js and Azure. I’m also assuming you have an Azure account and know your way around the management portal (note that I’m using the current portal, not the new one recently previewed at Build 2014).
For convenience, I’m hosting my node application in an Azure Web Site. This is not a requirement for use of Azure AD authentication; your code can run anywhere as long as it’s publicly accessible (part of the OAuth protocol involves an HTTP callback into your app, so your app must be visible to the outside world). I’ll walk through deployment of the app to Azure, below.
Although you can build this app on any OS with nothing more than a text editor and a node.js command line, I’ve chosen to use Visual Studio 2013 and the awesome Node.js Tools for VS2013. The node tools have many nice features, but the single most compelling item for me is the Azure debugging integration… in a nutshell, you can deploy your node app to Azure and, with a few additional mouse clicks, debug your remote app within Visual Studio as though it were running on your local machine. Breakpoints, call stacks, variables, all of it. Craziness.
Note that the Azure node debugging experience requires not only the aforementioned Node.js Tools beta, but also the Visual Studio 2013 Update 2 RC install. If you’re a VS dev, run-don’t-walk and install these now.
Let’s Set Up Azure AD
First, browse to the Azure portal and click on the ‘Active Directory’ tab down the left hand side. By default you’ll have a single tenant already created, let’s ignore that one for the moment and create a new one… click the ‘Add’ button at the bottom of the page:
Fill in the details for your new tenant and click the checkbox at the bottom right:
Once your tenant is created, clicking it in the grid view will take you to its details page. Select ‘Users’ at the top and note that your top-level Azure login is already added as a Global Admin:
Configuring An Application Within Azure AD
Now select the ‘Applications’ item. This presents a (currently empty) list of applications configured to authenticate against this tenant. Click the ‘Add’ button and let’s add one.
When you click ‘Add’, you’re asked if you’re building a new app or integrating an existing one. Select the first option:
Next, give your app a descriptive name and choose ‘web application and/or web api’:
After this, you’ll need to provide both a login URL for your app and a URI to uniquely identify this app from others. For now use https://localhost/login and https://<yourtenantname>.onmicrosoft.com/<yourappname> (replacing tenant and app names with the ones you chose in previous steps). We’ll change the login URL later, once we deploy our app to an Azure Web Site.
A Basic, Secured Node Application
To get you started, I’ve created a simple node app that enables the basic OAuth authentication flow. You can find that code on Wintellect’s GitHub repository, here. Download the code and put it someplace handy. Though you can accomplish the end goal of configuring a node app to authenticate against Azure AD without using VS2013 for development, I’ll be using it for the remainder of this walkthrough (for easy Azure publishing, debugging, etc.).
(if you don’t have the node templates you probably don’t have the Tools installed )
Click OK and browse to the folder containing the node application code you just downloaded. Then select Next, then Next again to continue to the last page of the wizard.
Here, select the location for your node app’s project file (recommended: put it in the same folder where you downloaded the code) and then click Finish.
When the wizard completes, navigate to Solution Explorer and examine the project file layout:
The code itself is well-commented inline to describe the moving parts and overall logic flow, but here’s a brief description of the major elements:
This is a project pseudo-folder that displays a hierarchical view of all npm-based dependencies for the app (for the uninitiated, npm is more or less NuGet for node). Dependencies can be added/removed a few ways: manually editing the package.json file (like any other node app), via the command line using ‘npm install someDependency’, or by right-clicking this npm folder and selecting ‘Manage npm Modules…’. All these options ultimately modify the package.json file, so do whatever you’re most comfortable with and know that under the covers its all the same end result.
Node doesn’t impose a specific view engine on your app, and there are a number to choose from. This app uses the ejs view engine, which is conceptually similar to .NET view engines like Razor. This folder contains all the view templates rendered by the app.
This file contains configuration info needed to connect to Azure, MongoDB, etc. We’ll edit the values in this file in a later step.
This file contains the basic connection and schema information for our MongoDB instance. We’ll use Mongo to hold app-specific information about authenticated users (though nothing sensitive like passwords, etc… those remain securely stored in Azure AD).
This module sets up our Express-based HTTP endpoints… /login and /logout are self-explanatory, the root path is used to display basic user info, /account display a bit more detailed info (for authenticated users only), and the /auth/* paths are used during the OAuth handshake process.
This module configures the OAuth2 strategy specifically for Azure AD. Like many secured node applications, this one uses passport for authentication. Passport is a high-level, easy-to-use node module that abstracts away provider-specific authentication details into pluggable strategy modules. There are dozens of strategies to choose from, including one that takes care of most of the Azure AD connection goo… for illustration purpose I’m eschewing that one here and using the generic OAuth2 strategy module.
As you walk over the code in this module, note the Azure-specific customizations for request payload, token deserialization, etc.
This is the standard node.js file that specifies app-level configuration like version, dependencies, etc. You can edit this file directly, even if you’re working within Visual Studio… the GUI enhancements for the VS node tools simply read/write to this file, as needed.
This logic wires up our passport authentication instance with our OAuth2 strategy, and also defines overrides for serializing/deserializing authentication info into a session cookie (like most interactive web applications, this one uses sessions to allow a single login to persist for the lifetime of the client browser activity).
This is the main entrypoint to our app (as designated in package.json). It invokes various config (in the desired order) and ultimately establishes an HTTP listener to handle incoming requests. If you’ve spent more than 30 seconds with node this is pretty standard fare.
Here’s a minor allowance for Azure-specific node behavior. This file defines configuration info for the iisnode module that allows hosting of node apps behind IIS, within Azure. It’s boilerplate so you can mostly ignore it, and if you’re not deploying to Azure (or any Windows box) then you can completely ignore it.
Another file you can ignore if you’re not deploying to Windows or Azure. This config provides for the aforementioned (and totally awesome!) remote debugger behavior within VS 2013. If this doesn’t apply to you, skip it and carry on.
Before we move on, let’s take care of some housekeeping. First we need to update the project dependencies. As mentioned above, there are a few ways to do this… the easiest is to right-click the ‘npm’ node in the project tree and select ‘Install Missing npm Modules’. This will pull down all node modules on which the project depends… Express, Mongoose, etc. It’ll take several seconds to complete.
After that, let’s do an initial push of our app to an Azure Web Site. Right-click the project node in Visual Studio and select ‘Publish’:
You’re presented with a few options, select ‘Windows Azure Web Sites’:
If you’re prompted to log into your Azure account (if you don’t have one, sign up for a free trial here or through your MSDN account), do so and then choose a new web site, giving it a unique name and appropriate region but no database:
Click ‘Next’ through the following dialog page, and then select ‘Debug’ configuration when given that choice (this allows you to attach the remote debugger later on, if you wish):
From here, click ‘Publish’ and if all goes well your app will be deployed to Azure. Don’t worry if you see an error in the browser after deployment, we still need to do some final configuration of the app before we’re ready to test.
One final thing… make a note of the public URL of your newly deployed app. It’s probably something like http://yourapp.azurewebsites.net. We’ll need that in our next step.
About That Azure App Configuration
Let’s revisit our app configuration in Azure and in code and finalize things so we can run the app. Go back to the application configuration in the Active Directory section of the Azure management portal. Click on ‘Configure’:
Using the app URL for the web site created above, replace the Sign-On URL value with the value of your newly deployed app’s public URL, with a “/login” suffix… probably something like this: https://yourapp.azurewebsites.net/login (note the use of https):
Copy the client ID value and make a note of it, you’ll need it in a moment:
Make a note of the host name portion of the app ID URI, again you’ll need this shortly:
Change the reply URL to use the server name of the web site you published your node app to in the last section (use https and add ‘/auth/waad/callback’ as the path), and make a note of this value for later use, too:
The last step is to generate a new client secret under the ‘Keys’ section. In the dropdown box, select (for starters) a 1 year duration for the new key (you’ll be able to retrieve this key value (one time only) after you save all the changes to this page):
Finally, click ‘Save’ at the bottom to persist your changes:
Now go back and copy the client secret value you created under the ‘Keys’ section… save it for the next step:
To recap, at this point you should have copied and saved somewhere handy three distinct values:
- the client ID (a GUID)
- the hostname from the app ID URI (“yourtenant.onmicrosoft.com”)
- the callback URL (“https://yourapp.azurewebsites.net/auth/waad/callback”)
- the generated client secret (a long string of characters)
MongoDB Is In Your Cloud
The next step is to provision a MongoDB instance to hold our user information (again, not passwords… this will be app-related user info). There are several options for using Mongo within Azure… we’ll make use of MongoLab’s free “database-as-a-service” offering, which cleverly allows you to use Mongo within Azure without spinning up a VM, installing and manually administering an OS, etc.
From within the Azure management portal root page, select ‘Add-Ons’ on the left hand side. Then click ‘New’ at the lower left:
Select ‘Store’, which will open a dialog presenting a list of add-ons to choose from. Scroll down the list and select ‘MongoLab’:
Click next and then choose a plan (free to start), name, and appropriate region. Click next again and then select ‘Purchase’ (assuming you selected the Sandbox plan there’s no cost). Once you’re back on the Add-On listing screen, click the ‘Connection Info’ button at the bottom of the screen and copy the connection string someplace safe… you’ll need it next.
Connect The Dots
The last step in this process is to go back to the code and update the settings in config.js with the values needed to connect to Azure AD, MongoDB, etc. Open the config.js file and amend it as follows:
- change ‘authUrl’ to use the host name of your tenant, probably ending up something like this… https://login.windows.net/yourtenant.onmicrosoft.com/oauth2/authorize
- change ‘tokenUrl’ to use the host name of your tenant, probably ending up something like this…https://login.windows.net/yourtenant.onmicrosoft.com/oauth2/token
- change ‘clientId’ to use the value copied from your application config in Azure AD (this is a GUID value)
- change ‘clientSecret’ to use the value copied from your application config in Azure AD (this is a long string of nonsense text)
- change ‘callbackUrl’ to the value copied from your application config (“Reply URL”) in Azure AD, something like this…
- change ‘mongodb’ to the connection string for your MongoDB instance
Save these changes locally, and then re-publish your app to Azure by (again) right-clicking the project in Visual Studio and selecting ‘Publish…’. Keep the default wizard values and push your changes to the server.
Let’s Test It Out!
Your app should be ready to test. Open a browser and navigate to the root page of your deployed app at https://yourapp.azurewebsites.net (note the use of https). You should see the home page, like this:
Click ‘Log In’ and then ‘Login with Azure’. You should see a brief delay as your authentication request is routed to Azure and then ultimately back to your application. If you’re prompted to log into your Microsoft account, do so (depending on your personal usage, your credentials may already be cached by your browser).
If you’re successfully authenticated, you should see your name on the subsequent landing page:
Clicking on ‘Account’ will display additional information about your account, including some application-specific details stored in MongoDB (and not in Azure AD):
Clicking ‘Log Out’ will end your app session and take you back (unauthenticated) to the home page.
Congratulations! You’ve successfully deployed a node app to Azure Web Sites and authenticated against Azure AD using the OAuth2 protocol. You’ve also stored application-specific user data in MongoDB.
Debugging and Troubleshooting
A few tips if you’re having trouble…
- Obvious trouble spots are the config settings in node. If you can view the app home page without logging in, but you’re seeing HTTP 500 errors when attempting login, double-check that you’ve copied the settings correctly from the application page in the Azure AD management portal.
- Also… don’t mix up the app URL (https://myapp.azurewebsites.net) and the tenant URI (https://mytenant.onmicrosoft.com/appname).
- If you’re unable to view even the app home page, trying debugging it on a local machine. Set a breakpoint on the first line of code in server.js and then F5 from Visual Studio. Step line-by-line and see if you can spot the issue (node will dump any errors to the console window). You won’t be able to run through the full authentication sequence locally, but you should be able to pull up the initial home page.
- As I mentioned above, its possible to use remote debugging of your app running in Azure from inside Visual Studio 2013… it was a huge help to me in putting this demo together, in fact. See here for more details on setting this up.
I hope you’ve found this walkthrough useful. If you’re interested in learning more about node development (particularly from a .NET developer’s perspective), check out my video Node.js For the Confused on WintellectNow. Use promo code Lane-13 for a free trial.