Skip to main content
The Signicat Blog
Thomas Martinussen

Full Stack Development Intern @ Signicat

Getting started with our Authentication REST API and Norwegian Bank ID

Say you have a business dedicated to matching Norwegian borrowers and lenders. You want to make sure that both parties are who are they say they are, and you decide on verifying this using the Norwegian Bank ID. This could be either in the onboarding flow, or later on to authenticate already registered users.

This is how you would do it using Signicats Rest API.

In this example we will be using Typescript along with the Signicat Rest API. If you prefer to use Open ID Connect, you can check out the guide for that here.

Prerequisites:

Step 1: Sign up for the dashboard at Signicat Dashboard

Step 2: Create an organisation

Step 3: Create a new sandbox account (take note of your account ID)

An account should be Person scoped

You find the ID under the account name after creation

Step 4: Add a domain

Step 5: Create a new API-Client (take note of your client ID)

The client ID is found under the client name after creation

Step 6: Add a secret to your API-Client (take note of your client secret)

Step 7: Under “permissions”, enable “Authentication REST API”

You find the permissions tab under the api client name

Step 8: Enable Norwegian Bank ID

Click add new, and select Norwegian Bank ID. Afterwards it should look like this.

Now you’re all done in the dashboard and can move on to the actual code.

Server Application:

For our server application we will use Node.js with Typescript and Express. If you are unfamiliar with this or want to read more about it you can find more info here: https://nodejs.dev/en/learn/no....

We want our users to verify their identity in order to increase trust on our hypothetical borrower/lender platform. We can use the Signicat REST API for this.

For our example you need two files, index.ts and signicatClient.ts. You will also need to install some dependencies:

      npm install axios dotenv express nodemon ts-node typescript @types/axios @types/express
    

Here is a starting point for you to use our REST API. It’s a basic client for authenticating yourself with our API, along with methods for letting your end users authenticate themselves.

signicatClient.ts
      // signicatClient.ts

import axios from "axios";

export class signicatClient {
  private clientId: string;
  private clientSecret: string;
  private accountId: string;
  private baseUrl = "https://api.signicat.com";
  private token_endpoint = "/auth/open/connect/token";
  private session_endpoint = "/auth/rest/sessions";

  constructor(clientId: string, clientSecret: string, accountId: string) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.accountId = accountId;
  }

  private getBearerToken(): Promise<string | false> {
    // Requests Signicat's servers for a Bearer token to use later
    // Signicat expects a base64 encoded string composed of our Client ID and Client Secret as authentication
    const basicAuth = Buffer.from(
      `${this.clientId}:${this.clientSecret}`,
      "binary"
    ).toString("base64");

    // Make post request using Axios
    const res = axios
      .post(
        `${this.baseUrl}${this.token_endpoint}`,
        new URLSearchParams({
          grant_type: "client_credentials",
          scope: "signicat-api",
        }),
        {
          headers: {
            Authorization: `Basic ${basicAuth}`,
          },
        }
      )
      .then((tokenResponse) => {
        // Bearer token is on the 'access_token' property
        // expires_in can be changed in the dashboard
        console.log(tokenResponse.data);
        const bearerToken = tokenResponse.data.access_token;
        if (!bearerToken || typeof bearerToken !== "string") {
          console.log("Error while getting Bearer token");
          return false;
        }
        // Can now use the token for further requests to the API
        return bearerToken;
      })
      .catch((error): false => {
        console.log(error);
        console.log("Error while getting Bearer token");
        return false;
      });
    return res;
  }
  async getAuthSession(sessionId: string) {
    const bearerToken = await this.getBearerToken();
    if (!bearerToken) {
      return false;
    }
    // Get status for one given session ID, server returns 404 if session is expired (deleted)
    const res = await axios
      .get(`${this.baseUrl}${this.session_endpoint}/${sessionId}`, {
        headers: {
          Authorization: `Bearer ${bearerToken}`,
        },
      })
      .then((authResponse) => {
        // User has a valid authentication session
        console.log(authResponse.data);
        return authResponse.data;
      })
      .catch((error) => {
        // No authentication session found / other error
        console.log(error.response.data);
        return false;
      });
    return res;
  }

  async initializeAuthSession(body: any) {
    // Starts the authentication flow

    const bearerToken = await this.getBearerToken();
    if (!bearerToken) {
      return false;
    }
    // Create new session, in return we get a url for our user to authenticate
    const authResponse = await axios
      .post(`${this.baseUrl}${this.session_endpoint}`, body, {
        headers: {
          Authorization: `Bearer ${bearerToken}`,
        },
        params: {
          "signicat-accountId": this.accountId,
        },
      })
      .then((response) => {
        console.log(response.data);
        return response.data;
      })
      .catch((error) => {
        console.log(error);
        return false;
      });
    return authResponse;
  }
}
    

