Node.js - Group home

Form-based authentication using Passport and RACF Node.js npms

  
IBM SDK for Node.js - z/OS® is a Javascript runtime that provides a secure, module-driven, highly scalable approach to accelerate digital transformation on IBM Z.

This tutorial will demonstrate how to create a form-based authentication on z/OS using the RACF npm module and Passport npm module.

Prerequisites



Download IBM SDK for Node.js – z/OS and follow the Knowledge Center Documentation installation instructions to install IBM SDK for Node.js – z/OS.



Authenticating with the z/OS RACF npm Module



Step 1: Starting a New Project



Start a terminal session and connect to your z/OS UNIX System Services (z/OS UNIX) environment.  
Create a directory named my-racf-project for your project and change your current working directory to it.  





mkdir my-racf-project
cd my-racf-project


Use npm to create a Node.js package as follows:





npm init --yes



This command will create a package.json file which describes your package and its dependencies. The file will contain the following contents:





{
  "name": "my-racf-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}



By default, the main source file for our project is index.js.





Step 2: Installing the RACF npm dependency




The next step is to install the RACF native npm module as a dependency for our project as follows:





npm install racf



This command will generate a new file package-lock.json, as well as modify package.json to add RACF as a project dependency. The RACF npm module, as well as all of its dependencies, will be downloaded and installed under a newly created node_modules directory.




In order to use the RACF npm module, you must set the program control bit on the node executable, node dlls, and native addon dlls.




A convenience script, setup.sh, located under the node_modules/racf directory can be used to set the program control bit automatically. Invoke it as follows:



./node_modules/racf/setup.sh


You should receive output similar to the following:



+ find . -name racf.node -exec extattr +p {} ;
+ [ -e $PATHTONODE/bin/../lib/libnode.so ]
+ extattr +p $pathtonode/bin/../lib/libnode.so
+ [ -e $PATHTONODE/bin/../bin/node ]
+ extattr +p $PATHTONODE/bin/../bin/node
+ [ -e CXXRT64 ]


If you receive the following error:

chattr() error: rv=-1, errno=8B, rsn=0924041B

It may indicate that you do not have READ access to the BPX.FILEATTR.PROGCTL resource in the FACILITY class. This issue can be solved by providing access to BPX.FILEATTR.PROGCTL for your userid.


Step 3: Using the RACF npm




We will now leverage the RACF npm to authenticate a userid with a given password. In addition, we will verify that the supplied userid belongs to a given group.




Create a new file named index.js, and open it in a text editor of your choosing. 




Write the following code to the file:





// Import the RACF npm module
const racf = require("racf");

// Check if user belongs to a RACF group
function authenticateUser(userid, password, group) {

  if (racf.isUserInGroup(userid, group)) {
    // Authenticate user and password against RACF
    var isSuccessful = racf.authenticate(userid, password);
    return isSuccessful;
  }
  return false;
}

var isSuccessful = authenticateUser("MYUSERID", "MYPASSWORD", "MYGROUP");
console.log("Authentication is " + (isSuccessful ? "successful" : "unsuccessful"));





The above source code begins by importing the RACF npm module. It then defines a function authenticateUser, which takes 3 parameters: userid, password, and group.  




Before we describe the logic in the function authenticateUser, let's take a closer look at the two APIs provided by the RACF npm module:


  • isUserInGroup(userid, group) function: validates if a userid belongs to a RACF group


  • authenticate(userid, password) function: authenticates a RACF userid with a given password





In the above definition of authenticateUser, we call the function isUserInGroup to validate that the supplied userid belongs to the RACF group passed into the function. If the userid belongs to the group, we then authenticate the userid with the password provided.  




At the end of the above code snippet, a call to authenticateUser is performed. Make sure to modify the line authenticateUser("MYUSERID", "MYPASSWORD", "MYGROUP"); to match your userid and password credentials, as well as your group id.




Step 4: Running the RACF npm example






Run the index.js code with the Node.js runtime using the following command:





node index.js





If the credentials provided were valid, the following message will be displayed:

Authentication is successful





Creating a Web Form with RACF Authentication and the Passport npm Module






Now, let's use what we've learned with the RACF npm module to create a web-based form that authenticates locally against RACF.  




Step 1: Installing Passport, Passport-Local, Body-parser, Express-Session, and Express npm dependencies






In this step, we will install 5 additional npm dependencies.




    • The passport npm module is used to authenticate requests, which it does through an extensible set of plugins known as strategies. It has the added benefit of maintaining persistent login sessions. 


    • The passport-local npm exposes the local authentication strategy to be used in our web-based form. We will use this strategy to implement RACF authentication. 


    • The express npm exposes a web framework that will be used to serve our web form. 

    • The express-session npm handles and stores session data on the server-side. 

    • The body-parser npm enables the processing of HTTP POST requests. 





These npms can be installed as dependencies to your existing package as follows:





npm install passport passport-local express express-session body-parser





The above command will modify the file package.json to add passport, passport-local, express, express-session, and body-parser as project dependencies.




Step 2: Creating a web form that authenticates against RACF






The Passport npm supports 300+ authentication strategies, such as username/password and OAuth. In this tutorial, we will implement a username and password strategy. The username and password strategy is exposed in the passport-local npm and we will use it to authenticate a userid and password against RACF. The Express framework will be used to serve our web form.






Create a new file named passport.js, and open it in a text editor of your choosing.  




Write the following code to the file:





var express = require('express');
var passport = require('passport');
var Strategy = require('passport-local').Strategy;
var racf = require('racf');


// Configure the local strategy for use by Passport.
//
// The local strategy require a `verify` function which receives the credentials
// (`username` and `password`) submitted by the user. The function must verify
// that the password is correct and then invoke `cb` with a user object, which
// will be set at `req.user` in route handlers after authentication.
passport.use(new Strategy(
  function(username, password, cb) {
    try {
        isAuthenticated = racf.authenticate(username, password);
        if (!isAuthenticated)
            return cb(null, false);
        else
            return cb(null, username);
    } catch(err) {
        return cb(err);
    }
  }));

// Configure Passport authenticated session persistence.
//
// In order to restore authentication state across HTTP requests, Passport needs
// to serialize users into and deserialize users out of the session. The
// typical implementation of this is as simple as supplying the user ID when
// serializing, and querying the user record by ID from the database when
// deserializing.
passport.serializeUser(function(user, cb) {
  cb(null, user);
});

passport.deserializeUser(function(user, cb) {
  cb(null, user);
});

// Create a new Express application.
var app = express();

// Use application-level middleware for common functionality, including
// POST request parsing, and session handling.
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));

// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.use(passport.session());

// Define routes.
app.get('/', (req, res) => res.sendFile('form.html', { root : __dirname}));

app.post('/login',
  passport.authenticate('local', { failureRedirect: '/' }),
  function(req, res) {
    res.redirect('/success');
  });

app.get('/success',
  function(req, res){
    res.send("Welcome " + req.user+"!!")
  });

const port = 3000;
app.listen(port , () => console.log('App listening on port ' + port));




The source code begins by importing the dependency modules. The RACF authentication logic is implemented as a new username and password Passport strategy, with the resulting strategy passed to passport.use(). The functions passport.serializeUser and passport.deserializeUser are implemented for session persistence and in our implementation we use the userid as is. The remainder of the code defines the GET and POST routes. On the root ('/') URI request handler, we display the html form. If the credentials provided are valid, we redirect the browser to '/success' URI, otherwise we redirect back to the root ('/') URI.

The next step is to create an html file, which will be used to display the form. Name the file form.html, and copy and paste the following contents to it:



The form has two text inputs: username and password, and one input button that is used to submit the form. Once the Log in button is pressed, the form will redirect to the /login URI.




Step 3: Running the Passport npm example



Run the passport.js code with the Node.js runtime using the following command:




node passport.js



Our web server will begin listening on port 3000. You can now direct your web browser to port 3000 on the IP address or domain name of the server you are running this code on. You will be presented with the following form:



Enter your credentials and press Log in. The server will now authenticate your userid and password credentials against RACF. If successful, you will be redirected to a "Welcome" page:



Congratulations!