MFA Transaction Signing
Enrol a user, set a PIN, and sign a transaction from JavaScript, on Android and iOS alike.
Overview
Section titled “Overview”Read MFA Transaction Signing first for the three-call flow and what the token proves.
All three resolve rather than throw. A refusal arrives as { success: false, errorCode }, so the
transaction path and the error path are the same await.
Basic Usage
Section titled “Basic Usage”import { AlphynSDK } from '@alphyn/react-native-sdk';
// 1. Once per (user, device), after login. No biometric prompt appears here.const enrolment = await AlphynSDK.enrollForMfa(userId);if (!enrolment.success) { console.warn('enrol failed:', enrolment.errorCode);}
// 2. Set or change the PIN. Verified and hashed ON THE SERVER.await AlphynSDK.setMfaPin(userId, newPin); // first timeawait AlphynSDK.setMfaPin(userId, newPin, oldPin); // changing it
// 3. Per transaction, after the user clears your confirmation screen.const result = await AlphynSDK.signAction( userId, 'TRANSFER', { amount: '250.00', currency: 'TRY', toIBAN: 'TR12…' }, pin,);
if (result.success) { await sendToYourBackend(result.verificationToken);}Methods
Section titled “Methods”AlphynSDK.enrollForMfa(userId: string): Promise<MfaResult>
AlphynSDK.setMfaPin( userId: string, newPin: string, oldPin?: string | null): Promise<MfaResult>
AlphynSDK.signAction( userId: string, actionLabel: string, context: ActionContext, pin?: string | null, subject?: AlphynSubject | null,): Promise<ActionVerificationResult>Parameters
Section titled “Parameters”| Parameter | Type | Notes |
|---|---|---|
userId | string | your identifier for the logged-in user. Empty resolves with ARG_ERROR |
newPin / oldPin | string | omit oldPin on a first set. An empty string is treated as omitted |
actionLabel | string | your own name for the operation. Empty resolves with ARG_ERROR |
context | ActionContext | the transaction. Its hash is bound into the signature |
pin | string | null | optional. null omits the factor rather than failing |
subject | AlphynSubject | null | optional. See below |
The PIN crosses as a string here because that is what JavaScript has; the native bridge converts it to bytes and zeroes its own copy. A JavaScript string cannot be wiped, so do not hold one longer than the call.
MfaResult
Section titled “MfaResult”{ success: boolean; errorCode?: string; errorMessage?: string; securityLevel?: string; enrollmentId?: string }errorCode is the backend’s string verbatim. Branch on it, never on errorMessage.
ActionVerificationResult
Section titled “ActionVerificationResult”{ success: boolean; verificationToken?: string; expiresAtMs?: number; errorCode?: string; errorMessage?: string }Declaring an identity
Section titled “Declaring an identity”const result = await AlphynSDK.signAction( userId, 'TRANSFER', { amount: '250.00', currency: 'TRY' }, pin, { tc: '10000000146', phone: '+905342223344' },);AlphynSubject is a plain object with three optional string fields, tc, phone and email. The
values cross to the native core, which validates and tokenises them; nothing is hashed in JavaScript.
An object whose fields are all undefined reaches native as no identity, not as three empty
strings; an empty string would be validated, dropped, and would read in a log like a caller who
sent garbage rather than one who sent nothing.
A field that does not conform is dropped, never corrected, and a subject in which nothing survived produces a request byte-identical to one that declared none. See Subject identity for the accepted forms and for why a token must be treated as identifying data.
Use Cases
Section titled “Use Cases”- Signing a transfer, withdrawal or beneficiary change so your backend can prove afterwards that a specific user’s device authorised those exact values
- Adding a second factor to a high-risk action without an SMS round trip
- Correlating a signed transaction with a person across institutions, through a subject token
Example: signing a transfer end to end
Section titled “Example: signing a transfer end to end”async function confirmTransfer(userId: string, iban: string, amount: string, pin: string) { const result = await AlphynSDK.signAction( userId, 'TRANSFER', { amount, currency: 'TRY', toIBAN: iban }, pin, { tc: customerTckn }, );
if (!result.success) { // THREAT_DETECTED is a refusal, not a transport failure. Do not retry silently. showError(result.errorCode); return; }
// Your backend validates this token before it moves anything. await yourApi.transfer(iban, amount, result.verificationToken);}The token is evidence, not a decision. Your backend must validate it server-side and compare the echoed context against the transfer it is about to perform; see Backend verification.