Notification based flows

Notification based flows are flows that rely on the push notification mechanism to notify the Smart-ID app. In these flows, the RP website or app shows a verification code (VC) which the user must compare to the VC displayed on Smart-ID to match the authentication or signature session.

To force the user to perform the verification, the RP can request the Smart-ID app to display three VCs (one correct code and two random codes) and the user is required to select the correct code which is displayed by the RP website or app. In case the user doesn’t choose the correct code, the Smart-ID app aborts the request.

This can be requested in the Smart-ID RP API call using the confirmationMessageAndVerificationCodeChoice interaction type. For details of the interactions object, see Interactions page.

NOTE

In case of same-device use cases where the recommended App2App or Web2App device link flow cannot be used for some reason, then in notification flows it is recommended to include a small delay after showing the verification code to user and before starting the transaction on the RP API. This way, the user has additional time to look at the verification code on the RP’s app before the push notification arrives.

Below, sequence diagrams are given along with explanations of the differences.

Flow diagrams

The following diagram describes the notification based flow for the authentication session.

flow

The following sequence diagram illustrates the notification based signing flow.

flow

The following sequence diagram illustrates the device link based certificate choice flow being linked to a notification based signing flow.

flow

Signature protocols

There are separate signing protocols for authentication and signature requests (see signature protocols for details).

Verification codes for authentication requests

In RP API v3 notification based authentication requests, RP must compute verification codes (VC) for each authentication request, so the user can bind together the session on the browser or RP app and the authentication request on the Smart-ID app.

The VC is computed as follows:

Calculate SHA-256 from the rpChallenge, extract 2 rightmost bytes from the result, interpret them as a big-endian unsigned integer and take the last 4 digits in decimal form for display. SHA-256 must be used to calculate the VC.

Please keep in mind that the rpChallenge is the real rpChallenge byte value (for example, the byte array returned from the SecureRandom() or equivalent method), not the Base64 encoded form used for transport or the popular hexadecimal representation.

The VC value must be displayed to the user in the browser together with a message asking the end user to verify the code matches with the one displayed on their mobile device. The user must not proceed if these don’t match.

integer(SHA-256(rpChallenge)[-2:-1]) mod 10000
  • Java

  • PHP

  • Python

  • Go

  • Rust

  • Kotlin

  • C#

  • Node.js

  • Ruby

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;

public class NotificationVerificationCode {

    public static void main(String[] args) throws Exception {

        String rpChallengeBase64 = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==";
        byte[] rpChallengeBytes = Base64.getDecoder().decode(
            rpChallengeBase64
        );

        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(rpChallengeBytes);
        byte[] sha256Hash = md.digest();

        BigInteger LAST_2_BYTES_BITMASK = new BigInteger("65535");

        BigInteger result = new BigInteger(sha256Hash)
            .and(LAST_2_BYTES_BITMASK)
            .mod(new BigInteger("10000"));

        String verificationCode = String.format("%04d", result);

        System.out.println(verificationCode);
    }
}
<?php

$rpChallengeBase64 = base64_decode('GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==');
$sha256Hash = hash('sha256', $rpChallengeBase64, true);
$result = hexdec(bin2hex(substr($sha256Hash, -2))) % 10000;
$verificationCode = sprintf('%04d', $result);
echo $verificationCode;

?>
import base64
import hashlib

rp_challenge_base64 = b"GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=="
rp_challenge_bytes = base64.b64decode(rp_challenge_base64)
sha256_hash = hashlib.sha256(rp_challenge_bytes).digest()

result = int.from_bytes(sha256_hash[-2:], byteorder='big') % 10000
verification_code = f"{result:04d}"
print(verification_code)
package main

import (
	"crypto/sha256"
	"encoding/base64"
	"fmt"
)

func main() {
	var rpChallengeBase64 string = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=="
	var rpChallengeBytes, err = base64.StdEncoding.DecodeString(rpChallengeBase64)
	if err != nil {
		panic(err)
	}

	var sha256Hash [32]byte = sha256.Sum256(rpChallengeBytes)
	var result int = int(sha256Hash[len(sha256Hash)-2])<<8 + int(sha256Hash[len(sha256Hash)-1])
	result %= 10000

	var verificationCode string = fmt.Sprintf("%04d", result)
	fmt.Printf("%s\n",verificationCode)
}
use base64::decode;
use sha2::{Sha256, Digest};

fn main() {
    let rp_challenge_base64: &str = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==";
    let rp_challenge_bytes = decode(rp_challenge_base64).unwrap();
    let sha256_hash = Sha256::digest(&rp_challenge_bytes);
    let result = (((sha256_hash[30] as u16) << 8) + (sha256_hash[31] as u16)) % 10000;
    let verification_code: String = format!("{:04}", result);
    println!("{}", verification_code);
}
import java.math.BigInteger
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.Base64

fun main() {
    val rpChallengeBase64: String = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=="
    val rpChallengeBytes: ByteArray = Base64.getDecoder().decode(rpChallengeBase64)

    val sha256Hash: ByteArray = MessageDigest
        .getInstance("SHA-256")
        .digest(rpChallengeBytes)

    val LAST_2_BYTES_BITMASK: BigInteger = BigInteger("65535")

    val result: BigInteger = BigInteger(sha256Hash)
        .and(LAST_2_BYTES_BITMASK)
        .mod(BigInteger("10000"))

    val verificationCode = "%04d".format(result)

    println(verificationCode)
}
using System;
using System.Security.Cryptography;

public class NotificationVerificationCode
{
    public static void Main(string[] args)
    {
        string rpChallengeBase64 = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==";
        byte[] rpChallengeBytes = Convert.FromBase64String(rpChallengeBase64);

        using (SHA256 sha256 = SHA256.Create())
        {
            byte[] sha256Hash = sha256.ComputeHash(rpChallengeBytes);
            int result = ((sha256Hash[30] << 8) + (sha256Hash[31])) % 10000;
            string verificationCode = result.ToString("D4");
            Console.WriteLine(verificationCode);
        }
    }
}
const crypto = require('crypto');

const rpChallenge = Buffer.from('GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg==', 'base64');

const sha256 = crypto.createHash('sha256');
sha256.update(rpChallenge);
const sha256Hash = sha256.digest();

const result = sha256Hash.readUInt16BE(30) % 10000
  .toString()
  .padStart(4, '0');

console.log(result);
require 'base64'
require 'digest'

rp_challenge = "GYS+yoah6emAcVDNIajwSs6UB/M95XrDxMzXBUkwQJ9YFDipXXzGpPc7raWcuc2+TEoRc7WvIZ/7dU/iRXenYg=="
sha256_hash = Digest::SHA256.digest(Base64.decode64(rp_challenge))

last_2_bytes = sha256_hash[-2..-1].unpack('n*').first

result = (last_2_bytes % 10_000).to_s.rjust(4, '0')

puts result
Example 1. Verification code
7180

Verification codes for signature requests

In RP API v3 notification based signature requests, verification codes (VC) are not generated by RP based on the DTBS (data-to-be-signed), but are returned from the server to RP for security reasons. Verification codes are only relevant for the notification based flows.

Currently, there is only one supported verification code type:

  • numeric4

The returned verification code consists of 4 numbers. It must be shown to the user as it is received from the server.