Strong Customer Authentication (SCA)

PSD2 compliant Strong Customer Authentication (SCA) for e-money and e-money tokens

As a licensed Institution, we are legally required to enforce SCA on e-money (EUR) and e-money token (USDC) transactions. Further, with the API model, in order to keep your application out of CASP licensing scope in the EEA, 2FA confirmation from the end user for transactional activity (namely trades, inter/intra and withdrawals of crypto assets) is required.

We implement SCA with three factors - SMS, TOTP (authenticator app), and passkey (WebAuthn) - and two carve-outs that lower the operational cost without breaking PSD2 conformance: a 180-day session that covers reads and a Trusted Beneficiary mark that lets recurring payouts skip the per-debit Dynamic Linking ceremony.

Login management

📘

To keep the burden of SCA as minimal as possible without breaking existing user experiences, Striga (LSPEU) will outsource the management of user login/logout & session management to your application. This will be a stringent part of the go-live review to ensure that your application is managing user sessions appropriately. This way, a single SCA session will introduce the most minimal set of changes to your UX, i.e. either authenticating with TOTP/SMS/Passkey effectively twice a year (i.e. every 180 days) keeps your UX simple without the requirement of Striga (LSPEU) having to manage password/login sessions.

What SCA covers

OperationGated?Notes
EUR / USDC debit (SEPA, swap, onchain, intra-ledger)YesPer-operation challenge with Dynamic Linking.
BTC / ETH / Lightning onchain debitPartiallyChallenge required; Dynamic Linking does not apply (currency not classified as FIAT/EMT).
Reading an EUR / USDC account balanceYesRequires an active 180-day session; no per-call challenge.
Reading account history older than 90 daysYesFresh SCA event required (sub-window of the 180-day session).
Standing order setup or cancelYesSCA at setup; no per-execution challenge.
Trust / untrust a beneficiaryYesPer-operation challenge; choice of any allowed factor.
Removing an enrolled passkeyYesTwo-step challenge; the user confirms with any enrolled factor before the credential is removed.
LoginYesUnified across factors via /user/login/start + /user/login/complete.
Reading a non-EUR/USDC account, KYC, profile updatesNoNo SCA gate.

Per Workflow Mapping

This table is the authoritative compliance reference for which authentication tier and which second factor is required for every account action on the Striga (LSP EU) platform.

Legend

Symbol / termMeaning
SCAStrong Customer Authentication. PSD2-compliant two-factor authentication with all the structural requirements (independence, dynamic linking where applicable). Required for EUR/USDC ("payment account") operations.
2FATwo-factor authentication. Lighter contract - no PSD2 dynamic-linking, no 180-day session model. Applies to non-payment-account operations (other crypto-assets).
2×SCATwo independent SCA ceremonies, using two different second factors where possible. If only SMS OTP is enrolled, two separate SMS OTPs are acceptable.
✓Second factor is permitted for this action.
✗Second factor is not permitted for this action (typically because it cannot carry the Dynamic Linking payload).
—Not applicable. Either the action is out of scope for SCA (other crypto-assets) or the field doesn't apply.
Dynamic linkingPSD2 Art. 97(2) requirement that the authentication code is cryptographically bound to the transaction amount and payee. Only SMS OTP and Passkey can carry this binding; TOTP cannot, which is why it is ✗ on dynamic-linked operations.

Mapping

Account management

ActionGateSMS OTPTOTPPasskeyDynamic linkingNotes
Access EUR or USDC accountSCA✓✓✓No180-day SCA exemption after the first/previous SCA event, limited to viewing the last 90 days of transaction history. Viewing history beyond 90 days requires a fresh SCA event.
Create new EUR or USDC accountSCA✓✓✓No
Access other crypto-asset account—————Not subject to SCA. Partner password is the minimum standard.
Create new crypto-asset account—————Not subject to SCA.

Factor management

ActionGateSMS OTPTOTPPasskeyDynamic linkingNotes
Remove an enrolled passkeySCA——✓NoTwo-step ceremony: /user/passkey/delete binds a challenge to the credential and returns the assertion request for that passkey alone; /user/passkey/delete/confirm verifies an assertion by the passkey being removed and deletes it.

Trusted Beneficiary list

ActionGateSMS OTPTOTPPasskeyDynamic linking
Whitelist (trust) EUR or USDC beneficiarySCA✓✓✓No
De-whitelist (untrust) EUR or USDC beneficiarySCA✓✓✓No

Send

ActionGateSMS OTPTOTPPasskeyDynamic linkingNotes
Send EUR or USDC - beneficiary not whitelistedSCA✓✗✓YesSMS OTP by default. Passkey requires a separate agreement with LSP EU. TOTP cannot be used (no Dynamic Linking payload).
Send EUR or USDC - beneficiary whitelisted2FA✓✓✓—2FA may be removed in the future once active-session monitoring is in place.
Send other crypto-asset2FA✓✓✓—

Convert

ActionGateSMS OTPTOTPPasskeyDynamic linkingNotes
Convert from EUR or USDCSCA✓✗✓YesSMS OTP by default. Passkey requires a separate agreement with LSP EU. TOTP cannot be used. (see below)
Convert from other crypto-asset2FA✓✓✓—2FA may be removed in the future once active-session monitoring is in place.

Set up standing order to convert

