Interactions

Smart-ID app can support different interaction types and a Relying Party (RP) can demand a particular interaction with or without a fallback possibility. Different interactions can support different amount of data to display information to user.

Each interaction is defined by an object that has 2 parameters: type and either displayText60 or displayText200.

The following table shows interaction types available for each flow type. Linked notification based flow refers to the BASE/v3/signature/notification/linked endpoint.

Interaction type & extra parameter Description Device link based flows Notification based flows Linked notification based flow

displayTextAndPIN with displayText60

The simplest interaction with max 60 chars of text and PIN entry on a single screen.

Yes

Yes

Yes

confirmationMessage with displayText200

First screen is for text only (max 200 chars) and has Confirm and Cancel buttons. Second screen is for PIN.

Yes

Yes

Yes

confirmationMessageAndVerificationCodeChoice with displayText200

First screen combines text and Verification Code choice. Second screen is for PIN.

No

Yes

No

All three interactions types are currently supported by all the clients.

Requirements for interactions object

RP uses interactions parameter to list interactions it allows for the current session.

New interaction types may be introduced in the future. However, it takes some time until all apps have been updated and are actually capable of displaying the new types.

After introduction of a new interaction type both the new interaction type as a well as an older type should be provided in the array for clients that have not been yet updated.

The interactions parameter is a Base64 encoded JSON array and the order of the items is significant. Each interaction in the array is a JSON object consisting of the type, and either displayText60 or displayText200. The first interaction that is supported by a specific app installation will be used.

The interaction that was used is always reported back to RP with interactionTypeUsed response parameter to the session request.

If an app cannot support any interaction requested then the session is cancelled with reason code REQUIRED_INTERACTION_NOT_SUPPORTED_BY_APP.

IMPORTANT

The following requirements apply for the interaction object:

  1. For each interaction, the type and the corresponding displayText (either displayText60 or displayText200) parameters are mandatory. Meaning that each interaction must include a non-empty displayText60 or displayText200 parameter.

  2. It is forbidden to provide both displayText60 and displayText200 parameters.

  3. It is not allowed to have more than one interaction with the same type value.

  4. In addition, it is not allowed to list interaction types not meant for the flow (device link flow or notification based flow).

  5. JSON to Base64 encoding is non-deterministic (parameters may be ordered randomly, whitespace can be optional). Therefore, the specific Base64-encoded string generated from the interactions object to initiate the session must be stored and reused in all subsequent calculations. This is relevant for all device link flows, more specifically in:

Failing to meet the first 4 requirements above will result in an error (HTTP 400 Bad Request). Failing to follow the last recommendation can result in some dynamic link sessions failing. If authCode calculation returns a different value from what RP API calculates, then the device link will be rejected due to wrong authCode value. If ACSP_V2 digest is calculated incorrectly, a valid user authentication signature will appear to be invalid and RP will have to deny the authentication attempt.

Guidance for writing display texts

displayText is a short text string displayed to the user in the Smart-ID authentication or digital signature consent screen. It helps the user understand the context of the operation. displayText will be displayed on the authentication or signing consent screen. The RP can use this to distinguish between the different information systems or different services within the RP, when requesting the authentication. When requesting a digital signature, the RP can use this to give information about the nature of signature, such as the name of the document to be signed or information about the transfer order to be executed.

For example, in case the RP is using Smart-ID to authenticate users calling the RP’s helpdesk via phone, these authentication requests must use a different displayText than requests sent by the RP’s website. This way the user has greater confidence that the person on the phone (a helpdesk agent who might be out-sourced from an external company) is not logging into the website of the RP under the name of the user.

displayText value can only be set by the RP backend and phishing attacks can’t change it. This is why, it is recommended to use the displayText to alert users and show warnings if an attack is suspected. For more details, see Additional security measures page.

The displayText60 and displayText200 parameters are used by the Smart-ID app to display text on the screen. The preferred way of writing displayText60 and displayText200 is to explain the action user is taking which the user is expected to confirm.

The descriptions should be concise and specific allowing the user to verify the context of the operation quickly. A few examples:

  • Logging in to internet banking

  • Log in to mobile banking app

  • Applying for a credit card application, 1000€ credit limit

  • Signing document Contract.asice

  • Transfer 1000€ to Jane Doe GB33BUKB20201555555555

Usually, the Relying Party name does not need to be part of the display text as the RP name parameter is always displayed to the user along with the display text.

Base64 encoding interactions

