Authentication
Vortum supports multiple authentication methods, allowing users to connect with their preferred blockchain wallet or identity provider.
Supported Authentication Methods
ICP Identity Providers
| Provider | Description |
|---|---|
| Internet Identity | ICP's native decentralized identity with biometric/device authentication |
| NFID | Email-based identity with Internet Identity compatibility |
| OISY | ICP wallet with transaction signing capabilities |
Blockchain Wallets
| Blockchain | Wallets | Protocol |
|---|---|---|
| Solana | Phantom, Solflare | Sign-In with Solana (SIWS) |
| Bitcoin | Phantom, Unisat | Sign-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
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
// Login with NFID (email-based)
await client.auth.loginWithNFID()OISY Wallet
// 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
- Connect Wallet: User connects their Solana or Bitcoin wallet (e.g., Phantom, Solflare, Unisat)
- Sign Message: The wallet signs a verification message proving ownership
- Create Delegation: Vortum creates a delegated ICP identity linked to the wallet address
- 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:
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:
const unsubscribe = client.subscribe(() => {
if (client.isAuthenticated()) {
console.log('Logged in:', client.getPrincipal())
} else {
console.log('Logged out')
}
})Error Handling
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:
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
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
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
// 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):
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