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
| Capability | Details |
|---|---|
| Handshake | GET /public-key then POST /session against the Secure Channel base URL. |
| Key exchange | Encrypts generated AES-256 request and response keys with RSA-OAEP. |
| Payload protection | Uses AES-256-GCM for request encryption and response decryption. |
| Session reuse | Reuses the active session until expiresAt, then re-initializes automatically. |
| Concurrency guard | Shares 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.
| Constructor | Description |
|---|---|
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 stringinitSession() Flow
- Fetch the active RSA public key from
GET {baseUrl}/public-key. - Generate two random 32-byte AES keys.
- Import the RSA public key from Base64 SPKI DER.
- Encrypt both AES keys with RSA-OAEP.
- Create the session through
POST {baseUrl}/session. - Import both AES keys as
CryptoKeyinstances and store the returnedsessionIdandexpiresAt.
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
| Phase | Behavior |
|---|---|
| Init | No session exists until initSession() runs or encrypt() / decrypt() calls ensureSession(). |
| Use | encrypt() and decrypt() reuse the current sessionId, request key, and response key. |
| Expire | isSessionActive() returns false once Date.now() >= expiresAt. |
| Re-init | The 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
| Layer | Implementation |
|---|---|
| Public-key exchange | importRsaPublicKey() loads an SPKI DER key and rsaOaepEncrypt() encrypts raw AES key bytes with RSA-OAEP + SHA-256. |
| Symmetric encryption | aesGcmEncrypt() / aesGcmDecrypt() use AES-GCM with a 256-bit key and 128-bit tag. |
| IV | Each envelope uses a random 12-byte IV. |
| AAD | Request AAD is {sessionId}:request, response AAD is {sessionId}:response. |
| Envelope | encodeEnvelope() serializes `IV |
Lower-Level Helpers
Use these exports when you need custom transport or protocol testing.
| Export | Use |
|---|---|
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'));