In one of the mobile app project in Centralsoft we had the need and the request to implement the authentication using the Bank ID service. Today we will talk and describe our journey to the success of BankID integration.

BankID is by far the largest electronic identification system in Sweden, with a usage rate of 94% among smartphone users, and is administered by Finansiell ID-Teknik BID AB that is owned by several Swedish and Scandinavian banks. Individuals who have a Swedish personnummer (Swedish national identification number) can obtain Swedish BankID through their bank. A BankID has the same value and is used the same way, regardless of the bank that issued it.

Swedish national identification number?

Permalink to "Swedish national identification number?"

The personal identity number (personnnummer) is the Swedish national identification number. It is a ten or twelve-digit number that is widely used in Sweden to identify individuals. It is constructed from some personal parameters which give this number some special information about your identity.

swedish-national-identification-number

BankID has a public API which we can consume in two different environments:

  • Test
  • Production

When developing and testing you should use the test environment. To be able to use the test environment you will need:

  1. An SSL certificate for identification with the BankID web service API. https://www.bankid.com/en/utvecklare/test Passphrase for this certificate: qwerty123

  2. The URL for BankID’s web service API. https://appapi2.test.bankid.com/rp/v5.1

  3. Trust the issuer of the SSL certificate.

  4. A test version of the BankID app

  5. Download cert bundles: https://ufile.io/fcj4eykh

    Android: https://play.google.com/store/apps/details?id=com.bankid.bus&hl=en&gl=US iOS: https://apps.apple.com/se/app/bankid-säkerhetsapp/id433151512 Even after installing the application you need to perform some other configurations for the BankID application to work in the test environment

    iOS:

    Android:

    • Put phone to Airplane Mode
    • Open the BankID App and Settings in the BankID app and select About BankID
    • Make a "long press" (ie press and hold) on the heading Error information .
    • Enter kundtest in the box that appears and press OK
    • Without leaving About BankID, check that it says CUST after version number. This verifies that you entered correctly.

Note:\

  1. App must be uninstalled / reinstalled to be restored to the production environment. \
  2. The BankID app cannot be run in emulated environments. (simulators won’t work)
  1. A BankID for test.

    Go to www.demo.bankid.com You’ll see a section “Login with a personal code”

Fill the required fields. As for email please be aware that Gmail, Outlook providers might not work so in that case use your company email (example: info@centralsoft.io)

Check the email and get the code

Go back again to www.demo.bankid.com use your code and login.

Let’s issue BankID for test

You will need to fill details and also a swedish personal number. To generate one we can use this online generator: https://tedeh.net/tools/generator-for-swedish-personnummer/ You should delete the dash (-) from the generated number.

Then open BankID issuing

A new window will open with an animated QR-code which we will scan through the mobile bankID app

Let’s open the mobile app and let’s press the QR code button and scan the QR code. After that we will need to set a security code (imagine this as a bankID password). Finally we will have the account completed and we can start using it.

Development

For the backend part we will use NodeJS.

Let’s create a service that handles all the needs for the BankID.

We have to configure a request agent to use the SSL client certificate provided by BankID. This can happen easily using Axios package.

npm install axios
npm i await-to-js
const axiosLib = require('axios');
const fs = require('fs');
const https = require('https');
const to = require('await-to-js').to;

const config = {
    mobileBankIdPolicy: '1.2.3.4.25',
    bankdIdUrl: 'https://appapi2.test.bankid.com/rp/v5',
    pfx: fs.readFileSync('./certs/FPTestcert2_20150818_102329.pfx'),
    passphrase: 'qwerty123',
    ca: fs.readFileSync(`./certs/test.ca`),
};

// Agent for using SSL client certificate
const axios = axiosLib.create({
  httpsAgent: new https.Agent({
    pfx: config.pfx,
    passphrase: config.passphrase,
    ca: config.ca,
  }),
  headers: {
    'Content-Type': 'application/json',
  },
});

After setting up the agent we’re ready to start making BankID API calls but also we should remember some bullet points:

  • All calls are made using HTTP POST method
  • We must include header: Content-Type: application/json
  • All of the parameters should be sent as JSON in req.body

After setting up the agent we’re ready to start making BankID API calls but also we should remember some bullet points:

  • All calls are made using HTTP POST method
  • We must include header: Content-Type: application/json
  • All of the parameters should be sent as JSON in req.body

Setting up a wrapper for API calls

Permalink to "Setting up a wrapper for API calls"
async function call (method, params) {
  const [error, result] = await to(
    axios.post(`${config.bankdIdUrl}/${method}`, params));

  if (error) {
    // You will want to implement your own error handling here
    console.error('Error in call');
    if (error.response && error.response.data) {
      console.error(error.response.data);
      if (error.response.data.errorCode === 'alreadyInProgress') {
        console.error('You would have had to call cancel on this orderRef before retrying');
        console.error('The order should now have been automatically cancelled by this premature retry');
      }
    }
    return {error};
  }

  return result.data;
}

For error handling please refer to this official BankID api documentation (section 6)

https://www.bankid.com/assets/bankid/rp/BankID-Relying-Party-Guidelines-v3.7.pdf

To initiate authentication, call the auth  endpoint (for example https://appapi2.test.bankid.com/rp/v5/auth ) with the following parameters

Making this call will trigger the authorization flow in the users BankID app.

const auth = async (endUserIp: any) =>
  await call('auth', {
    endUserIp,
    requirement: {
      allowFingerprint: true,
    },
  })

Response: (we will need the orderRef and autoStartToken)

{
"orderRef": "3efa7e6a-6ee6-426b-b4be-e18f24b4738c",
"autoStartToken": "3575439f-b003-470f-b5c0-fa9c00650e17",
"qrStartToken": "08f70922-8428-4335-81fe-643f4a96ec49",
"qrStartSecret": "9f413434-4ad7-43d5-8c51-5acc8c597f14"
}

autoStartToken be used to construct a url that can be launched on the users device and picked up by the BankID app, if it is installed.

iOS: https://app.bankid.com/?autostarttoken=[AUTOSTART_TOKEN_HERE]&redirect=null Other devices: bankid:///?autostarttoken=[AUTOSTART_TOKEN_HERE]&redirect=null

The brackets around the autoStartToken should be included.

BankID polling for results (ongoing order)

Permalink to "BankID polling for results (ongoing order)"

You will need to poll the collect method to retrieve the status of an ongoing operation.

As refering to the BankID official documentation, it states that we should call collect every two seconds but specifically not more often than once per second

The collectmethod takes a single parameter – orderRef – and returns two out of three values: statushintCode and completionData.

const collect = async (orderRef: any) => await call('collect', { orderRef })

const startPolling = async (orderRef: any) => {
  try {
    while (true) {
    const {status, hintCode, completionData} = await collect(orderRef);
    if (status === 'failed') {
      return {ok: false, status: hintCode};
    } else if (status === 'complete') {
      return {ok: true, status: completionData};
    } else {
      console.log(hintCode);
    }
    await new Promise(resolve => setTimeout(resolve, 2000));
  }
  } catch (err) {
    return err
  }
}