Skip to main content

Account Management

Learn how to register, authenticate, manage sessions, recover passwords, and delete accounts in CIFER Web2 mode.

Overview​

The web2 namespace enables email + password registration for CIFER, removing the requirement for a blockchain wallet. This is ideal for:

  • Server-side applications and backend services
  • Traditional web apps that don't use wallets
  • TEE (Trusted Execution Environment) integrations
  • Onboarding users who aren't familiar with Web3

Under the hood, Web2 mode uses the same blackbox encryption pipeline as Web3 mode, but replaces wallet-based signing with session-based signing using ephemeral EOA keypairs.

Web2 vs Web3​

FeatureWeb3Web2
AuthEIP-1193 wallet (MetaMask, etc.)Email + password + Ed25519 key
Chain IDReal chain ID (e.g. 8453)WEB2_CHAIN_ID = -1 (sentinel)
Block freshnessRPC eth_blockNumberDate.now() (millisecond timestamp)
SignerWallet personal_signSession EOA personal_sign
Secret creationOn-chain transactionPOST /web2/secret API call

The web2.createClient() factory creates a client that stores your session, blackboxUrl, and readClient internally. Once a session is created, all subsequent calls use it automatically — no need to pass session and blackboxUrl on every call.

import { createCiferSdk, web2 } from 'cifer-sdk';

const sdk = await createCiferSdk({ blackboxUrl: 'https://blackbox.cifersecurity.com:3010' });

// Create a client with stored defaults
const client = web2.createClient({
blackboxUrl: sdk.blackboxUrl,
readClient: sdk.readClient,
});

// Session is auto-stored after creation
await client.createManagedSession({
principalId: 'your-principal-uuid',
ed25519Signer: myEd25519Signer,
});

// All subsequent calls use the stored session automatically
const secret = await client.createSecret();
const encrypted = await client.payload.encryptPayload({
secretId: secret.secretId,
plaintext: 'Hello Web2!',
});

Per-Call Overrides​

Every client method accepts optional overrides for session, blackboxUrl, and readClient:

// Use a different session for this one call
const secret = await client.createSecret({
session: anotherSession,
});

// Override blackboxUrl for this call
const secret2 = await client.createSecret({
blackboxUrl: 'https://other-blackbox.example.com:3010',
});

Manual Session Management​

You can also set the session manually:

// Set session from external source
const session = web2.session.useExistingSessionKey({
sessionPrivateKey: '0xabc123...',
});
client.setSession(session);
Stateless API

The original stateless web2.* functions (web2.secret.createSecret(), web2.blackbox.payload.encryptPayload(), etc.) remain available. Use them when you need full control or are managing multiple sessions.

Registration Flow​

Registration is a three-phase process.

Phase 1: Register with Email​

import { web2 } from 'cifer-sdk';

