Secure your IBM BlueMix web app with OAuth 2.0 using the IBM ID Single Sign-on Service

Authentication and authorization often form the starting set of security capabilities one adds to a web application. Due to the widespread need for these capabilities across web applications of various types, numerous standards are available, including OpenID, OAuth, and others. For example, in case of authorization, OAuth 2.0 has been broadly adopted by vendors including IBM, Google, LinkedIn and others. This post describes how a Node.js application deployed to IBM BlueMix can be augmented to use IBM ID, which is an OAuth 2.0 compliant, policy-based authentication service that provides an easy to use single sign-on (SSO) capability for web and mobile applications.

IBM ID provides access to IBM applications, services, communities, support, on-line purchasing, and more. IBM ID credentials are easy to create and your profile information is centralized so you can maintain it in a convenient and secure location.[1]

This post extends an earlier entry from this blog on how to deploy a minimal, hello world style application to Node.js on IBM BlueMix. The authentication and authorization approach presented here can be easily layered onto any existing Node.js app.

Getting Started

To try the code described in this post, you have to have your own IBM ID account. It is easy to register for one using the following form: https://www.ibm.com/account/profile/us?page=reg

Throughout this post ibm-sso-nodejs-sample will be used as the BlueMix application name. The hostname for the application is going to be generated by replacing a substring ‘sample’ from the name with a random value (e.g. ibm-sso-nodejs-42) to minimize the chances of name collisions across multiple readers of this post.

Start by cloning the ibm-sso-nodejs-sample from the git repository available here: https://github.com/osipov/ibm-sso-nodejs-sample

The next section is going to describe the implementation of the code, so if you’d like you can skip forward to the Prepare and push code to BlueMix section to try out the code.

Implementation Overview

package.json file (shown below) declares key dependencies used in this sample.

Node.js Express framework[2] simplifies the setup of the routes and management of the application session state. Passport[3] is another Node.js framework for integration of various authentication providers (including OAuth2 based ones) into an Express based application. The library dependency for the IBM ID OAuth2 provider[4] is declared on line 11. If you are planning on debugging the application on your local machine (i.e. outside of BlueMix), then you should execute the npm install command from the ibm-sso-nodejs-sample directory to download and install the dependencies from package.json into your environment. Otherwise, the dependencies will be downloaded automatically inside the BlueMix runtime container during the execution of the cf push command. The application code for this sample resides in the main.js file (snippet below). The initial section of main.js[5] contains commonly used setup code to import the two frameworks mentioned earlier: Express and Passport. Note that this implementation uses the Express memory based session store[6]. While this is a reasonable approach for sample code, if you decide to take this code into production, you should migrate to using an alternative session cache. Examples of alternatives include the IBM Session Cache[7] and Redis[8], both available from the BlueMix service catalog.

The sample provides templates for callback functions that are invoked by the Passport framework when serializing/deserializing a user[9]. Although this sample does not specify any code in these functions, a production class application would insert code here to manage application specific extensions to a user object, e.g. an application specific user ID.

The application is initialized using BlueMix specific code to extract environment variables from the BlueMix runtime container and to set the variables related to the hostname, port number, URL as well as the IBM ID OAuth 2.0 client ID and client secret[10]. Note that the variables for the ID and secret are set to blank in the source code repository but will be modified according to the instructions later in this post.

To use the IBM ID OAuth2 provider, one must include the code from lines 34-52 into a Node.js application[11]. clientID, clientSecret and callbackURL are the minimal required parameters to create a new instance of the IbmIdStrategy OAuth2 strategy. The callback function[12] processes the access and the refresh tokens retrieved in case of a successful OAuth 2.0 authorization. The profile variable carries the IBM ID profile information supplied by an IBM ID user. The code used to save the profile information in the session[13] is arbitrary, an alternative implementation can choose to disregard the profile information entirely or extract just the necessary attributes of the profile to populate the session.

OAuth2.0 specification recommends the use of a state variable to help prevent cross-site request forgery[14]. Since the code in this post is not intended for production, the ‘/ibmid/auth’ route does not pass a state attribute along with the scope to the passport.authenticate function. When adopting this code for production, you should implement a random string generator to set the state attribute and then verify that the random value is correctly returned back as a req.query.state variable in the handler function of the ‘ibmid/auth/callback’ route[15].

The rest of this section describes the routes added just to demonstrate the OAuth 2.0 based authentication and authorization process, there is a “public” route that can be accessed without any authentication whatsoever[16], a “private” route that requires a client to authorize with IBM ID[17], a “logout” route that logs a client out of an IBM ID authentication[18], and an “error” route that is used to display a message to a client in case of any OAuth 2.0 errors[19].

Prepare and push code to BlueMix

Before proceeding with the rest of the instructions in this post, push the code you cloned from the github repository to BlueMix using the following command

cf push ibm-sso-nodejs-sample -n ibm-sso-nodejs-$RANDOM -c 'node main.js'

The result of the push command should provide a session similar to the one shown in the snippet. Note the value of the URL, e.g. from the session which should look like line 50 from the snippet. It will be referred to as <URI> in the instructions below.

Any IBM ID OAuth 2.0 client must be registered with IBM. To do so, Open https://idaas.ng.bluemix.net/idaas/developer/manageclients.jsp using your browser, login using your IBM ID and click on “Create a new client”. Leave client identifier and client secret as then type in ibm-sso-node-js-sample for the Display Name and specify <URI> for the Redirect URI. Check off the Enabled box, click Submit and take note of the client ID and secret values that were randomly generated. The rest of this post will refer to their values as <ID> and <SECRET> .

To propagate the OAuth 2.0 credentials that you just obtained from IBM, execute the following commands to create Cloud Foundry user defined variables:

cf set-env ibm-sso-nodejs-sample SSO_CLIENT_ID <ID>
cf set-env ibm-sso-nodejs-sample SSO_CLIENT_SECRET <SECRET>

Do one last push to persist the user defined variables.

cf push ibm-sso-nodejs-sample -c 'node main.js'

Test out the app

Start by navigating to the URL assigned to you by BlueMix. You should see a ‘Hello World’ message along with a login link.

Clicking on the link should force you to login using the IBM ID you created earlier.

Once you are logged in, you should see a Hello message with the first and last name you used when registering for your IBM ID.

References

[1] https://www.ibm.com/account/profile/us?page=regfaqhelp
[2] http://expressjs.com/
[3] http://passportjs.org/
[4] https://www.npmjs.org/package/passport-ibmid-oauth2
[5] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L2-L12
[6] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L13-L14
[7] https://www.ng.bluemix.net/docs/Services/ECaaS/session.html
[8] https://www.ng.bluemix.net/docs/Services/Redis/Index.html
[9] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L19-L25
[10] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L27-L32
[11] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L34-L52
[12] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L40-L43
[13] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L41-L42
[14] http://tools.ietf.org/html/rfc6749#section-4.1.1
[15] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L51
[16] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L63-L65
[17] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L67-L70
[18] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L76-L78
[19] https://github.com/osipov/ibm-sso-nodejs-sample/blob/master/main.js#L72-L74