Node.js with Express

This is a sample application showing how to configure and enable OpenID Connect middleware to use with Criipto Verify in a Node.js web application with Express, openid-client and Passport.

Four steps are required to complete your first test login:

  1. Register your application in Criipto Verify
  2. Configure your OAuth2 flow
  3. Configure your application to use Criipto Verify
  4. Trigger authentication in your application

This explains how to set up your application and test with test users. To use real e-IDs for login the setup is the same, but you must be set up for Production

And note that you need test e-ID users to see your code in action. How to get those is described further down.

You may get a completed and ready to run sample from GitHub showing the below recipe in the simplest of Node.js Express applications.

To modify your existing application to work with Criipto Verify follow the steps below.

Register Your Application in Criipto Verify

After you signed up for Criipto Verify, you must register an application before you can actually try logging in with any e-ID.

Once you register your application you will also need some of the information for communicating with Criipto Verify. You get these details from the settings of the application in the dashboard.

Specifically you need the following information to configure you application

  • Client ID to identify you application to Criipto Verify. In the case below we chose urn:criipto:nodejs:demo:1010
  • Domain on which you will be communicating with Criipto Verify. Could be for example nodejs-sample.criipto.id

Register App

Register callback URLs

Before you can start sending authentication requests to Criipto Verify you need to register the URLs on which you want to receive the returned JSON Web Token, JWT.

The Callback URL of your application is the URL where Criipto Verify will redirect to after the user has authenticated in order for the OpenID Connect middleware to complete the authentication process.

You will need to add this URL to the list of allowed URLs for your application. The Callback URL for the sample project is http://localhost:3000/auth/callback and http://localhost:3000/logout/callback so be sure to add this to the Callback URLs section of your application. Put each URL on a new line.

Register App

If you deploy your application to a different URL you will also need to ensure to add that URL to the Callback URLs.

Configure the OAuth2 code flow

If you are creating a new application you must first save the configuration.

Once you have a saved application registration you are ready to configure the OAuth2 code flow. Open the application registration and configure it for the right OAuth2 flow:

  1. Enable OAuth2 code flow
  2. Copy the generated client secret.
  3. Set the user info response strategy to plainJson to enable retrieval of plain JSON user information from the /oauth2/userinfo endpoint.

OAuth2 code flow

Note that this is the only time you will be shown the actual value of the client secret. Criipto only stores this as a hashed value, which means you cannot retieve the value once it has been generated and stored.

OAuth2 code flow

Configure Your Application to Use Criipto Verify

Install dependencies

For this sample application you will be using Node.js with Express. If you are building a new Express application from scratch, consider using Express generator to quickly create an application skeleton.

On top of Express, you will need to install openid-client, a server side OpenID implementation for Node.js, and Passport, Express-compatible authentication middleware. Also make sure to install express-session and connect-ensure-login packages which provide a way to establish a user session and protect private routes.

npm install openid-client
npm install passport
npm install express-session
npm install connect-ensure-login

Configure openid-client

Got to app.js file where your Express app is initialized and import Issuer from openid-client.

const { Issuer } = require('openid-clien');

Call discover method of Issuer and pass it your Criipto domain as an argument. This will return a promise which will, after resolved, provide a Criipto Issuer instance. From Criipto Issuer instance you can create a new client by calling a Client constructor and passing in a settings object which must include following properties:

  • client_id: Criipto Client ID/Realm, for example urn:criipto:nodejs:demo:1010
  • client_secret: Generated in second step, for example j9wYVyD3zXZPMo3LTq/xSU/sMu9/shiFKpTHKfqAutM=
  • redirect_uris: An array of post-login redirect URIs, for example ["http://localhost:3000/auth/callback"]
  • post_logout_redirect_uris: An array of post-logout redirect URIs, for example ["http://localhost:3000/logout/callback"]
  • token_endpoint_auth_method: Requested authentication method for the token endpoint, for example client_secret_post
