Use BankID and NemID from a Node.js application

Niels Flensted-Jensen, Feb 14, 2017

Learn how to authenticate your users with Norwegian or Swedish BankID or Danish NemID with Node.js and Express. By leveraging OpenID Connect, connecting Node applications to national identity services via Criipto Verify has become a trivial job, as shown in this post.

To immediately get a running version of the code from this post get the sample from GitHub

This example creates a site from scratch, but the instructions may as well be applied to an existing Node application. Two steps are involved in getting going from zero:

  1. Set up an account in Criipto Verify to enable OpenID Connect on top of the national identities
  2. Modify you Node application to defer authentication to Criipto Verify.

A note on test users

In order to test the use of Norwegian BankID, Swedish BankID, and Danish NemID you need test identities.

For the sample shown here, we’ve set it up to use Norwegian BankID with a hardware token. You may try it out with a test user with Fødselsnummer, Engangskode, and Personlig passord set to 15076400000, otp, qwer1234

Please let us know if you need your own test identities, and we will help you set them up. Just go to criipto.com and sign up. Once signed up you can join our Slack channel from the Criipto Verify dashboard.

1. Setting up your Criipto Verify account

As the various national and bank identity services do not support OpenID Connect or any other standard protocol, we use Criipto Verify as the identity service in the middle.

So, if you haven’t done so already, sign up for Criipto Verify: criipto.com

Name your first domain which will be a subdomain of criipto.id. This will be the domain name we will use in the following.

Sign up

Once signed up, go to the Applications tab to register your application.

Note that if you use Auth0 as your identity broker your life just got a little bit easier and you should take a look at our screencast showing how that scenario works.

But this post is not about that, so for this exercise just go for the manual option.

Create application

Remember to copy the Client ID value - you will need it when configuring the Express middleware. For the Callback URLs just enter the URL where your application may be reached for notification of the authenticated user. In our example that would be http://localhost:3000/callback

Once the application has been registered in Criipto Verify, you have one more step to get it set up for OpenID Connect. Open the Criipto Verify application registration again and switch on the OAuth2 Code flow. This will generate a client secret that you will need in your application. Be sure to copy the value or you will have to generate a new one. (As with any other password we will, of course, only store the hashed value)

Turn on OIDC

With this you will be able to set up your application to authenticate using any of the enabled identity services: Danish NemID, Swedish BankID and/or Norwegian BankID.

2. Integrate your application with Criipto Verify

For this example we use a simple, automatically scaffolded, Express application. The steps will be the same if you want to add Criipto Verify authentication to your existing application.

So starting from scratch, simply generate an Express app skeleton:

# You may have to sudo this one
npm install -g express-generator

# Scaffolding of basic site
express --view=ejs --git node-verify
cd node-verify

# Install dependencies
npm install

# Check that the site runs
DEBUG=node-verify:* npm start

This generates a simple site structure for an Express application using the EJS view engine. Navigate your browser to http://localhost:3000 to view the site.

Next add the necessary middleware to enable authentication using an OpenID Connect module. Note that we pull the OpenID Connect passport strategy directly from GitHub. As of this writing the NPM version has not been updated for a while and did not have the needed features. We will soon release a specialized Criipto Verify strategy, but till then:

# Passport middelware 
npm install --save passport

# NPM version is 4 years old
# Install OIDC strategy directly from GitHub to get needed features
npm install --save git+https://github.com/jaredhanson/passport-openidconnect.git

npm install --save express-session
npm install --save connect-ensure-login

# Needed if you want to verify JWT 
npm install --save jsonwebtoken
npm install --save jwks-rsa

Add the authentication middleware

Once the application has been created and you have installed the necessary Node modules, you need to update your application to perform OpenID Connect authentication with the Criipto Verify identity provider.

In your application file, here app.js, include the new modules:

// in app.js
const passport = require('passport');
const OpenIdStrategy = require('passport-openidconnect').Strategy;
const expressSesssion = require('express-session');

// These two only if you want to verify issued id_token
const jsonwebtoken = require('jsonwebtoken');
const jwksRsa = require('jwks-rsa');

Note that the jsonwebtoken and the jwksRsa modules are needed only if you plan to verify the token issued by Criipto Verify. In the scenario outlined here, where you simply authenticate the user and extract the user information, this is not a hard requirement as the token is being retrieved over a confidential backchannel using the client ID and client secret.

Next is the instantiation of the passport strategy. As you can see, several settings revolve around the domain name of your Criipto Verify domain.

