Josh Nerius

7 minute read

In Inbound OAuth Auth Code Grant Flow Part 1 - Getting Started with Postman, we configured Postman to use the new OAuth functionality available in Istanbul. In this post, we’re going to implement this functionality in a real web application.

A Few Notes About the Node.js “My Work” App

The ServiceNow Interfaces team has released a series of sample applications that demonstrate the use of ServiceNow APIs. These apps are available on GitHub here, and are great resources for learning more about ServiceNow APIs and getting some example code up and running quickly. My Work is one of these samples. It is a simple task management application built on Node.js using Express.js and other common Node libraries. It interacts with task records in a ServiceNow instance using a simple Scripted REST API.

Currently, the app uses Basic Authentication when making API calls to ServiceNow. In this blog post, I will walk you through the process of adding support for OAuth and Auth Code Grant Flow using Passport.js.

About Express.js and Passport.js

Express (not to be confused with ServiceNow Express) is a simple/minimal web application framework used to build Node.js web applications. The My Work app is built on Express.

Passport.js is an authentication middleware library for Express web applications. For more information about Express Middleware, I highly recommend spending some time reading the documentation.

1. Configure Inbound OAuth in your Instance

Before we make any changes to the Node.js app, we must add a record to the OAuth Application Registry. Follow the steps described in Part 1 and use the following values when filling out the form:

Make note of the generated Client ID and Client Secret values, we’ll need them in a few minutes.

2. Set Up the Node Development Environment

Follow the instructions in the My Work App README. I won’t repeat all of the instructions here, but at a high level, you will:

  1. Install Node.js if necessary
  2. Clone the My Work git repository to the directory of your choice
  3. Install the My Work update set in your instance
  4. Start the My Work server

If you run into issues with any of these steps, leave a comment on this post and I’ll be happy to help troubleshoot.

3. Install Passport

Run the following commands from a Terminal:

$ cd example-restclient-myworkapp-nodejs

$ npm install passport –save

$ npm install passport-oauth –save

install_passport_and_oauth_strategy.gif

This accomplishes two things:

  1. Installs the passport and passport-oauth packages into our local copy of the My Work app.
  2. Adds dependencies to the project’s package.json file

3.1 Add Code to Initialize Passport