Before sending the RP API request, interactions array must be turned into a string and then Base64 encoded. The interactions JSON should be minified to lower the overhead. The Base64 encoding can be done as follows:

interactions := BASE64-ENCODE(TO-STRING(MINIFY(interactionsJson)))
  • Java

  • PHP

  • Python

  • Go

  • Rust

  • Kotlin

  • C#

  • Node.js

  • Ruby

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class InteractionsBase64Encode {

  public static void main(String[] args) {
    String interactions = """
[{"type":"confirmationMessage","displayText200":"Longer description of the transaction context"},{"type":"displayTextAndPIN","displayText60":"Short description of the transaction context"}]""";

    String interactionsBase64 = Base64.getEncoder().encodeToString(
      interactions.getBytes(StandardCharsets.UTF_8)
    );

    System.out.println(interactionsBase64);
  }
}
<?php
$interactionsJsonStr = <<<EOD
[{"type":"confirmationMessage","displayText200":"Longer description of the transaction context"},{"type":"displayTextAndPIN","displayText60":"Short description of the transaction context"}]
EOD;

$interactionsBase64 = base64_encode($interactionsJsonStr);
echo $interactionsBase64;

?>
import base64
import json

interactions = [{"type":"confirmationMessage","displayText200":"Longer description of the transaction context"},{"type":"displayTextAndPIN","displayText60":"Short description of the transaction context"}]
interactions_string = json.dumps(interactions, separators=(',', ':'))
interactions_base64 = base64.b64encode(interactions_string.encode('utf-8'))
print(interactions_base64)
package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
)

type Interactions struct {
	Type           string `json:"type"`
	DisplayText200 string `json:"displayText200,omitempty"`
	DisplayText60  string `json:"displayText60,omitempty"`
}

func main() {
	var interactions []Interactions = []Interactions{{Type: "confirmationMessage", DisplayText200: "Longer description of the transaction context"}, {Type: "displayTextAndPIN", DisplayText60: "Short description of the transaction context"}}
	interactionsBytes, err := json.Marshal(interactions)
	if err != nil {
		fmt.Printf("error: %s\n", err)
		return
	}
	var interactionsBase64 []byte = []byte(base64.StdEncoding.EncodeToString(interactionsBytes))
	fmt.Printf("%q\n", interactionsBase64)
}
use base64::Engine as _;
use serde::{Deserialize, Serialize};

type Interactions<'a> = Vec<Interaction<'a>>;

#[derive(Debug, Deserialize, Serialize)]
enum DisplayText<'a> {
    #[serde(rename = "displayText200")]
    DisplayText200(&'a str),
    #[serde(rename = "displayText60")]
    DisplayText60(&'a str),
}

#[derive(Serialize, Deserialize)]
struct Interaction<'a> {
    #[serde(rename = "type")]
    type_field: &'a str,
    #[serde(flatten)]
    display_text: DisplayText<'a>,
}

fn main() {
    let interactions: Interactions = vec![Interaction{type_field: "confirmationMessage", display_text: DisplayText::DisplayText200("Longer description of the transaction context")}, Interaction{type_field: "displayTextAndPIN", display_text: DisplayText::DisplayText60("Short description of the transaction context")}];

    let interactions_base64: String = base64::engine::general_purpose::STANDARD.encode(serde_json::to_string(&interactions).unwrap().into_bytes());
    println!("{:?}", interactions_base64);
}
import java.nio.charset.StandardCharsets
import java.util.Base64

fun main() {
    val interactions: String = """
[{"type":"confirmationMessage","displayText200":"Longer description of the transaction context"},{"type":"displayTextAndPIN","displayText60":"Short description of the transaction context"}]""".trimIndent()

    val interactionsBase64: String = Base64.getEncoder().encodeToString(
        interactions.toByteArray(StandardCharsets.UTF_8)
    )

    println(interactionsBase64)
}
using System;
using System.Text;

public class InteractionsBase64Encode
{
    public static void Main(string[] args)
    {
        String interactionsJson = """[{"type":"confirmationMessage","displayText200":"Longer description of the transaction context"},{"type":"displayTextAndPIN","displayText60":"Short description of the transaction context"}]""";
        String interactionsBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(interactionsJson));
        Console.WriteLine(interactionsBase64);
    }
}
const interactions = [{"type":"confirmationMessage","displayText200":"Longer description of the transaction context"},{"type":"displayTextAndPIN","displayText60":"Short description of the transaction context"}];
const interactionsString = JSON.stringify(interactions);
const interactionsBase64 = Buffer.from(interactionsString).toString('base64');
console.log(interactions_base64);
require 'base64'
require 'json'

