Skip to content

Authentication ​

Vortum supports multiple authentication methods, allowing users to connect with their preferred blockchain wallet or identity provider.

Supported Authentication Methods ​

ICP Identity Providers ​

ProviderDescription
Internet IdentityICP's native decentralized identity with biometric/device authentication
NFIDEmail-based identity with Internet Identity compatibility
OISYICP wallet with transaction signing capabilities

Blockchain Wallets ​

BlockchainWalletsProtocol
SolanaPhantom, SolflareSign-In with Solana (SIWS)
BitcoinPhantom, UnisatSign-In with Bitcoin (SIWB)

Blockchain wallet authentication uses a "Sign-In with X" (SIWx) pattern - users sign a message with their wallet to prove ownership, and Vortum creates a delegated ICP identity linked to that wallet address.

ICP Identity Providers ​

Internet Identity ​

typescript
import { VortumClient } from '@vortum/sdk'

const client = await VortumClient.create()

// Login with Internet Identity
await client.auth.loginWithII()

// Check authentication
if (client.isAuthenticated()) {
  console.log('Principal:', client.getPrincipal())
}

// Logout
await client.auth.logout()

NFID ​

typescript
// Login with NFID (email-based)
await client.auth.loginWithNFID()

OISY Wallet ​

typescript
// Login with OISY wallet
await client.auth.loginWithOISY()

Blockchain Wallet Authentication ​

Blockchain wallet authentication is handled in the webapp through the Sign-In with Solana (SIWS) and Sign-In with Bitcoin (SIWB) protocols.

How It Works ​

  1. Connect Wallet: User connects their Solana or Bitcoin wallet (e.g., Phantom, Solflare, Unisat)
  2. Sign Message: The wallet signs a verification message proving ownership
  3. Create Delegation: Vortum creates a delegated ICP identity linked to the wallet address
  4. Session Established: User can now interact with the platform using their wallet identity

Solana Wallets ​

Supported wallets:

  • Phantom - Popular multi-chain wallet
  • Solflare - Solana-native wallet

Bitcoin Wallets ​

Supported wallets:

  • Phantom - Uses BIP-322 message signing
  • Unisat - Uses ECDSA message signing

Session Persistence ​

Sessions are automatically persisted. Check for existing session on page load:

typescript
const client = await VortumClient.create()

// Initialize auth (restores existing session)
await client.auth.initAuth()

if (client.isAuthenticated()) {
  // User is already logged in
  console.log('Welcome back!')
} else {
  // Show login UI
}

State Subscription ​

React to authentication changes:

typescript
const unsubscribe = client.subscribe(() => {
  if (client.isAuthenticated()) {
    console.log('Logged in:', client.getPrincipal())
  } else {
    console.log('Logged out')
  }
})

Error Handling ​

typescript
try {
  await client.auth.loginWithII()
} catch (error) {
  console.error('Login failed:', error)
}

// Or check error state
const error = client.auth.getError()
if (error) {
  console.error('Auth error:', error)
}

// Check if connecting
if (client.auth.isConnecting()) {
  console.log('Authentication in progress...')
}

Account Creation ​

After authentication, create or get your account:

typescript
await client.auth.loginWithII()

// Get existing account
let account = await client.account.get()

if (!account) {
  // Create new account
  await client.account.create({ name: 'Trader' })
  account = await client.account.get()
}

console.log('Account:', account)

Two-Factor Authentication (TOTP) ​

Enable TOTP-based 2FA for enhanced security. Vortum uses a Merkle tree commitment scheme - the server never stores your TOTP secret, only a cryptographic commitment.

Check 2FA Status ​

typescript
const status = await client.totp.getStatus()
console.log('2FA enabled:', status.enabled)
console.log('Account locked:', status.isLocked)
console.log('Recent verification:', status.hasRecentVerification)

Enable 2FA ​

typescript
if (!status.enabled) {
  // Step 1: Start setup - get encrypted secret
  const setup = await client.totp.startSetup()

  // Step 2: Decrypt and display QR code to user
  // (requires client-side VetKD decryption)

  // Step 3: Confirm with TOTP code and Merkle proof
  await client.totp.confirmSetup({
    code: totpCode,
    commitment: merkleRoot,
    treeStartWindow: currentWindow,
    treeSize: treeSize
  })
}

Verify 2FA ​

typescript
// Verify 2FA for sensitive operations (withdrawals, settings changes)
await client.totp.verify({
  code: totpCode,
  proof: merkleProof
})

Tree Refresh ​

The Merkle tree needs periodic refresh (every ~30 days):

typescript
if (status.needsTreeRefresh) {
  await client.totp.refreshTree({
    currentCode: totpCode,
    currentProof: currentProof,
    newCommitment: newMerkleRoot,
    newTreeStartWindow: newWindow,
    newTreeSize: newSize
  })
}

Security Considerations ​

  • Non-custodial: Your keys never leave your wallet
  • Delegated identity: Blockchain wallet logins create ICP delegations, not direct key access
  • Session expiry: Sessions expire after 7 days for security
  • 2FA protection: Enable TOTP for withdrawals and sensitive operations
  • Merkle commitments: 2FA uses zero-knowledge proofs - server never sees your TOTP secret