Dynamic QR Code Implementation (BETA)

Dynamic QR Code Implementation (BETA)

QR Code Generation

The QR code used for Freja eID authentication or signing is generated on the Relying Party side.
You can use any standard QR code generation library for this purpose.

The QR code link should follow this pattern:

https://app.prod.frejaeid.com/freja?action=bindUserToTransaction&transactionReference=<transactionReference>.<timeWindow>.<hmac>&originAppScheme=https://acme.com

Where:

  • https://app.prod.frejaeid.com/freja — base URL used by the Freja eID mobile app to interpret the QR code.

  • action — fixed parameter with the value bindUserToTransaction

  • transactionReference —unique identifier returned in the response from the initiate authentication or initiate signing request.

  • timeWindow — number of seconds elapsed since the transaction was initiated (i.e., since you received the response from the initiate request).

  • hmac — Base64 URL-safe encoded value calculated as:

HMACSHA256(qrCodeSecret, timeWindow)

where:

  • qrCodeSecret is the secret returned from the initiate authentication or signing response.

  • timeWindow is the number of seconds since the transaction started.

The qrCodeSecret must never be exposed to or handled by the frontend.
It should remain private between your backend system and the Freja eID service.

Java sample code

package com.verisec.frejaeid.mobileservice; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; /** * Example demonstrating how to generate Freja eID QR code data * for binding a user to a transaction. */ public class QrCodeDataGenerationExample { public static void main(String[] args) throws Exception { // Values received in the response when initiating authentication or signing String transactionReference = "9oAyYm8qniEjB22GpcxAdCfoiwLI5EHblg2eENmVgx3-uf5I2iLhqq15tqenBgcd"; String qrCodeSecret = "nfSYvFgpgxrUk3JOtzRf1IUt4XtZaXFTyiZgGnIvLNM"; String originAppScheme = "https://acme.com"; // Calculate the number of seconds since the transaction started long transactionStartedAt = Instant.now().minusSeconds(1).toEpochMilli(); // Example only String timeWindow = Long.toString( Duration.between(Instant.ofEpochMilli(transactionStartedAt), Instant.now()).getSeconds() ); // Generate HMAC-SHA256 of timeWindow using qrCodeSecret as the key Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(qrCodeSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); byte[] result = mac.doFinal(timeWindow.getBytes(StandardCharsets.UTF_8)); // Base64 URL-safe encoding without padding (native Java 8+) String hmac = Base64.getUrlEncoder().withoutPadding().encodeToString(result); // Build the final Freja eID QR code link String qrCodeData = String.format( "https://app.prod.frejaeid.com/freja?action=bindUserToTransaction&transactionReference=%s.%s.%s&originAppScheme=%s", transactionReference, timeWindow, hmac, originAppScheme ); // Print the final QR code data System.out.println(qrCodeData); /* * Example output: * https://app.prod.frejaeid.com/freja?action=bindUserToTransaction&transactionReference= * 9oAyYm8qniEjB22GpcxAdCfoiwLI5EHblg2eENmVgx3-uf5I2iLhqq15tqenBgcd.1.EFH24t58Kne9TZjU6yeB1cvMmibQHTqlKbnky1sZWVA * &originAppScheme=https://acme.com */ } }