interactions = [{"type":"confirmationMessage","displayText200":"Longer description of the transaction context"},{"type":"displayTextAndPIN","displayText60":"Short description of the transaction context"}]
interactions_string = JSON.generate(interactions)
interactions_base64 = Base64.strict_encode64(interactions_string)
puts interactions_base64
Example 1. interactions parameter
W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgdHJhbnNhY3Rpb24gY29udGV4dCJ9LHsidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IlNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0In1d

interactions elements examples

Example 2. confirmationMessage only, if not available then fail.
Before encoding
[
  {
    "type": "confirmationMessage",
    "displayText200": "Logging in to internet banking"
  }
]
Base64 encoded
{
  "interactions": "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkxvZ2dpbmcgaW4gdG8gaW50ZXJuZXQgYmFua2luZyJ9XQ=="
}
App visualization - confirmation message (device link flow)
example 2 devicelink 1
Initial screen
example 2 devicelink 2
PIN entry screen
App visualization - confirmation message (notification based flow)
example 2 notification 1
Initial screen
example 2 notification 2
PIN entry screen
Watch visualization - confirmation message
example 2 watch 1
Initial screen (scrolls down)
example 2 watch 2
PIN entry screen
Example 3. displayTextAndPIN only, if not available then fail.
Before encoding
[
  {
    "type": "displayTextAndPIN",
    "displayText60": "Log in to mobile banking app"
  }
]
Base64 encoded
{
  "interactions": "W3sidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IkxvZyBpbiB0byBtb2JpbGUgYmFua2luZyBhcHAifV0="
}
App visualization - display text and PIN (device link flow)
example 3 devicelink
Initial screen
App visualization - display text and PIN (notification based flow)
example 3 notification
Initial screen
Watch visualization - display text and PIN
example 3 watch 1
Initial screen (scrolls down)
example 3 watch 2
PIN entry screen
Example 4. RP first choice is confirmationMessage, if not available then fall back to displayTextAndPIN.
Before encoding
[
  {
    "type": "confirmationMessage",
    "displayText200": "Applying for mortgage, 100 000€, 20-year period, interest rate 2.5% + 6 month Euribor rate"
  },
  {
    "type": "displayTextAndPIN",
    "displayText60": "Applying for mortgage, 100 000€"
  }
]
Base64 encoded
{
  "interactions": "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2UiLCJkaXNwbGF5VGV4dDIwMCI6IkFwcGx5aW5nIGZvciBtb3J0Z2FnZSwgMTAwIDAwMFx1MjBhYywgMjAteWVhciBwZXJpb2QsIGludGVyZXN0IHJhdGUgMi41JSArIDYgbW9udGggRXVyaWJvciByYXRlIn0seyJ0eXBlIjoiZGlzcGxheVRleHRBbmRQSU4iLCJkaXNwbGF5VGV4dDYwIjoiQXBwbHlpbmcgZm9yIG1vcnRnYWdlLCAxMDAgMDAwXHUyMGFjIn1d"
}
App visualization - confirmation message (device link flow)
example 4 devicelink 1
Initial screen
example 4 devicelink 2
PIN entry screen
App visualization - confirmation message (notification based flow)
example 4 notification 1
Initial screen
example 4 notification 2
PIN entry screen
Watch visualization - confirmation message
example 4 watch 1
Initial screen (scrolls down)
example 4 watch 2
PIN entry screen
Example 5. confirmationMessageAndVerificationCodeChoice only, if not available then fail (only notification based flow).
Before encoding
[
  {
    "type": "confirmationMessageAndVerificationCodeChoice",
    "displayText200": "Transfer 1000€ to Jane Doe GB33BUKB20201555555555"
  }
]
Base64 encoded
{
  "interactions": "W3sidHlwZSI6ImNvbmZpcm1hdGlvbk1lc3NhZ2VBbmRWZXJpZmljYXRpb25Db2RlQ2hvaWNlIiwiZGlzcGxheVRleHQyMDAiOiJUcmFuc2ZlciAxMDAwXHUyMGFjIHRvIEphbmUgRG9lIEdCMzNCVUtCMjAyMDE1NTU1NTU1NTUifV0="
}
App visualization - confirmation message and VC choice (only notification based flow)
example 5 notification 1
Initial screen
example 5 notification 2
PIN entry screen
Watch visualization - confirmation message and VC choice
example 5 watch 1
Initial screen (scrolls down)
example 5 watch 2
PIN entry screen