Skip to main content

Session Management

This guide covers saving and restoring authenticated sessions in the Privacy Boost SDK.

Overview

Sessions contain the authentication state needed to use the SDK without re-authenticating. This includes:
  • Wallet address
  • JWT token
  • MPK (Master Public Key)
  • Token expiry time

Export Session

Save the current session:
const session = sdk.auth.exportSession();

// Store securely
localStorage.setItem('privacy_session', JSON.stringify(session));

Session Data Structure

interface ExportableSession {
  walletAddress: string;   // Connected wallet
  jwt: string;             // Authentication token
  jwtExpiry: number;       // Expiry timestamp
  mpk: string;             // Master public key
}

Import Session

Restore a saved session:
const savedSession = localStorage.getItem('privacy_session');

if (savedSession) {
  try {
    const session = JSON.parse(savedSession);
    await sdk.auth.importSession(session);
    console.log('Session restored');
  } catch (error) {
    console.log('Session invalid or expired');
    localStorage.removeItem('privacy_session');
  }
}

Check Session Validity

Before importing, verify the session is still valid:
const sessionInfo = await sdk.auth.getSessionInfo();

if (sessionInfo) {
  console.log('Valid:', sessionInfo.valid);
  console.log('Expires:', new Date(sessionInfo.expiresAt * 1000));
  console.log('Is Auditor:', sessionInfo.isAuditor);
}

Session Flow

┌─────────────────────────────────────────────────────────┐
│                    App Startup                           │
└────────────────────────┬────────────────────────────────┘


              ┌──────────────────────┐
              │  Check saved session │
              └──────────┬───────────┘

           ┌─────────────┴─────────────┐
           │                           │
           ▼                           ▼
   ┌───────────────┐           ┌───────────────┐
   │ Session found │           │ No session    │
   └───────┬───────┘           └───────┬───────┘
           │                           │
           ▼                           ▼
   ┌───────────────┐           ┌───────────────┐
   │ Import session│           │  Show login   │
   └───────┬───────┘           └───────────────┘

     ┌─────┴─────┐
     │           │
     ▼           ▼
┌─────────┐ ┌─────────┐
│ Valid   │ │ Invalid │
│ Ready!  │ │ Re-auth │
└─────────┘ └─────────┘

Complete Session Management Example

class SessionManager {
  private readonly storageKey = 'privacy_session';

  async initialize() {
    const session = this.load();
    if (!session) return false;

    try {
      await sdk.auth.importSession(session);
      const info = await sdk.auth.getSessionInfo();

      if (!info?.valid) {
        this.clear();
        return false;
      }

      return true;
    } catch {
      this.clear();
      return false;
    }
  }

  save() {
    const session = sdk.auth.exportSession();
    localStorage.setItem(this.storageKey, JSON.stringify(session));
  }

  load(): unknown | null {
    const data = localStorage.getItem(this.storageKey);
    return data ? JSON.parse(data) : null;
  }

  clear() {
    localStorage.removeItem(this.storageKey);
  }
}

// Usage
const sessionManager = new SessionManager();

async function initApp() {
  const restored = await sessionManager.initialize();

  if (restored) {
    console.log('Welcome back!');
  } else {
    // Show login UI
  }
}

// Save after login
await sdk.auth.login();
sessionManager.save();

// Clear on logout
await sdk.auth.logout();
sessionManager.clear();

Secure Storage

Browser (with encryption)

import CryptoJS from 'crypto-js';

class SecureSessionStorage {
  private readonly storageKey = 'privacy_session_encrypted';

  save(password: string) {
    const session = sdk.auth.exportSession();
    const encrypted = CryptoJS.AES.encrypt(
      JSON.stringify(session),
      password
    ).toString();
    localStorage.setItem(this.storageKey, encrypted);
  }

  load(password: string): unknown | null {
    const encrypted = localStorage.getItem(this.storageKey);
    if (!encrypted) return null;

    try {
      const decrypted = CryptoJS.AES.decrypt(encrypted, password);
      const json = decrypted.toString(CryptoJS.enc.Utf8);
      return JSON.parse(json);
    } catch {
      return null;
    }
  }

  clear() {
    localStorage.removeItem(this.storageKey);
  }
}

React Native

import * as Keychain from 'react-native-keychain';

class KeychainSessionStorage {
  private readonly service = 'privacy-boost';

  async save() {
    const session = sdk.auth.exportSession();
    await Keychain.setGenericPassword(
      'session',
      JSON.stringify(session),
      { service: this.service }
    );
  }

  async load(): Promise<unknown | null> {
    const credentials = await Keychain.getGenericPassword({
      service: this.service
    });

    if (!credentials) return null;
    return JSON.parse(credentials.password);
  }

  async clear() {
    await Keychain.resetGenericPassword({ service: this.service });
  }
}

Node.js

import fs from 'fs';
import path from 'path';
import os from 'os';

class FileSessionStorage {
  private readonly filePath: string;

  constructor() {
    const dir = path.join(os.homedir(), '.privacy-boost');
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { mode: 0o700 });
    }
    this.filePath = path.join(dir, 'session.json');
  }

  save() {
    const session = sdk.auth.exportSession();
    fs.writeFileSync(
      this.filePath,
      JSON.stringify(session),
      { mode: 0o600 }
    );
  }

  load(): unknown | null {
    if (!fs.existsSync(this.filePath)) return null;
    const data = fs.readFileSync(this.filePath, 'utf-8');
    return JSON.parse(data);
  }

  clear() {
    if (fs.existsSync(this.filePath)) {
      fs.unlinkSync(this.filePath);
    }
  }
}

Session Expiration Handling

class SessionExpirationHandler {
  private checkInterval: number | null = null;

  start(onExpired: () => void) {
    this.checkInterval = window.setInterval(async () => {
      const info = await sdk.auth.getSessionInfo();

      if (!info?.valid) {
        this.stop();
        onExpired();
      }
    }, 60000); // Check every minute
  }

  stop() {
    if (this.checkInterval) {
      clearInterval(this.checkInterval);
      this.checkInterval = null;
    }
  }
}

// Usage
const expirationHandler = new SessionExpirationHandler();

expirationHandler.start(() => {
  console.log('Session expired!');
  // Show re-login prompt
});

Auto-Refresh Sessions

async function withSessionRefresh<T>(
  operation: () => Promise<T>
): Promise<T> {
  try {
    return await operation();
  } catch (error) {
    if (error.code === 'SESSION_EXPIRED') {
      // Re-authenticate
      await sdk.auth.login();
      sessionManager.save();
      // Retry operation
      return await operation();
    }
    throw error;
  }
}

// Usage
const balance = await withSessionRefresh(() =>
  sdk.vault.getBalance('0x...')
);

Best Practices

1. Check Session on App Start

useEffect(() => {
  async function checkSession() {
    const restored = await sessionManager.initialize();
    if (!restored) {
      navigate('/login');
    }
  }
  checkSession();
}, []);

2. Save Session After Login

async function handleLogin() {
  await sdk.auth.connect(adapter);
  await sdk.auth.login();
  sessionManager.save();
}

3. Clear Session on Logout

async function handleLogout() {
  await sdk.auth.logout();
  sessionManager.clear();
  navigate('/login');
}

4. Handle Account Changes

window.ethereum?.on('accountsChanged', async (accounts: string[]) => {
  const currentAddress = sdk.auth.getAddress();

  if (accounts[0]?.toLowerCase() !== currentAddress?.toLowerCase()) {
    sessionManager.clear();
    navigate('/login');
  }
});

Next Steps