// in app.js
// ...
// Example assumes you use the Criipto Verify endpoint structure
var strategy = new OpenIdStrategy({ 
    issuer: 'https://' + process.env.DOMAIN,
    authorizationURL: 'https://' + process.env.DOMAIN + '/oauth2/authorize', 
    tokenURL: 'https://' + process.env.DOMAIN + '/oauth2/token', 
    clientID:     process.env.CLIENT_ID,
    clientSecret: process.env.CLIENT_SECRET,
    callbackURL:  process.env.CALLBACK_URL || 'http://localhost:3000/callback',
    acr_values: 'urn:grn:authn:no:bankid:central',  // Pick one of the supported authentication methods
  }, function(iss, sub, profile, jwtClaims, accessToken, refreshToken, params, done) {
    // To verify signature on the params.id_token, uncomment
    // and add the verification function shown further down.
    // verifySignature(params.id_token, done);
    return done(null, jwtClaims);
  });

passport.use(strategy);

// This can be used to keep a smaller payload
passport.serializeUser(function(user, done) {
  done(null, user);
});

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

app.use(expressSesssion({ secret: 'Some secret you say?', resave: false, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());

// ...
// The above must be added before the routing modules, e.g.:
app.use('/', index);
app.use('/users', users);

Note the acr_values parameter for the strategy setup. This identitfies the specific kind of authentication you choose. Your options include:

  • Norwegian BankID:
    • Mobile: urn:grn:authn:no:bankid:mobile
    • Hardware token (kodebrikke): urn:grn:authn:no:bankid:central
  • Swedish BankID:
    • Same device: urn:grn:authn:se:bankid:same-device
    • Another device (aka mobile): urn:grn:authn:se:bankid:another-device
  • Danish NemID:
    • Personal with code card: urn:grn:authn:dk:nemid:poces
    • Employee with code card: urn:grn:authn:dk:nemid:moces
    • Employee with code file: urn:grn:authn:dk:nemid:moces:codefile

With this we’ve set up most of the infrastructure and now just need to make our endpoints do their thing.

A note on signature validation

If you want to verify the signature of the id_token you get back from Criipto Verify, add this function, and call it as shown in the comment in the above code block.

function verifySignature(id_token, done) {
  // no error checking: push on or fail miserably
  const jwksClient = jwksRsa({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: 'https://' + process.env.DOMAIN + '/.well-known/jwks'
  });
  const jwt_header = JSON.parse(new Buffer(id_token.split('.')[0], 'base64').toString());
  jwksClient.getSigningKey(jwt_header.kid, (err, key) => {
    var signingKey = key.publicKey || key.rsaPublicKey;
    jsonwebtoken.verify(id_token, signingKey, function (err, userInfo) {
      done(err, userInfo);
    });
  });
}

The views: Start the login and show the user info

First add a login link to the front page. Put this, or something to the same effect, in the page:

<!-- in views/index.ejs -->
<!-- ... -->
<a href="/login">Click here to log in!</a>
<!-- ... -->l

That will get the login process started. Next we need to display a protected page upon successful autthentication.

Add a new file, users.ejs to the views directory

<!DOCTYPE html>
<!-- in views/users.ejs -->
<html>
  <head>
    <title>Successful authentication</title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1>Welcome <em><%= name %></em></h1>
    <p>You signed in with a token for this audience: <em><%= aud %></em></p>
  </body>
</html>

Routing to login and back

With the views set up, we route requests to the right endpoints.

First the login request is routed.

// in routes/index.js

// ...
var passport = require('passport');
// ...

// initiate the login process
router.get('/login',
  passport.authenticate('openidconnect'), 
  function(req, res){
    res.render('login', { env: env });
  });
  
// complete the login process
router.get('/callback',
  passport.authenticate('openidconnect', { failureRedirect: '/url-if-something-fails' }),
  function(req, res) {
    res.redirect(req.session.returnTo || '/users'); // success!
  });

We route authenticated requests to the users view. Authentication is checked by the simple helper module, connect-ensure-login.

Change the routes/users.js to something like this:

// in routes/users.js
var express = require('express');
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn();
var router = express.Router();

router.get('/', ensureLoggedIn, function(req, res, next) {
  console.log(req.user);
  res.render('users', req.user);  // display user info
});

module.exports = router;

Running the application

As you would have noticed in the above code for app.js we rely on a set of environment variables to run the application:

# Feed in the necessary configuration parameters
DOMAIN=<your Criipto Verify domain> \
CLIENT_ID=<your client id> \
CLIENT_SECRET=<your client secret> \
CALLBACK_URL=http://localhost:3000/callback \
npm start

If you want to run the sample without your own Criipto Verify tenant, we’ve set up test tenant in Criipto Verify for you to use. Just use this to run you local version.

# Feed in the necessary configuration parameters
DOMAIN=acme-corp.grean.id \
CLIENT_ID=urn:easyid:login-demo \
CLIENT_SECRET=LHla2/sf5OqbtCZ/0In9pm5HP1qWmuffUwxnhfSPReU= \
CALLBACK_URL=http://localhost:3000/callback \
npm start

That’s it! View, download, and run this sample from GitHub


Go to criipto.com to sign up today to quickly enable your applications to accept real people’s real identities. We run a tight ship to ensure that you get the same level of security and safety that you get from the various proprietary bank and government solutions!