ts-sdk

Identity Management and Certificates

Duration: 90 minutes
Prerequisites: Node.js, basic TypeScript knowledge, understanding of digital certificates
Learning Goals:

Introduction

The BSV ecosystem uses a decentralized identity system based on cryptographic certificates. The IdentityClient provides tools for managing identity certificates, verifying identities, and building identity-aware applications.

Key Concepts

What You’ll Build

Setting Up Identity Management with WalletClient

Basic Identity Operations

import { IdentityClient, WalletClient } from '@bsv/sdk'

async function basicIdentityOperations() {
  const wallet = new WalletClient()
  const identityClient = new IdentityClient(wallet)
  
  console.log('Identity client initialized')
  
  // Get our identity key
  const { publicKey: identityKey } = await wallet.getPublicKey({
    identityKey: true
  })
  
  console.log('Our identity key:', identityKey.substring(0, 20) + '...')
  
  return { identityClient, identityKey }
}

basicIdentityOperations().catch(console.error)

Resolving Identities by Key

import { IdentityClient, WalletClient } from '@bsv/sdk'

async function resolveIdentityByKey(identityKey: string) {
  const identityClient = new IdentityClient()
  
  try {
    console.log('Resolving identity for key:', identityKey.substring(0, 20) + '...')
    
    const identities = await identityClient.resolveByIdentityKey({
      identityKey
    })
    
    console.log('Found identities:', identities.length)
    
    identities.forEach((identity, index) => {
      console.log(`Identity ${index + 1}:`)
      console.log('  Name:', identity.name)
      console.log('  Badge:', identity.badgeLabel)
      console.log('  Avatar:', identity.avatarURL)
      console.log('  Key:', identity.abbreviatedKey)
    })
    
    return identities
  } catch (error) {
    console.error('Failed to resolve identity:', error)
    return []
  }
}

// Example usage (replace with actual identity key)
// resolveIdentityByKey('actual-identity-key-here').catch(console.error)

Resolving Identities by Attributes

import { IdentityClient } from '@bsv/sdk'

async function resolveIdentityByAttributes() {
  const identityClient = new IdentityClient()
  
  try {
    console.log('Searching for identities by attributes...')
    
    const identities = await identityClient.resolveByAttributes({
      attributes: {
        email: 'user@example.com'
      }
    })
    
    console.log('Found identities with matching attributes:', identities.length)
    
    identities.forEach((identity, index) => {
      console.log(`Match ${index + 1}:`)
      console.log('  Name:', identity.name)
      console.log('  Badge:', identity.badgeLabel)
      console.log('  Identity Key:', identity.identityKey.substring(0, 20) + '...')
    })
    
    return identities
  } catch (error) {
    console.error('Failed to resolve by attributes:', error)
    return []
  }
}

resolveIdentityByAttributes().catch(console.error)

Certificate Management System

Identity Manager Class

import { IdentityClient, WalletClient } from '@bsv/sdk'

interface IdentityRecord {
  identityKey: string
  name: string
  badgeLabel: string
  avatarURL: string
  verificationStatus: 'verified' | 'unverified' | 'pending'
  certificates: any[]
  lastUpdated: Date
}

class IdentityManager {
  private identityClient: IdentityClient
  private wallet: WalletClient
  private identityCache: Map<string, IdentityRecord> = new Map()
  
  constructor(wallet?: WalletClient) {
    this.wallet = wallet || new WalletClient()
    this.identityClient = new IdentityClient(this.wallet)
  }
  
  async getOurIdentity(): Promise<string> {
    const { publicKey } = await this.wallet.getPublicKey({
      identityKey: true
    })
    return publicKey
  }
  
  async verifyIdentity(identityKey: string): Promise<IdentityRecord | null> {
    try {
      console.log('Verifying identity:', identityKey.substring(0, 20) + '...')
      
      // Check cache first
      const cached = this.identityCache.get(identityKey)
      if (cached && this.isCacheValid(cached)) {
        console.log('Using cached identity data')
        return cached
      }
      
      // Resolve identity from network
      const identities = await this.identityClient.resolveByIdentityKey({
        identityKey
      })
      
      if (identities.length === 0) {
        console.log('No identity found for key')
        return null
      }
      
      // Use the first identity found
      const identity = identities[0]
      
      const record: IdentityRecord = {
        identityKey,
        name: identity.name,
        badgeLabel: identity.badgeLabel,
        avatarURL: identity.avatarURL,
        verificationStatus: 'verified',
        certificates: [], // Would contain actual certificates
        lastUpdated: new Date()
      }
      
      // Cache the result
      this.identityCache.set(identityKey, record)
      
      console.log('Identity verified:', record.name)
      return record
    } catch (error) {
      console.error('Identity verification failed:', error)
      return null
    }
  }
  
