Frontend Guide
Building the Browser-Side Certification Interface with BSV SDK
This guide covers frontend patterns for implementing certificate acquisition, wallet integration, and authenticated access to protected content using WalletClient and AuthFetch.
Complete Code Repository: https://github.com/bsv-blockchain-demos/certification-platform
All code examples in this guide are taken from the working implementation in the
frontend/directory.
Table of Contents
Setup
Installation
Environment Configuration
Create .env.local:
Note: The
NEXT_PUBLIC_prefix exposes this variable to the browser. The backend must be running on the configured port.
Project Structure
Wallet Integration
Pattern 1: WalletClient React Context
WalletClient connects to the user's BSV wallet extension. A React context provides wallet access across the entire application:
See full implementation:
frontend/src/context/WalletContext.tsx
Key Points:
WalletClient()auto-discovers the browser wallet extension (no constructor arguments)getPublicKey({ identityKey: true })returns the user's 33-byte compressed public keyThe identity key is used as the certificate
subjectand for authenticationNo private keys leave the wallet extension — all signing happens inside it
Mount the Provider in the root layout:
Reference: WalletClient Integration
Certifier Discovery
Pattern 2: Fetching Certifier Info
Before acquiring certificates, the frontend must discover the server's identity (certifier public key) and certificate type:
Why Discovery?
The certifier public key is needed to filter certificates in the wallet
The certificate type identifies what kind of certificate to look for
Both values come from the server's private key and are deterministic
Checking for Existing Certificates
After discovering the certifier, check if the user already has a certificate:
listCertificates Parameters:
certifiers
string[]
Filter by certifier public keys
types
string[]
Filter by certificate type identifiers
limit
number
Maximum certificates to return
Reference: WalletClient.listCertificates
Certificate Acquisition
Pattern 3: Direct Acquisition Protocol
The core issuance flow: request a certificate from the server, then store it in the wallet using the direct protocol.
See full implementation:
frontend/src/app/page.tsx
Step 1: Request Certificate from Server
The server returns a complete, signed, encrypted certificate:
Step 2: Store in Wallet
acquireCertificate Parameters:
type
"Y2VydGlmaWNhdGlvbg=="
Certificate type identifier
certifier
Server's public key
Who signed this certificate
acquisitionProtocol
'direct'
Skip external handshake, store directly
fields
Encrypted field values
{ certified: "<enc>", timestamp: "<enc>" }
serialNumber
Unique hex string
Certificate instance ID
revocationOutpoint
"000...0.0"
Revocation UTXO pointer
signature
ECDSA hex
Certifier's signature over the certificate
keyringRevealer
'certifier'
Tells wallet the keyring was encrypted by the certifier
keyringForSubject
Encrypted keys per field
Lets the subject decrypt field values
Why 'direct' Protocol?
'direct' Protocol?The BSV SDK supports two acquisition protocols:
'issuance'
Client ↔ external certifier URL handshake
Third-party certifier
'direct'
Client stores pre-signed cert data
Your server IS the certifier
Since this platform's backend signs the certificate itself, the data is already complete. No external negotiation is needed.
Why keyringRevealer: 'certifier'?
keyringRevealer: 'certifier'?This tells the wallet that the keyring entries were encrypted by the certifier's key. The wallet uses the certifier's public key as the ECDH counterparty to recover the shared secrets and decrypt field values.
Handling Re-certification
Before acquiring a new certificate, remove any existing ones to prevent duplicates:
Certificate Revocation
Pattern 4: Removing Certificates
Users can revoke their certificate, removing it from the wallet:
The three-part key (type, serialNumber, certifier) uniquely identifies a certificate in the wallet.
Complete Revocation also notifies the server to clean up sessions:
When the user subsequently tries to access the protected dashboard, AuthFetch will fail the auth challenge (no certificate to present), and the server-side session will eventually be cleaned up.
Authenticated Dashboard
Pattern 5: AuthFetch for Gated Content
AuthFetch from @bsv/sdk wraps the standard fetch() API and handles the mutual authentication protocol transparently:
See full implementation:
frontend/src/app/dashboard/page.tsx
What AuthFetch Does Internally:
Key Difference from Plain fetch():
Feature
fetch()
AuthFetch.fetch()
Authentication
None
Automatic mutual auth
Certificate presentation
Manual
Automatic from wallet
Session management
Manual
Automatic caching
Use for public endpoints
Yes
Unnecessary overhead
Use for protected endpoints
Cannot work
Required
Use plain fetch() for public endpoints (/api/info, /api/certify) and AuthFetch for protected endpoints (/protected/dashboard).
Dashboard Component
Complete Flow
End-to-End Certificate Acquisition and Access
Important Concepts
WalletClient Methods Used
getPublicKey({ identityKey: true })
Get user's identity
App initialization
listCertificates({ certifiers, types })
Check existing certs
Page mount
acquireCertificate({ ... })
Store new certificate
After server issuance
relinquishCertificate({ ... })
Remove certificate
User revocation
AuthFetch vs Plain Fetch
Certificate Uniqueness
A certificate is uniquely identified by three fields:
type — the base64-encoded certificate category
serialNumber — unique instance identifier
certifier — public key of the signing authority
Error Handling
Always handle wallet and network errors gracefully:
Common errors:
"Wallet not connected" — WalletClient not initialized
"Certificate already exists" — duplicate serial number
"Invalid signature" — certificate data was tampered with
"Network error" — backend not reachable
Summary
This frontend guide covered:
WalletClient Context — React provider for wallet access across components
Certifier Discovery — Fetching server identity and certificate type
Certificate Acquisition — Direct protocol for storing pre-signed certificates
Certificate Revocation — Removing certificates from the wallet
AuthFetch — Transparent mutual authentication for protected routes
Complete Integration — End-to-end flow from discovery to gated access
These patterns form the foundation for building certificate-gated frontends on BSV.
Next Steps
Backend Server Guide — Learn how the server issues and verifies certificates
WalletClient Integration — WalletClient fundamentals
BSV SDK Documentation — Full SDK reference
BRC-56: Wallet Wire Protocol — How WalletClient communicates with extensions
Last updated