ActionGateSMS OTPTOTPPasskeyDynamic linkingNotes
Convert from EUR or USDCSCA✓✓✓NoEnvelope SCA at setup; no per-execution challenge. TOTP is permitted here because the standing-order setup carries no per-debit amount/payee to dynamically link.
Convert from other crypto-asset2FA✓✓✓—

Set up standing order to convert and pay out

ActionGateSMS OTPTOTPPasskeyDynamic linkingNotes
Convert EUR/USDC and pay out to other crypto-assetSCA✓✓✓No
Convert EUR/USDC and pay out to USDC or EUR -beneficiary not whitelisted2×SCA✓✓✓NoTwo independent SCAs required. One authorises whitelisting the beneficiary; the other authorises the rest of the instruction. Two different second factors must be used where possible; otherwise two separate SMS OTPs.
Convert EUR/USDC and pay out to USDC or EUR -beneficiary whitelistedSCA✓✓✓NoTrusted Beneficiary carve-out: a single SCA at setup is sufficient.
Convert from other crypto-asset and pay out to other crypto-asset2FA✓✓✓—
Convert from other crypto-asset and pay out to USDC or EUR - beneficiary not whitelisted2×SCA✓✓✓NoSame rule as above: one SCA to whitelist, one SCA for the rest. Different factors preferred; two separate SMS OTPs accepted.
Convert from other crypto-asset and pay out to USDC or EUR - beneficiary whitelistedSCA✓✓✓No
📘

Why TOTP is ✗ on per-transaction EUR/USDC sends and converts

PSD2 Article 97(2) requires the authentication code to be dynamically linked to the amount and the payee. The code displayed by an authenticator app is computed only from a clock and a shared secret - it has no input from the transaction parameters and therefore cannot satisfy this binding. SMS OTP and passkey-issued challenges can whereas TOTP cannot. The implementation enforces this with a server-side reject (INVALID_FACTOR_FOR_REGULATED_DEBIT) and the /wallets/send/initiate/* endpoints surface the rejection at request time.

The same legal text grants exemptions for trusted beneficiaries (the "Row 7" carve-out) and for standing orders that have no per-execution amount/payee to link. In both cases TOTP becomes permissible, which is why the matrix lights it up on those rows.


SCA Workflow

Managing passkeys

A user may enrol up to two passkeys - a primary device plus one spare - so that losing a device does not mean losing the factor. Pass an optional displayName ("iPhone", "Work laptop") on /user/passkey/register/start; it is echoed back by /user/passkey/list so the user can tell their passkeys apart. A third registration is rejected with 100004. Adding a factor also needs a fresh login: /user/start/setup-two-factor, /user/passkey/register/start and /user/passkey/import/start require an active SCA session granted within the last 15 minutes, else 30070 (SCA_REQUIRED, reason NO_SESSION or EXPIRED, as the SCA-gated reads report it); run /user/login/start + /user/login/complete and retry inside the window. An open 2FA reset for that factor whose /complete call has already removed the old factor authorizes the call as well, and users without an SCA-gated (EUR/USDC) account yet are exempt.

Removing a passkey is itself an SCA event: the user must prove possession of the passkey being removed before it goes away. No other factor is accepted; a user who no longer holds the key goes through the 2FA reset (see the note below). The removal is a two-step ceremony:

  1. POST /user/passkey/delete with the credentialId from the list endpoint. Nothing is deleted yet. The response binds a 10-minute challenge to that credential and returns the WebAuthn assertion request for that passkey alone (allowCredentials lists only the passkey being removed); there is no separate approval ceremony. An unknown credentialId is refused with 999998.
  2. POST /user/passkey/delete/confirm with the challengeId, the client ip, and the assertion (origin + credential) produced by the passkey being removed. On success the bound credential is removed and the refreshed multiFactorAuth state is returned; removing the last passkey drops passkey from enrolledMethods.

One challenge authorises one deletion and cannot be redirected to a different credential. An assertion by any other passkey, even one enrolled on the same account, is refused with 999998 before it is verified. Confirming a consumed challenge returns 999998; an expired or unknown challengeId returns 999995; an assertion that does not verify returns 999998. The assertion request is single-use: if the assertion was accepted but the removal failed downstream, start again from /user/passkey/delete.

The business endpoints /business/passkey/delete and /business/passkey/delete/confirm follow the same contract keyed by businessId.

Native apps have a third way in: /user/passkey/import/start + /user/passkey/import/complete enrol a key that lives in the phone's Secure Enclave or Android Keystore and is released only by the device biometric, without the system passkey sheet. Striga verifies Apple App Attest or Android Key Attestation before accepting it, and the result is an ordinary passkey for every ceremony on this page. See Device-bound passkeys (native apps).

📘

Removal needs the passkey itself by design. A user who no longer holds it goes through the liveness-gated 2FA reset instead: opened with the credentialId of the passkey to replace, it removes that passkey alone and returns creation options for its replacement; opened without one, it removes every passkey.

WebAuthn Configuration Prerequisites

Before implementing the Passkey (WebAuthn) flow, you must configure the required verification form in the Striga Portal.

Navigate to:

Settings → WebAuthn → Questionnaires

Create a verification form that will be used during the passkey reset process.

Important: The passkey reset flow requires a verification form to be configured before users can complete the reset process successfully.

Example Configuration

WebAuthn Questionnaire Configuration