  async searchIdentities(query: {
    name?: string
    email?: string
    attributes?: Record<string, string>
  }): Promise<IdentityRecord[]> {
    try {
      console.log('Searching identities with query:', query)
      
      const searchAttributes = {
        ...query.attributes,
        ...(query.name && { name: query.name }),
        ...(query.email && { email: query.email })
      }
      
      const identities = await this.identityClient.resolveByAttributes({
        attributes: searchAttributes
      })
      
      const records: IdentityRecord[] = identities.map(identity => ({
        identityKey: identity.identityKey,
        name: identity.name,
        badgeLabel: identity.badgeLabel,
        avatarURL: identity.avatarURL,
        verificationStatus: 'verified' as const,
        certificates: [],
        lastUpdated: new Date()
      }))
      
      console.log('Found', records.length, 'matching identities')
      return records
    } catch (error) {
      console.error('Identity search failed:', error)
      return []
    }
  }
  
  async revealPublicAttributes(
    certificate: any,
    fieldsToReveal: string[]
  ): Promise<boolean> {
    try {
      console.log('Revealing public attributes:', fieldsToReveal)
      
      const result = await this.identityClient.publiclyRevealAttributes(
        certificate,
        fieldsToReveal
      )
      
      console.log('Attributes revealed successfully')
      return true
    } catch (error) {
      console.error('Failed to reveal attributes:', error)
      return false
    }
  }
  
  private isCacheValid(record: IdentityRecord): boolean {
    const cacheAge = Date.now() - record.lastUpdated.getTime()
    const maxAge = 60 * 60 * 1000 // 1 hour
    return cacheAge < maxAge
  }
  
  getCachedIdentities(): IdentityRecord[] {
    return Array.from(this.identityCache.values())
  }
  
  clearCache(): void {
    this.identityCache.clear()
    console.log('Identity cache cleared')
  }
}

async function demonstrateIdentityManager() {
  const identityManager = new IdentityManager()
  
  console.log('=== Identity Manager Demo ===')
  
  // Get our own identity
  const ourIdentity = await identityManager.getOurIdentity()
  console.log('Our identity key:', ourIdentity.substring(0, 20) + '...')
  
  // Search for identities
  const searchResults = await identityManager.searchIdentities({
    name: 'John'
  })
  
  console.log('Search results:', searchResults.length)
  
  // Verify specific identities
  for (const result of searchResults.slice(0, 2)) { // Limit to first 2
    const verified = await identityManager.verifyIdentity(result.identityKey)
    if (verified) {
      console.log('Verified identity:', verified.name)
    }
  }
  
  // Show cached identities
  const cached = identityManager.getCachedIdentities()
  console.log('Cached identities:', cached.length)
  
  return { ourIdentity, searchResults, cached }
}

demonstrateIdentityManager().catch(console.error)

Identity-Based Authentication

Authentication Service

import { IdentityClient, WalletClient } from '@bsv/sdk'

interface AuthenticationResult {
  success: boolean
  identityKey?: string
  identity?: any
  error?: string
}

class IdentityAuthService {
  private identityClient: IdentityClient
  private authenticatedUsers: Map<string, {
    identity: any
    sessionStart: Date
    lastActivity: Date
  }> = new Map()
  
  constructor(wallet?: WalletClient) {
    this.identityClient = new IdentityClient(wallet)
  }
  
