Device link flows
The device link flows are the primary flows to be used for initiating Smart-ID transactions. It consists of the following steps:
-
User interacts with an RP frontend (a webpage or an app).
-
RP backend makes a request to RP API to create a device link based session (
authentication
,signature
, orcertificate-choice
) to obtain security parameters that are necessary for generating device links. Forauthentication
andcertificate-choice
sessions, the request may either be anonymous (no personal identifier is provided) or non-anonymous (signature
session can’t be anonymous). -
RP starts creating device links. In case of QR codes, but not Web2App nor App2App links, the links must be updated each second. The device links must be generated in the RP backend and then forwarded to the RP frontend each time.
-
RP frontend displays QR codes / Web2App links / App2App links. The user interacts with one of them by either:
-
Opening Smart-ID app and scanning the dynamic QR code (preferred option for QR codes);
-
Using a native camera app to scan the dynamic QR code (not supported by all camera apps) which opens the Smart-ID app where user has to scan the QR code again.
-
Clicking on the Web2App link in an RP webpage, thus opening the Smart-ID app;
-
Clicking on the App2App link in an RP app, thus opening the Smart-ID app.
-
-
Smart-ID app extracts the necessary parameters and makes a request to the Smart-ID backend to check the integrity, validity and freshness of the device link. If the request was non-anonymous, it is verified that the provided personal identifier agrees with the actual user’s identity.
-
The user is asked to verify the transaction details and enter their PIN.
-
After the signature has been given, the RP receives the result via the session status endpoint. In case of anonymous flow, RP learns the identity of the user only at this point (via the user’s certificate).
-
User is redirected back to the RP with the given callback URL in same-device flows and RP performs the callback URL verification steps described in Callback URLs page.
IMPORTANT
|
The following diagram describes the device link based flow for the authentication
session.
The following diagram describes the device link based flow for the signature
session.
The following diagram describes the device link based flow for the certificateChoice
session.
Device link calculation
When RP backend makes a request to RP API to create a new device link based session, RP API server generates and returns the following parameters as a response:
-
sessionID
- session ID (RP UUID); -
sessionToken
- unique value that is used to connect this authentication/signing attempt between the relevant parties (RP, RP API, Smart-ID app); -
sessionSecret
- Base64 encoded key value that must be kept secret and must not be shared with any other party (including RP frontend); -
deviceLinkBase
- the base URL to be used to generate the QR/Web2App/App2App device links.
RP then calculates the Web2App or App2App device link and dynamic QR code and shows it to the end user. The dynamic QR code must be updated regularly with the interval of 1 second.
It is recommended that RP offers multiple options, QR image and Web2App link, to the user at the same time if either:
-
RP is unable to detect from the user agent whether the user is using a mobile device or a PC,
-
The user is accessing RP website from a mobile device, for example a tablet device, and might want to authenticate using a separate mobile device.
NOTE
The device link structure is similar for both same-device and cross-device flows. The differences between them are:
|
IMPORTANT
|
Same-device flows
The device link for App2App and Web2App is a URL:
deviceLink := STR-CONCAT(
deviceLinkBase, "?",
"deviceLinkType=", deviceLinkType,
"&sessionToken=", sessionToken,
"&sessionType=", sessionType,
"&version=", version,
"&lang=", lang,
"&authCode=", authCode)
-
Java
-
PHP
-
Python
-
Go
-
Rust
-
Kotlin
-
C#
-
Node.js
-
Ruby
public class DeviceLinkGenerator {
public static void main(String[] args) {
String deviceLinkBase = "https://smart-id.com";
String deviceLinkType = "Web2App";
String sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
String sessionType = "auth";
String version = "1.0";
String lang = "eng";
String authCode = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0";
String deviceLink = deviceLinkBase + "?" +
"deviceLinkType=" + deviceLinkType +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode;
System.out.println(deviceLink);
}
}
<?php
$deviceLinkBase = "https://smart-id.com";
$deviceLinkType = "Web2App";
$sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
$sessionType = "auth";
$version = "1.0";
$lang = "eng";
$authCode = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0";
$deviceLink = $deviceLinkBase . "?"
. "deviceLinkType=" . $deviceLinkType
. "&sessionToken=" . $sessionToken
. "&sessionType=" . $sessionType
. "&version=" . $version
. "&lang=" . $lang
. "&authCode=" . $authCode;
echo $deviceLink;
?>
device_link_base = "https://smart-id.com"
device_link_type = "Web2App" # One of "Web2App", "App2App"
session_token = "wGIrqveE6AuGDATZKmR1mtAZ"
session_type = "auth"
version = "1.0"
lang = "eng"
auth_code = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0"
device_link = device_link_base + "?" + "deviceLinkType=" + device_link_type + "&sessionToken=" + session_token + "&sessionType=" + session_type + "&version=" + version + "&lang=" + lang + "&authCode=" + auth_code
print(device_link)
package main
import (
"fmt"
"strconv"
)
func main() {
var deviceLinkBase string = "https://smart-id.com"
var deviceLinkType string = "Web2App" // One of "Web2App", "App2App"
var sessionToken string = "wGIrqveE6AuGDATZKmR1mtAZ"
var sessionType string = "auth"
var version string = "1.0"
var lang string = "eng"
var authCode string = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0"
var deviceLink string = deviceLinkBase + "?" + "deviceLinkType=" + deviceLinkType +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode
fmt.Printf("%s\n", deviceLink)
}
fn main() {
let device_link_base: &str = "https://smart-id.com";
let device_link_type: &str = "Web2App"; // One of "Web2App", "App2App"
let session_token: &str = "wGIrqveE6AuGDATZKmR1mtAZ";
let session_type: &str = "auth";
let version: &str = "1.0";
let lang: &str = "eng";
let auth_code: &str = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0";
let device_link: String = format!("{}?deviceLinkType={}&sessionToken={}&sessionType={}&version={}&lang={}&authCode={}", device_link_base, device_link_type, session_token, session_type, version, lang, auth_code);
println!("{}", device_link);
}
fun main() {
val deviceLinkBase: String = "https://smart-id.com"
val deviceLinkType: String = "Web2App"
val sessionToken: String = "wGIrqveE6AuGDATZKmR1mtAZ"
val sessionType: String = "auth"
val version: String = "1.0"
val lang: String = "eng"
val authCode: String = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0"
val deviceLink: String = "$deviceLinkBase" + "?" +
"deviceLinkType=$deviceLinkType&" +
"sessionToken=$sessionToken&" +
"sessionType=$sessionType&" +
"version=$version&" +
"lang=$lang&" +
"authCode=$authCode"
println(deviceLink)
}
using System;
public class DeviceLinkGenerator
{
public static void Main(string[] args)
{
string deviceLinkBase = "https://smart-id.com";
string deviceLinkType = "Web2App";
string sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
string sessionType = "auth";
string version = "1.0";
string lang = "eng";
string authCode = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0";
string deviceLink = deviceLinkBase + "?" +
"deviceLinkType=" + deviceLinkType +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode;
Console.WriteLine(deviceLink);
}
}
const deviceLinkBase = "https://smart-id.com";
const deviceLinkType = "Web2App";
const sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
const sessionType = "auth";
const version = "1.0";
const lang = "eng";
const authCode = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0";
const deviceLink = deviceLinkBase + "?" +
"deviceLinkType=" + deviceLinkType +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode;
console.log(deviceLink);
device_link_base = "https://smart-id.com"
device_link_type = "Web2App" # One of "Web2App", "App2App"
session_token = "wGIrqveE6AuGDATZKmR1mtAZ"
session_type = "auth"
version = "1.0"
lang = "eng"
auth_code = "aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0"
device_link = device_link_base + "?" +
"deviceLinkType=" + device_link_type +
"&sessionToken=" + session_token +
"&sessionType=" + session_type +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + auth_code
puts device_link
https://smart-id.com/device-link?deviceLinkType=Web2App&sessionToken=wGIrqveE6AuGDATZKmR1mtAZ&sessionType=auth&version=1.0&lang=eng&authCode=aegUh6gCKkXBJhhvtJqSTWB5_2W8TDQt5eZ7db6krv0
https://smart-id.com/device-link?deviceLinkType=App2App&sessionToken=wGIrqveE6AuGDATZKmR1mtAZ&sessionType=sign&version=1.0&lang=eng&authCode=8QZ16rkBW_ffkq6osT7UH1DbUlF9gEOZevgj-A0VNbM
https://smart-id.com/device-link?deviceLinkType=Web2App&sessionToken=wGIrqveE6AuGDATZKmR1mtAZ&sessionType=cert&version=1.0&lang=eng&authCode=PI1qYa9_l6zR-v6Pkre6ycSm7S3BGiOQSe5OQlw4UJg
Cross-device (dynamic QR code) flows
QR message itself is defined as a URL, just like App2App and Web2App links.
deviceLink := STR-CONCAT(
deviceLinkBase, "?",
"deviceLinkType=", deviceLinkType,
"&elapsedSeconds=", TO-STRING(elapsedSeconds),
"&sessionToken=", sessionToken,
"&sessionType=", sessionType,
"&version=", version,
"&lang=", lang,
"&authCode=", authCode)
-
Java
-
PHP
-
Python
-
Go
-
Rust
-
Kotlin
-
C#
-
Node.js
-
Ruby
public class DeviceLinkGenerator {
public static void main(String[] args) {
String deviceLinkBase = "https://smart-id.com";
String deviceLinkType = "QR";
int elapsedSeconds = 22;
String sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
String sessionType = "auth";
String version = "1.0";
String lang = "eng";
String authCode = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0";
String deviceLink = deviceLinkBase + "?" +
"deviceLinkType=" + deviceLinkType +
"&elapsedSeconds=" + elapsedSeconds +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode;
System.out.println(deviceLink);
}
}
<?php
$deviceLinkBase = "https://smart-id.com";
$deviceLinkType = "QR";
$elapsedSeconds = 22;
$sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
$sessionType = "auth";
$version = "1.0";
$lang = "eng";
$authCode = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0";
$deviceLink = $deviceLinkBase . "?"
. "deviceLinkType=" . $deviceLinkType
. "&elapsedSeconds=" . $elapsedSeconds
. "&sessionToken=" . $sessionToken
. "&sessionType=" . $sessionType
. "&version=" . $version
. "&lang=" . $lang
. "&authCode=" . $authCode;
echo $deviceLink;
?>
device_link_base = "https://smart-id.com"
device_link_type = "QR" # "QR"
elapsed_seconds = 22
session_token = "wGIrqveE6AuGDATZKmR1mtAZ"
session_type = "auth"
version = "1.0"
lang = "eng"
auth_code = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0"
device_link = device_link_base + "?" + "deviceLinkType=" + device_link_type + "&elapsedSeconds=" + str(elapsed_seconds) + "&sessionToken=" + session_token + "&sessionType=" + session_type + "&version=" + version + "&lang=" + lang + "&authCode=" + auth_code
print(device_link)
package main
import (
"fmt"
"strconv"
)
func main() {
var deviceLinkBase string = "https://smart-id.com"
var deviceLinkType string = "QR" // "QR"
var elapsedSeconds int = 22
var sessionToken string = "wGIrqveE6AuGDATZKmR1mtAZ"
var sessionType string = "auth"
var version string = "1.0"
var lang string = "eng"
var authCode string = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0"
var deviceLink string = deviceLinkBase + "?" + "deviceLinkType=" + deviceLinkType +
"&elapsedSeconds=" + strconv.Itoa(elapsedSeconds) +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode
fmt.Printf("%s\n", deviceLink)
}
fn main() {
let device_link_base: &str = "https://smart-id.com";
let device_link_type: &str = "QR"; // "QR"
let elapsed_seconds: i32 = 22;
let session_token: &str = "wGIrqveE6AuGDATZKmR1mtAZ";
let session_type: &str = "auth";
let version: &str = "1.0";
let lang: &str = "eng";
let auth_code: &str = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0";
let device_link: String = format!("{}?deviceLinkType={}&elapsedSeconds={}&sessionToken={}&sessionType={}&version={}&lang={}&authCode={}", device_link_base, device_link_type, elapsed_seconds, session_token, session_type, version, lang, auth_code);
println!("{}", device_link);
}
fun main() {
val deviceLinkBase: String = "https://smart-id.com"
val deviceLinkType: String = "QR"
val elapsedSeconds: Int = 22
val sessionToken: String = "wGIrqveE6AuGDATZKmR1mtAZ"
val sessionType: String = "auth"
val version: String = "1.0"
val lang: String = "eng"
val authCode: String = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0"
val deviceLink: String = "$deviceLinkBase" + "?" +
"deviceLinkType=$deviceLinkType&" +
"elapsedSeconds=$elapsedSeconds&" +
"sessionToken=$sessionToken&" +
"sessionType=$sessionType&" +
"version=$version&" +
"lang=$lang&" +
"authCode=$authCode"
println(deviceLink)
}
using System;
public class DeviceLinkGenerator
{
public static void Main(string[] args)
{
string deviceLinkBase = "https://smart-id.com";
string deviceLinkType = "QR";
int elapsedSeconds = 22;
string sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
string sessionType = "auth";
string version = "1.0";
string lang = "eng";
string authCode = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0";
string deviceLink = deviceLinkBase + "?" +
"deviceLinkType=" + deviceLinkType +
"&elapsedSeconds=" + elapsedSeconds.ToString() +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode;
Console.WriteLine(deviceLink);
}
}
const deviceLinkBase = "https://smart-id.com";
const deviceLinkType = "QR";
const elapsedSeconds = 22;
const sessionToken = "wGIrqveE6AuGDATZKmR1mtAZ";
const sessionType = "auth";
const version = "1.0";
const lang = "eng";
const authCode = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0";
const deviceLink = deviceLinkBase + "?" +
"deviceLinkType=" + deviceLinkType +
"&elapsedSeconds=" + elapsedSeconds +
"&sessionToken=" + sessionToken +
"&sessionType=" + sessionType +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + authCode;
console.log(deviceLink);
device_link_base = "https://smart-id.com"
device_link_type = "QR" # "QR"
elapsed_seconds = 22
session_token = "wGIrqveE6AuGDATZKmR1mtAZ"
session_type = "auth"
version = "1.0"
lang = "eng"
auth_code = "OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0"
device_link = device_link_base + "?" +
"deviceLinkType=" + device_link_type +
"&elapsedSeconds=" + elapsed_seconds.to_s +
"&sessionToken=" + session_token +
"&sessionType=" + session_type +
"&version=" + version +
"&lang=" + lang +
"&authCode=" + auth_code
puts device_link
https://smart-id.com/device-link?deviceLinkType=QR&elapsedSeconds=22&sessionToken=wGIrqveE6AuGDATZKmR1mtAZ&sessionType=auth&version=1.0&lang=eng&authCode=OY1eHaD4UYedrBwtqUbSkpa0w7ttm4FllPkCD_3wlE0
For more details on how the dynamic QR code or other device links should be presented in the RP frontend, see Device link and QR presentation section.
Device link parameters
The table below explains the parameters that are included in the device link calculation.
Parameter | Formula |
---|---|
|
A pre-defined version value, currently |
|
Generated by RP API. It is a URL safe value and it must be used as is. |
|
Allowed values:
|
|
Allowed values:
|
|
Seconds since receiving the response from the initial request to RP API. Only used in cross-device flows (dynamic QR code). |
|
An ISO 639-2 three-letter language code. It should match the language that the user was seeing in the RP UI previously. This only affects the language of the fallback page, not the Smart-ID app itself. |
|
Calculated using |
IMPORTANT
|
version
parameter
version
parameter is a pre-defined value. Currently, only 1.0
is supported. It must be used in string
form in device links.
version
parameter"1.0"
sessionToken
parameter
RP API returns sessionToken
. It is a URL safe value and it must be used as is (in string
form) in device links.
sessionToken
parameter"wGIrqveE6AuGDATZKmR1mtAZ"
deviceLinkType
parameter
The parameter deviceLinkType
describes the device link type:
-
QR
- for dynamic QR codes, -
Web2App
- for non-dynamic Web2App device links, -
App2App
- for non-dynamic App2App device links.
The string value must be used as is (in string
form) in device links.
deviceLinkType
parameter"QR"
sessionType
parameter
The parameter sessionType
describes the session type:
-
auth
- for authentication session, -
sign
- for signature session, -
cert
- for certificate-choice session.
The string value must be used as is (in string
form) in device links.
sessionType
parameter"auth"
elapsedSeconds
parameter
The elapsedSeconds
parameters is seconds since receiving the response from the initial session creation request to RP API. Notice that dynamic QR code link must be re-calculated each second because the elapsedSeconds
value changes.
elapsedSeconds
value must be converted to string value to be used in QR code links:
elapsedSeconds := TO-STRING(elapsedSeconds)
-
Java
-
PHP
-
Python
-
Go
-
Rust
-
Kotlin
-
C#
-
Node.js
-
Ruby
public class Int2Str {
public static void main(String[] args) {
int elapsedSeconds = 22;
String elapsedSecondsStr = Integer.toString(elapsedSeconds);
System.out.println(elapsedSecondsStr);
}
}
<?php
$elapsedSeconds = 22;
$elapsedSecondsStr = strval($elapsedSeconds);
echo $elapsedSecondsStr;
?>
elapsed_seconds = 22
elapsed_seconds_str = str(elapsed_seconds)
print(elapsed_seconds_str)
package main
import (
"fmt"
"strconv"
)
func main() {
var elapsedSeconds int = 22
var elapsedSecondsStr string = strconv.Itoa(elapsedSeconds)
fmt.Printf("%s\n", elapsedSecondsStr)
}
fn main() {
let elapsed_seconds: i32 = 22;
let elapsed_seconds_str: String = elapsed_seconds.to_string();
println!("{}", elapsed_seconds_str)
}
fun main() {
val elapsedSeconds: Int = 22
val elapsedSecondsStr: String = elapsedSeconds.toString()
println(elapsedSecondsStr)
}
using System;
public class Int2Str
{
public static void Main()
{
int elapsedSeconds = 22;
string elapsedSecondsStr = elapsedSeconds.ToString();
Console.WriteLine(elapsedSecondsStr);
}
}
let elapsedSeconds = 22;
let elapsedSecondsStr = elapsedSeconds.toString();
console.log(elapsedSecondsStr);
elapsed_seconds = 22
elapsed_seconds_str = elapsed_seconds.to_s
puts elapsed_seconds_str
elapsedSeconds
parameter"22"
lang
parameter
The lang
parameter defines the interface language for the HTTPS fallback page that is shown to the user when there are technical problems regarding opening device links (for example, if user’s browser does not support Web2App links and needs to be configured to allow them or if Smart-ID app is not installed on the phone). Its format must be ISO 639-2 three-letter language code. It is recommended that for consistent user experience, RP provides here the same language that the user was already using in the RP UI. This parameter only affects the language of the fallback page, not the Smart-ID app itself.
lang
parameter"eng"
authCode
parameter
authCode
parameter generation is explained in detail in authCode
calculation page.
Parameter visibility
The following table aims to clarify which of the parameters involved in device link construction should be accessible by which parties (see legend below). The table also includes parameters which are used for authCode
calculation (e.g. unprotectedDeviceLink
) — for their descriptions, see authCode calculation page.
RP API server | RP backend | RP frontend/app | |
---|---|---|---|
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
Legend:
-
- source of parameter (the party that generates/calculates it)
-
- knows parameter
-
- does not know parameter
-
- MUST NOT know parameter
Fallback page
For cases when the app is not installed or some other problem occurs with opening a device link, a fallback HTTPS webpage is opened with instructions on how to resolve the issue. The lang
parameter in the link is by default used to show the same language as on the RP UI. The instructions cover the following scenarios:
-
User is not on a mobile device where Smart-ID app is installed and has been allowed by the RP to use a wrong type of device link (e.g. a same-device link on a PC).
-
Smart-ID App is not installed on the current device.
-
Mobile browser does not support universal links / Android App links.
-
Mobile OS/browser settings disallow the link and user intervention is required.
Device link and QR presentation
For generation of QR codes, error correction level LOW or MEDIUM should be used. HIGH cannot be used due to the typical device link length. The QR code should have sufficient margins to enable quick scanning by the Smart-ID app. The optimal block size for QR code is 6-10 pixels per block when displayed on a PC, since:
-
Block sizes 5 pixels and less are too small for easy scanning (devices might have focus issues, although many will not).
-
Block sizes 20 pixels and above are too big for convenient scanning and take up too much space.
When QR code is displayed on a mobile device, it is recommended to show the QR code as big as possible, up to 10 pixels per block.
QR code can be generated on the client or server. It is recommended to use the SVG format as it remains sharp when users zoom the page. Note that many QR code libraries that support SVG output do not accept block size as an input parameter. So, block size should be estimated based on overall image dimensions, device link length and error correction level. See QR generator section for an example which also calculates the resulting block size in pixels.
IMPORTANT
The actual generation of the QR image can be done both in the backend or the frontend, however the link itself must only be generated by the RP backend. This is because sensitive parameters such as |