const result = await web2.auth.register({
email: 'user@example.com',
password: 'securePassword123',
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

console.log('Principal ID:', result.principalId);
// An OTP has been sent to the email

Phase 2: Verify Email​

const verified = await web2.auth.verifyEmail({
email: 'user@example.com',
otp: '123456', // OTP from email
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

console.log('Verified:', verified.emailVerified);
Resend OTP

If the OTP expires or wasn't received, call web2.auth.resendOtp(). There is a 60-second cooldown between requests.

Phase 3: Register Ed25519 Key​

After verification, register an Ed25519 public key. This key is propagated to all enclave cluster nodes and is used to authenticate session creation.

const keyResult = await web2.auth.registerKey({
principalId: result.principalId,
password: 'securePassword123',
ed25519Signer: myEd25519Signer,
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

if (keyResult.nodeRegistrationStatus !== 'complete') {
// Some nodes failed -- retry
await web2.auth.retryNodeRegistration({
principalId: keyResult.principalId,
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});
}

Ed25519 Signer Setup​

The SDK uses a bring-your-own-library pattern for Ed25519 signing. You provide an object implementing the Ed25519Signer interface:

interface Ed25519Signer {
sign(message: Uint8Array): Promise<Uint8Array>;
getPublicKey(): Uint8Array;
}

Using @noble/ed25519​

npm install @noble/ed25519
import * as ed from '@noble/ed25519';

// Generate or load your Ed25519 private key
const privateKey = ed.utils.randomPrivateKey();
const publicKey = await ed.getPublicKeyAsync(privateKey);

const ed25519Signer = {
async sign(message: Uint8Array): Promise<Uint8Array> {
return ed.signAsync(message, privateKey);
},
getPublicKey(): Uint8Array {
return publicKey;
},
};

Using Node.js crypto​

import { createPrivateKey, createPublicKey, sign } from 'crypto';

// Generate an Ed25519 keypair
const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519');

const ed25519Signer = {
async sign(message: Uint8Array): Promise<Uint8Array> {
const sig = sign(null, Buffer.from(message), privateKey);
return new Uint8Array(sig);
},
getPublicKey(): Uint8Array {
// Export raw 32-byte public key
const raw = publicKey.export({ type: 'spki', format: 'der' });
return new Uint8Array(raw.slice(-32));
},
};

Session Management​

Sessions authenticate blackbox API calls. The SDK provides two modes.

The SDK generates an ephemeral EOA keypair, authenticates via your Ed25519 key, and handles renewal automatically.

const session = await web2.session.createManagedSession({
principalId: 'your-principal-uuid',
ed25519Signer: myEd25519Signer,
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
ttl: 900, // Optional: session TTL in seconds (default: 15 min)
});

console.log('Session address:', session.sessionAddress);
console.log('Expires at:', session.expiresAt);

Key properties of managed sessions:

  • session.signer -- The ephemeral EOA signer (used automatically by wrappers)
  • session.ensureValid() -- Checks expiry with 60s skew, auto-renews if needed
  • session.renew() -- Force-renew the session
  • session.isManaged -- Always true

Existing Session Key​

For advanced use cases (e.g. TEE web front) where a session was already created externally:

const session = web2.session.useExistingSessionKey({
sessionPrivateKey: '0xabc123...', // Hex-encoded secp256k1 private key
principalId: 'your-principal-uuid', // Optional
});
Cannot Renew

Sessions created with useExistingSessionKey cannot renew. Calling session.renew() will throw a Web2SessionError. The session must be recreated externally when it expires.

Verify Credentials (Web2 only)​

Use web2.auth.verifyCredentials() to confirm that a user entered the correct email and password. This is Web2 only (chainId = -1) — it is not available for Web3 wallet authentication.

This endpoint does not create a session or return session tokens. Use it when another system needs to validate credentials before proceeding (e.g. app unlock, key rotation pre-check).

import { web2 } from 'cifer-sdk';

// Web2 only — not for Web3 wallet users
const result = await web2.auth.verifyCredentials({
email: 'user@example.com',
password: 'securePassword123',
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

console.log('Valid:', result.valid); // true
console.log('Principal ID:', result.principalId);

// Wrong credentials throw Web2AuthError (401/403/404)

After credentials are confirmed, create a session separately with web2.session.createManagedSession().

Password Reset​

// Step 1: Request a reset OTP (60s cooldown)
await web2.auth.forgotPassword({
email: 'user@example.com',
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

// Step 2: Reset password with the OTP
await web2.auth.resetPassword({
email: 'user@example.com',
otp: '654321',
newPassword: 'newSecurePassword456',
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

Account Deletion​

Account deletion is a two-step, OTP-confirmed operation. These calls use the stateless web2.auth API; they are not methods on the client returned by web2.createClient().

Step 1: Request a deletion OTP​

Call web2.auth.requestAccountDeletion() with the account credentials and its registered principal ID.

const deletionRequest = await web2.auth.requestAccountDeletion({
email: 'user@example.com',
password: 'securePassword123',
principalId: 'your-principal-uuid',
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

console.log(deletionRequest.message);

For anti-enumeration, the Blackbox returns a generic success message. It sends an OTP only when the email, password, and principalId match a verified, active account.

Step 2: Confirm account deletion​

Call web2.auth.confirmAccountDeletion() with the emailed OTP.

const deletionResult = await web2.auth.confirmAccountDeletion({
email: 'user@example.com',
otp: '123456',
blackboxUrl: 'https://blackbox.cifersecurity.com:3010',
});

console.log('Deleted:', deletionResult.success);

Confirmation soft-deletes the account. The account becomes dormant and is hidden from APIs, but its records are retained for legal disclosure. Registering again with the same email reactivates the same principalId, including access to its existing secrets.

Error Handling​

import {
Web2Error,
Web2SessionError,
Web2AuthError,
isWeb2Error,
isWeb2SessionError,
isCiferError,
} from 'cifer-sdk';

try {
await web2.auth.register({ ... });
} catch (error) {
if (isWeb2SessionError(error)) {
// Session expired or cannot be renewed
console.log('Session error:', error.message);
// Re-create the session
} else if (error instanceof Web2AuthError) {
// Registration, OTP, or password error
console.log('Auth error:', error.message);
} else if (isWeb2Error(error)) {
// Generic Web2 error
console.log('Web2 error:', error.code, error.message);
} else if (isCiferError(error)) {
// Any other CIFER SDK error
console.log('CIFER error:', error.code, error.message);
}
}

Best Practices​

  1. Use the Web2 Client - Prefer web2.createClient() over individual stateless functions. The client stores your session and defaults, reducing boilerplate.
  2. Secure Your Ed25519 Key - Store Ed25519 private keys securely (e.g. environment variables, HSMs, or secret managers). Never expose them in client-side code.
  3. Use Managed Sessions - Prefer createManagedSession() over useExistingSessionKey() for automatic renewal and simpler lifecycle management.
  4. Let ensureValid() Handle Renewal - The web2.blackbox.* wrappers and the Web2 client call session.ensureValid() automatically before each request.
  5. Handle Node Registration Failures - After registerKey(), check nodeRegistrationStatus. If it's not 'complete', use retryNodeRegistration() to retry failed nodes.
  6. Respect Rate Limits - resendOtp() and forgotPassword() have a 60-second cooldown.
  7. Clear Local Account State After Deletion - Remove cached credentials, keys, and sessions after confirmAccountDeletion() succeeds. Keep the email only if your UI supports reactivation.

Next Steps​