Issuer.discover('https://nodejs-sample.criipto.id')
  .then(criiptoIssuer => {
    const client = new criiptoIssuer.Client({
      client_id: 'urn:criipto:nodejs:demo:1010',
      client_secret: 'j9wYVyD3zXZPMo3LTq/xSU/sMu9/shiFKpTHKfqAutM=',
      redirect_uris: [ 'http://localhost:3000/auth/callback' ],
      post_logout_redirect_uris: [ 'http://localhost:3000/logout/callback' ],
      token_endpoint_auth_method: 'client_secret_post'
    });

    // do the rest of setup here
  });

Configure Passport and Express session

Next step is to initialize Passport (authentication middleware) and Express session. Start by importing required modules.

const expressSesssion = require('express-session');
const passport = require('passport');

It is important to set up an Express session before initializing Passport. Add the following code to your app.js file.

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

app.use(passport.initialize());
app.use(passport.session());

After you have initialized Passport, you have to tell it which strategy (authentication mechanisms) to use with your application. Openid-client provides a strategy that you can use with the client, so make sure to import it from openid-client.

const { Strategy } = require('openid-client');

When passing a strategy to the passport, you have to pass two arguments to the use method: the strategy name and initialized strategy object. To initialize the strategy imported from opeid-client, you have to call a constructor with two arguments: settings object containing at least the initialized client and a callback function to execute after you receive user information from Criipto Verify.

Add the following code to your app.js file.

passport.use(
  'oidc',
  new Strategy({ client }, (tokenSet, userinfo, done) => {
    return done(null, tokenSet.claims());
  })
);

The last thing you need to finish setting up the passport is to provide a function to serialize and deserialize a user. Add the following code to your app.js file.

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

Trigger authentication in your application

Choosing the specific login method

Criipto Verify supports a range of country and bank specific e-ID services. They are all accessed through the same endpoints, e.g. https://<YOUR COMPANY>.criipto.id/oauth2/authorize

To pick the login method you must set the AcrValues parameter on the authentication request in order to choose the type of authentication you want. How you set this query string parameter varies with programming platform and your OpenID Connect library of choice.

The current list of possible values is:

Norwegian BankID  
  Mobile or Web (user choice):  urn:grn:authn:no:bankid
Norwegian Vipps Login  
  Login with Vipps app:  urn:grn:authn:no:vipps
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
Finish e-ID  
  BankID: urn:grn:authn:fi:bankid
  Mobile certificate (Mobiilivarmenne):  urn:grn:authn:fi:mobile-id
  Any of the two: urn:grn:authn:fi:all

  

In the sample, we have created a form which will put the chosen login option in a request parameter and redirect a user to the authentication route specified.

<select class="form-control" name="loginmethod" form="loginForm">
  <option value="urn:grn:authn:no:bankid">Norwegian BankID</option>
  <option value="urn:grn:authn:no:vipps">Norwegian Vipps</option>
  <option value="urn:grn:authn:se:bankid:same-device">Swedish BankID same device</option>
  <option value="urn:grn:authn:se:bankid:another-device">Swedish BankID another device</option>
  <option value="urn:grn:authn:dk:nemid:poces">Danish NemID personal with code card</option>
  <option value="urn:grn:authn:dk:nemid:moces">Danish NemID employee with code card</option>
  <option value="urn:grn:authn:dk:nemid:moces:codefile">Danish NemID employee with code file</option>
  <option value="urn:grn:authn:fi:bankid">Finish e-ID - BankID</option>
  <option value="urn:grn:authn:fi:mobile-id">Finish e-ID - Mobile certificate</option>
  <option value="urn:grn:authn:fi:all">Finish all options</option>
</select>

<form action="/auth" id="loginForm">
  <input class="btn btn-outline-primary" type="submit" value="Login">
</form>

Start the authentication request

After you submit a form, you will be redirected to the /auth route. Now you have to set up an Express middleware for the /auth route where you will tell passport to start the authentication request and redirect the user to the chosen login option.

To do that, you have to invoke authenticate method and tell it which strategy to use. You also have to provide an object with acr_values and optionally some other additional parameters.

Add the following code to your app.js file.

app.get('/auth', (req, res, next) => {
  let loginMethod = req.query.loginmethod;
  passport.authenticate('oidc', { acr_values: loginMethod })(req, res, next);
});