  async authenticateUser(identityKey: string): Promise<AuthenticationResult> {
    try {
      console.log('Authenticating user:', identityKey.substring(0, 20) + '...')
      
      // Resolve identity
      const identities = await this.identityClient.resolveByIdentityKey({
        identityKey
      })
      
      if (identities.length === 0) {
        return {
          success: false,
          error: 'Identity not found'
        }
      }
      
      const identity = identities[0]
      
      // Create session
      this.authenticatedUsers.set(identityKey, {
        identity,
        sessionStart: new Date(),
        lastActivity: new Date()
      })
      
      console.log('User authenticated:', identity.name)
      
      return {
        success: true,
        identityKey,
        identity
      }
    } catch (error) {
      console.error('Authentication failed:', error)
      return {
        success: false,
        error: error.message
      }
    }
  }
  
  async verifySession(identityKey: string): Promise<boolean> {
    const session = this.authenticatedUsers.get(identityKey)
    
    if (!session) {
      return false
    }
    
    // Check session timeout (24 hours)
    const sessionAge = Date.now() - session.sessionStart.getTime()
    const maxAge = 24 * 60 * 60 * 1000
    
    if (sessionAge > maxAge) {
      this.authenticatedUsers.delete(identityKey)
      return false
    }
    
    // Update last activity
    session.lastActivity = new Date()
    return true
  }
  
  async requireAuthentication(identityKey: string): Promise<{
    authorized: boolean
    identity?: any
    error?: string
  }> {
    const isValid = await this.verifySession(identityKey)
    
    if (!isValid) {
      return {
        authorized: false,
        error: 'Authentication required'
      }
    }
    
    const session = this.authenticatedUsers.get(identityKey)
    return {
      authorized: true,
      identity: session?.identity
    }
  }
  
  logout(identityKey: string): void {
    this.authenticatedUsers.delete(identityKey)
    console.log('User logged out:', identityKey.substring(0, 20) + '...')
  }
  
  getActiveSessions(): Array<{
    identityKey: string
    name: string
    sessionStart: Date
    lastActivity: Date
  }> {
    return Array.from(this.authenticatedUsers.entries()).map(([key, session]) => ({
      identityKey: key,
      name: session.identity.name,
      sessionStart: session.sessionStart,
      lastActivity: session.lastActivity
    }))
  }
}

async function demonstrateAuthentication() {
  const authService = new IdentityAuthService()
  
  console.log('=== Identity Authentication Demo ===')
  
  // Simulate user authentication
  const wallet = new WalletClient()
  const { publicKey: userIdentity } = await wallet.getPublicKey({
    identityKey: true
  })
  
  // Authenticate user
  const authResult = await authService.authenticateUser(userIdentity)
  
  if (authResult.success) {
    console.log('✅ Authentication successful')
    console.log('User:', authResult.identity?.name || 'Unknown')
    
    // Verify session
    const sessionValid = await authService.verifySession(userIdentity)
    console.log('Session valid:', sessionValid)
    
    // Test authorization
    const authCheck = await authService.requireAuthentication(userIdentity)
    console.log('Authorization check:', authCheck.authorized ? 'PASSED' : 'FAILED')
    
    // Show active sessions
    const sessions = authService.getActiveSessions()
    console.log('Active sessions:', sessions.length)
    
    // Logout
    authService.logout(userIdentity)
    console.log('User logged out')
    
  } else {
    console.log('❌ Authentication failed:', authResult.error)
  }
  
  return authResult
}

demonstrateAuthentication().catch(console.error)

Identity Verification Service

Complete Verification System

