Skip to content

WEB SDK Secure Channel

SecureChannel implements the client side of the SCv2 protocol. It creates a session with the backend, keeps request and response AES keys in memory, and encrypts JSON payloads into the SDK envelope format.

What It Does

CapabilityDetails
HandshakeGET /public-key then POST /session against the Secure Channel base URL.
Key exchangeEncrypts generated AES-256 request and response keys with RSA-OAEP.
Payload protectionUses AES-256-GCM for request encryption and response decryption.
Session reuseReuses the active session until expiresAt, then re-initializes automatically.
Concurrency guardShares one in-flight initSession() call through an internal promise.

Initialize

The constructor only stores configuration. The first explicit initSession() or the first encrypt() / decrypt() call triggers the SCv2 handshake.

ConstructorDescription
new SecureChannel(baseUrl: string, portalAccessCode: string)baseUrl should point at the Secure Channel root, for example https://api.example.com/sc.
typescript
import { SecureChannel } from '@slaunchx/web-sdk';

const sc = new SecureChannel('https://api.example.com/sc', 'portal-web');

await sc.initSession();

console.log(sc.isSessionActive()); // true
console.log(sc.getSessionId()); // session id string

initSession() Flow

  1. Fetch the active RSA public key from GET {baseUrl}/public-key.
  2. Generate two random 32-byte AES keys.
  3. Import the RSA public key from Base64 SPKI DER.
  4. Encrypt both AES keys with RSA-OAEP.
  5. Create the session through POST {baseUrl}/session.
  6. Import both AES keys as CryptoKey instances and store the returned sessionId and expiresAt.

Encrypt And Decrypt

encrypt() serializes the input as JSON, uses the request AES key, and emits a JSON envelope string.

typescript
const encrypted = await sc.encrypt({
  amount: 100,
  currency: 'USD',
});

console.log(encrypted);
// {"v":2,"p":"..."}

decrypt() expects the encrypted response envelope, uses the response AES key, and parses the JSON payload back into an object.

typescript
const decrypted = await sc.decrypt('{"v":2,"p":"..."}') as {
  approved: boolean;
  traceId: string;
};

console.log(decrypted.approved);

Session Lifecycle

PhaseBehavior
InitNo session exists until initSession() runs or encrypt() / decrypt() calls ensureSession().
Useencrypt() and decrypt() reuse the current sessionId, request key, and response key.
ExpireisSessionActive() returns false once Date.now() >= expiresAt.
Re-initThe next operation automatically runs initSession() again. Concurrent callers await the same initialization promise.
typescript
if (!sc.isSessionActive()) {
  await sc.initSession();
}

const sessionId = sc.getSessionId();

Crypto Internals

LayerImplementation
Public-key exchangeimportRsaPublicKey() loads an SPKI DER key and rsaOaepEncrypt() encrypts raw AES key bytes with RSA-OAEP + SHA-256.
Symmetric encryptionaesGcmEncrypt() / aesGcmDecrypt() use AES-GCM with a 256-bit key and 128-bit tag.
IVEach envelope uses a random 12-byte IV.
AADRequest AAD is {sessionId}:request, response AAD is {sessionId}:response.
EnvelopeencodeEnvelope() serializes `IV

Lower-Level Helpers

Use these exports when you need custom transport or protocol testing.

ExportUse
encodeEnvelope() / decodeEnvelope()Work directly with plaintext bytes and an AES CryptoKey.
generateAesKey() / importAesKey()Generate and import AES-256 keys.
aesGcmEncrypt() / aesGcmDecrypt()Encrypt and decrypt raw byte arrays.
importRsaPublicKey() / rsaOaepEncrypt()Perform the RSA key-exchange steps yourself.
toBase64Url() / fromBase64Url() / toBase64() / fromBase64()Convert between strings and Uint8Array.
buildAad()Build the SCv2 additional authenticated data bytes.
typescript
import {
  buildAad,
  decodeEnvelope,
  encodeEnvelope,
  generateAesKey,
  importAesKey,
} from '@slaunchx/web-sdk';

const rawKey = await generateAesKey();
const key = await importAesKey(rawKey);
const plaintext = new TextEncoder().encode(JSON.stringify({ ping: true }));

const envelope = await encodeEnvelope(plaintext, key, 'sc_session_123');
const bytes = await decodeEnvelope(envelope, key, 'sc_session_123');

console.log(new TextDecoder().decode(bytes));
console.log(buildAad('sc_session_123', 'request'));

SlaunchX Internal Documentation