This will redirect the user to the authentication URL. If you want to show the authentication page in an iframe, you can simply create an iframe with a name attribute and set the target of the login form to the name of the iframe. Just make sure that the iframe is generated in the DOM before executing the form.

<form action="/auth" id="loginForm" target="loginIframe">
  <input class="btn btn-outline-primary" type="submit" value="Login">
</form>

<iframe class="login-iframe" name="loginIframe"></iframe>

Set up authentication redirect route

After successful authentication with Criipto Verify, you will be redirected to the authentication redirect route which you provided when you initialized the openid-client. If you haven’t already, make sure that the route is registered in Callback URLs section in Criipto Verify. For this sample application, the redirect route is /auth/callback so you have to create an Express route which will handle the callback and authenticate the user in your application.

To do that, you simply have to call authenticate method of the passport and pass it the strategy name and an object containing following properties:

  • successRedirect: where to redirect if the authentication was successful
  • failureRedirect: where to redirect if the authentication was not successful
app.get('/auth/callback', (req, res, next) => {
  passport.authenticate('oidc', {
    successRedirect: '/success',
    failureRedirect: '/error'
  })(req, res, next);
});

If you are redirecting a user to the authentication URL, then you can set successRedirect directly to the private rout, for example /users, but if you are doing the authentication inside of an iframe, you will have to create an additional route, for example /success, which will load a JavaScript code to break out of an iframe and redirect the user on the client side. In the sample application, as soon as the /success route loads, we are calling a redirect on the top window.

window.top.location.replace('/users');

Protecting the private route

You can use connect-ensure-login middleware to protect the private route and redirect the user to the login route if they are not authenticated. To do that, first import ensureLoggedIn method from the connect-ensure-login middleware.

const ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;

Then you can simply use the method with Express Router for the protected route. You can specify where to redirect the user if they are not authenticated by passing the desired route as an argument of ensureLoggedIn method.

router.get('/', ensureLoggedIn('/'), (req, res) => {
  res.render('users', req.user);
});

Set up a user logout

The logout process consists of two parts: sending the request to Criipto Verify to end the session and clearing the user from the local storage.

First you will have to create a /logout route to send the request to Criipto Verify to end the session. You can retrieve the logout redirect URL from the client by calling endSessionUrl method.

app.get('/logout', (req, res) => {
  res.redirect(client.endSessionUrl());
});

When Criipto Verify successfuly ends the session, you will be redirected to the post-logout URL that you have provided when initializing the client.

Create a post-logout route where you have to invoke logout method to ensure that the passport clears the user from the local storage. Now the logout process is completed and you can redirect the user to the public route.

app.get('/logout/callback', (req, res) => {
  req.logout();
  res.redirect('/');
});

Test users

Almost all e-ID types have a notion of test users and real users.

Real users are real people logging in to a web site, thus voluntering their real name and typically also a social security number, SSN.

Test users are either created by you for the occasion, or we provide you with access to already created test users.

You may ready more in the section on test users

The runtime flow

In summary, the steps above will lead to a runtime flow that looks like this:

  1. The web server starts the application which configures and initializes the openid-client and Passport middleware. The openid-client is configured with a URL from which it retrieves the metadata describing the various endpoints and encryption keys, such as the token and userinfo endpoints as well the token signing certificates.
  2. The user picks the login method, or the application is hardcoded to one of the authentication options
  3. Posting the login form to the authentication route /auth will trigger the authentication flow.
  4. The user’s browser is redirected to the Criipto Verify service where actual login happens.
  5. A callback with an issued authorization code is sent back to the application and intercepted by the middleware.
  6. The middleware calls the Criipto Verify service to exchange the code for an access token. Note that this is a direct server to server call which - unlike the other communication - does not pass through the browser.
  7. The access token is used by the middleware to retrieve the available user information which is set as claims on the user principal.

If you want to inspect what is actually going on you may see much of it if you use for example Chrome and turn on the developer tools to inspect the network traffic.

Setting up for Production

Once you have integrated with Criipto Verify and tested that it works with test user accounts, you are ready to go to production to accept real e-ID logins and signatures.

Please note that for production usage a paid subscription is required.

Read more in the section on how to set up for production.