```typescript import { IdentityClient, WalletClient } from ‘@bsv/sdk’

interface VerificationRequest { identityKey: string requiredCertificates: string[] purpose: string timestamp: Date }

interface VerificationResult { verified: boolean identityKey: string certificates: any[] trustScore: number issues: string[] }

class IdentityVerificationService { private identityClient: IdentityClient private verificationHistory: Map<string, VerificationResult[]> = new Map()

constructor(wallet?: WalletClient) { this.identityClient = new IdentityClient(wallet) }

async verifyIdentity(request: VerificationRequest): Promise { console.log('Starting identity verification for:', request.identityKey.substring(0, 20) + '...') console.log('Purpose:', request.purpose)

const result: VerificationResult = {
  verified: false,
  identityKey: request.identityKey,
  certificates: [],
  trustScore: 0,
  issues: []
}

try {
  // Resolve identity
  const identities = await this.identityClient.resolveByIdentityKey({
    identityKey: request.identityKey
  })
  
  if (identities.length === 0) {
    result.issues.push('Identity not found')
    return result
  }
  
  const identity = identities[0]
  
  // Calculate trust score based on available information
  result.trustScore = this.calculateTrustScore(identity)
  
  // Check if verification meets requirements
  if (result.trustScore >= 70) { // Minimum trust score
    result.verified = true
    console.log('✅ Identity verified:', identity.name)
  } else {
    result.issues.push('Insufficient trust score')
    console.log('❌ Identity verification failed: low trust score')
  }
  
  // Store verification history
  const history = this.verificationHistory.get(request.identityKey) || []
  history.push(result)
  this.verificationHistory.set(request.identityKey, history)
  
} catch (error) {
  result.issues.push(`Verification error: ${error.message}`)
  console.error('Verification error:', error)
}

return result   }

private calculateTrustScore(identity: any): number { let score = 0

// Base score for having an identity
score += 30

// Score for having a name
if (identity.name && identity.name !== 'Unknown') {
  score += 20
}

// Score for having an avatar
if (identity.avatarURL) {
  score += 10
}

// Score for having a badge
if (identity.badgeLabel) {
  score += 20
}

// Additional score for verified badge
if (identity.badgeLabel?.includes('Verified')) {
  score += 20
}

return Math.min(score, 100) // Cap at 100   }

async batchVerify(identityKeys: string[], purpose: string): Promise<{ verified: VerificationResult[] failed: VerificationResult[] }> { console.log(Starting batch verification of ${identityKeys.length} identities)

const requests = identityKeys.map(key => ({
  identityKey: key,
  requiredCertificates: [],
  purpose,
  timestamp: new Date()
}))

const results = await Promise.all(
  requests.map(request => this.verifyIdentity(request))
)

const verified = results.filter(r => r.verified)
const failed = results.filter(r => !r.verified)

console.log(`Batch verification complete: ${verified.length} verified, ${failed.length} failed`)

return { verified, failed }   }

getVerificationHistory(identityKey: string): VerificationResult[] { return this.verificationHistory.get(identityKey) || [] }

getTrustStatistics(): { totalVerifications: number successRate: number averageTrustScore: number } { let totalVerifications = 0 let successfulVerifications = 0 let totalTrustScore = 0

for (const history of this.verificationHistory.values()) {
  for (const result of history) {
    totalVerifications++
    totalTrustScore += result.trustScore
    
    if (result.verified) {
      successfulVerifications++
    }
  }
}

return {
  totalVerifications,
  successRate: totalVerifications > 0 ? successfulVerifications / totalVerifications : 0,
  averageTrustScore: totalVerifications > 0 ? totalTrustScore / totalVerifications : 0
}   } }

async function demonstrateVerificationService() { const verificationService = new IdentityVerificationService()

console.log(‘=== Identity Verification Service Demo ===’)

// Create test identities const wallet1 = new WalletClient() const wallet2 = new WalletClient()

const { publicKey: identity1 } = await wallet1.getPublicKey({ identityKey: true }) const { publicKey: identity2 } = await wallet2.getPublicKey({ identityKey: true })

const testIdentities = [identity1, identity2]

// Batch verification const batchResult = await verificationService.batchVerify( testIdentities, ‘User registration verification’ )

console.log(‘Batch verification results:’) console.log(‘ Verified:’, batchResult.verified.length) console.log(‘ Failed:’, batchResult.failed.length)

// Individual verification with details for (const identity of testIdentities.slice(0, 1)) { // Just first one const request: VerificationRequest = { identityKey: identity, requiredCertificates: [‘identity’], purpose: ‘Account access verification’, timestamp: new Date() }

const result = await verificationService.verifyIdentity(request)

console.log(`\nVerification for ${identity.substring(0, 20)}...`)
console.log('  Verified:', result.verified)
console.log('  Trust Score:', result.trustScore)
console.log('  Issues:', result.issues)   }

// Get statistics const stats = verificationService.getTrustStatistics() console.log(‘\nVerification Statistics:’) console.log(‘ Total Verifications:’, stats.totalVerifications) console.log(‘ Success Rate:’, (stats.successRate * 100).toFixed(1) + ‘%’) console.log(‘ Average Trust Score:’, stats.averageTrustScore.toFixed(1))

return { batchResult, stats } }

demonstrateVerificationService().catch(console.error)