Now that passport is installed, we must initialize it in our server code. In the editor of your choice, open server.js. Find the code block that begins with app.use(session({… and ends on line 48. Add the following code after that code block:

// Set up Passport var config = require(‘./config’); var passport = require(‘passport’) B , OAuth2Strategy = require(‘passport-oauth’).OAuth2Strategy;

app.use(passport.initialize());

passport.use(‘provider’, new OAuth2Strategy({ B B B authorizationURL: config.oauth.authURL, B B B tokenURL:B B B B B B B B config.oauth.tokenURL, B B B clientID:B B B B B B B B config.oauth.clientID, B B B clientSecret:B B B B config.oauth.clientSecret, B B B callbackURL:B B B B B config.oauth.callbackURL B }, B function(accessToken, refreshToken, profile, done) { B B B B B var tokenInfo = {}; B B B B B tokenInfo.accessTokenB = accessToken; B B B B B tokenInfo.refreshToken = refreshToken; B B B B B tokenInfo.profileB B B B B = profile; B B B B B console.log(tokenInfo); B B B B B done(null, tokenInfo); B } ));

passport.serializeUser(function(user, done) { B done(null, user); });

passport.deserializeUser(function(id, done) { B done(null, id); });

4. Add a New Configuration File

We need a place to store the properties related to our OAuth configuration. Create a new file named config.js in the example-restclient-myworkapp-nodejs folder, and add the following code:

config = {};

config.instanceURL = ‘https://.service-now.com’;

// OAuth Configuration config.oauth = {}; config.oauth.clientIDB B B B = “; config.oauth.clientSecret = “; config.oauth.authURLB B B B B = config.instanceURL + ‘/oauth_auth.do’; config.oauth.tokenURLB B B B = config.instanceURL + ‘/oauth_token.do’; config.oauth.callbackURLB = ‘http://localhost:3000/auth/provider/callback';

module.exports = config;

  • Update the config.instanceURL value to reflect your instance name.
  • Set config.oauth.clientID and config.oauth.clientSecret to the values generated earlier while creating an OAuth configuration in your instance.
  • If you’ve decided to run the My Work app on a custom port, update config.oauth.callbackURL accordingly. You probably won’t need to change this value.

5. New Routes

When an unauthenticated user navigates to the My Work app, we need to redirect them to a login page in ServiceNow. When working with OAuth, this isn’t just any page, it must be a properly constructed authorization URL that matches the configuration of the 3rd party OAuth provider and contains the Client ID and Redirect (callback) URL.

To accomplish this, we’re going to add two new routes to the My Work app.

/auth/provider

This route will handle the process of redirecting users to the ServiceNow OAuth Authorization endpoint (oauth_auth.do). When a user navigates to /auth/provider, a full authorization URL will be constructed with the desired Response Type, OAuth Client ID, and Redirect URL. The user will then be redirected to that URL.

/auth/provider/callback

After a successful authorization, ServiceNow will redirect the user back to our Node.js app, specifically to this route (remember the callbackURL?). ServiceNow will include a code URL parameter containing the authorization code needed to generate an access token.

The good news is that Passport handles the heavy lifting for us, so this isn’t as complex as it sounds.

5.1 Add Routes to server.js

Near the end of server.js, you’ll find a section with a series of “router” statements. Add the following lines of code after the existing routes:

// Passport Routes router.get(‘/auth/provider’, passport.authenticate(‘provider’)); router.get(‘/auth/provider/callback’, B B B passport.authenticate(‘provider’, { successRedirect: ‘/’, B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B failureRedirect: ‘/login’}));

5.2 Test the Routes

Let’s make sure everything is working. Restart the server process:

Server listening on: http://localhost:3000

^C (Ctrl-C)

$ node server.js

Navigate to http://localhost:3000/auth/provider, and if everything worked, you’ll be redirected to the instance you configured earlier.

Log in if prompted, and click Allow on the OAuth consent screen. After clicking Allow, you’ll be redirected back to your local app. Ignore the “User session invalidated” error for now, we’ll address this in the next section.

animation-testing-oauth-redirect-with-console-output.gif

What just happened?

  1. When we navigated to /auth/provider in the local Node app, Passport built a redirect URL and redirected us to the oauth_auth.do page in ServiceNow.
  2. We then authorized the flow, effectively saying “The My Work App is allowed to talk to the instance as me”.
  3. ServiceNow generated an authorization code and sent it back to the local app as a code URL parameter.
  4. Behind the scenes, Passport.js used the authorization code to request a token from oauth_token.do.
  5. We logged the Access and Refresh tokens in the console log, confirming that we did indeed get tokens from ServiceNow.

It’s now up to us to store and use that token when making REST requests.

6. Using the Access Token in our REST Requests

Now that we have a token, we need to modify the code that makes calls to the Scripted REST API so it sends the Access Token instead of Basic Auth credentials.

6.1 Update taskDispatcher.js

Open dispatcher/taskDispatcher.js and make the following changes:

  1. Add var config = require(‘../config’); to the top of the file.
  2. Replace the first four lines of the getTasks method with:

    var session = serverRequest.session;
    if (session && session.passport && session.passport.user.accessToken) {
    B B B var SNTask = serverRequest.app.get(‘snTask’);
    B B B var options = serverRequest.app.get(‘options’);
    B B B var snTask = new SNTask(config.instanceURL, session.passport.user.accessToken, options);

6.2 Update task.js

Open sn_api/task.js and make the following changes:

  1. Update the definition of the Task function so it matches the following code:

    function Task(snInstanceURL, accessToken, options) {
    B B B B this.snInstanceURL = snInstanceURL;
    B B B B this.accessToken = accessToken;
    B B B B this.options = options;
    }

  2. In the getTasks() method, find this code:

    headers: {
    B B B B ‘Cookie’: this.snCoookie
    }

    And replace it with:

    auth: {
    B B B B bearer: this.accessToken
    }

7. Try It!

If everything went as planned, the My Work app should now be (partially) functional.

Restart the server process one last time, and navigate to http://localhost:3000/auth/provider.

Repeat the process of authorizing the request. This time, when you’re redirected back to the My Work app, you should see something like this instead of an error!

task-list-success.png

8. Summary and Next Steps

The working versions of each file are attached to this blog post. Use these if you get stuck at any point along the way.

Here’s a summary of what we accomplished:

  1. Downloaded and installed Node.js and the My Work Express application
  2. Added Passport.js as a dependency
  3. Added routes backed by Passport functionality to handle redirects
  4. Adapted the code that retrieves tasks to use the access token retrieved by Passport

This is a great start, but there are number of additional things we should do to make the app fully operational.

  1. Make similar modifications to addComment() and getComments()
  2. Update the login page to:
    • Allow the user to choose between Basic and OAuth
    • OR implement a configuration property to control the preferred method of authentication
  3. Use the refresh token to automatically obtain fresh access tokens when necessary
  4. Automatically redirect the user to /auth/provider if their session is not authenticated

These exercises are left up to you, and hopefully the information in this post gives you the necessary starting point to go accomplish these goals.

As always, drop me a line or comment on this post if you have any questions, comments or issues!


Comments