Now for our express app. We want:

  • A route to initialialize our end users' authentication.
  • Routes to handle successful/aborted/erroneous authentication.
  • A function to mark users as verified in our database ( why we’re doing all this ).

That should look something like this:

index.ts
      // index.ts 

import express from "express";
import * as dotenv from "dotenv";
import { signicatClient } from "./signicatClient";

dotenv.config();

const app = express();
const port = process.env.port;
const clientId = process.env.clientId ?? "";
const clientSecret = process.env.clientSecret ?? "";
const accountId = process.env.accountId ?? "";

const body = {
  prefilledInput: {
    nin: "01100844350", // national identification number for testing
  },
  callbackUrls: {
    error: "http://localhost:3000/error",
    abort: "http://localhost:3000/abort",
    success: "http://localhost:3000/success",
  },
  flow: "redirect",
  requestedAttributes: ["firstName", "lastName", "dateOfBirth"], // properties you want
  allowedProviders: ["nbid"],
};

const client = new signicatClient(clientId, clientSecret, accountId);

function updateUserInfoAndMarkAsVerified(userInfo: any) {
  console.log("Updating userinfo in database: ");
  console.log(userInfo);
  console.log("Marking as verified");
  console.log("Done!");
}

app.get("/", async (req, res) => {
  res.send("Home");
});

app.get("/startauth", async (req, res) => {
  // to be filled in
});

app.get("/success", async (req, res) => {
  // to be filled in
});

app.get("/abort", async (req, res) => {
  res.send("abort");
});
app.get("/error", async (req, res) => {
  res.send("error");
});

app.listen(port, async () => {
  console.log(`⚡️[server]: Server is running at http://localhost:${port}`);
  console.log(`️Verify yourself at at http://localhost:${port}/startauth`);
});
    

You can store the client ID and secret however you best see fit, I am using a .env file for this example.

.env
      // .env

accountId=a-aaaa-aaaaaaaaaaaaaaaa
clientId=aaaaaaa-aaaaaa-aaaaaa-111
clientSecret=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
port=3000
    

There are only really two routes that we need to fill in. One for starting the authentication session, and another for handling a successfull verification. Lets start with the start authentication route:

index.ts
      app.get("/startauth", async (req, res) => {
  const authResponse = await client.initializeAuthSession(body); // Start authentication
  if (!authResponse) {
    res.status(500).send("Internal server error"); // either no bearer token or signicat threw error
  }
  res.redirect(authResponse.authenticationUrl);
});
    

The body object that is passed to the signicatClient looks like this and will change depending on your needs. For a fully detailed description of what we expect to recieve along with what you can expect from us, see the documentation for the Authentication API here:  Authentication API | Developer Pages

index.ts
      const body = {
  prefilledInput: {
    nin: "01100844350", // national identification number for testing
  },
  callbackUrls: {
    error: "http://localhost:3000/error",
    abort: "http://localhost:3000/abort",
    success: "http://localhost:3000/success",
  },
  flow: "redirect",
  requestedAttributes: ["firstName", "lastName", "dateOfBirth"],
  allowedProviders: ["nbid"],
};
    

Now let us create the success route. We check if we get a sessionId, then try to get the session from Signicat. If the session is a successful verification, we store the data in our database.

index.ts
      app.get("/success", async (req, res) => {
  const sessionId = req.query.sessionId;
  if (!sessionId || typeof sessionId !== "string") {
    res.status(400).send("No SessionId in request");
    return;
  }
  const authResponse = await client.getAuthSession(sessionId);
  if (!authResponse) {
    res.status(400).send("Could not validate session");
  }
  if (authResponse.status === "SUCCESS") {
    updateUserInfoAndMarkAsVerified(authResponse.subject);
  }
  res.redirect("/");
});
    

You should now be able to run the app and log in using our test credentials (see below):

shell
      nodemon index.ts
    

NOTE: For testing you can use our Sign-in test credentials:
Choose Norwegian BankID and sign with a test user as follows:

National identity number: 01100844350

One time password: otp

Password: qwer1234

Then, after verifying we can see that the updateUserInfoAndMarkAsVerified function is called :

Wrapping up

You now have validated user info to use in your application. Whether you use this to create a checkmark next to profile pictures, or require it for your own sake, you can now do so. You’d want to fill in the “/error“ and “/abort“ routes and handle them how you see fit. Next you’d want to change the prefilledInput on the body object with the National Identification Number of the person identifying if you already have it from your frontend, or omit it entirely.

Finally, you also need to implement the updateUserInfoAndMarkAsVerified function to actually store the data coming in. 

Happy coding!