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-platformarrow-up-right

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 key

  • The identity key is used as the certificate subject and for authentication

  • No 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:

Parameter
Type
Purpose

certifiers

string[]

Filter by certifier public keys

types

string[]

Filter by certificate type identifiers

limit

number

Maximum certificates to return

Reference: WalletClient.listCertificatesarrow-up-right


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:

Parameter
Value
Purpose

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?

The BSV SDK supports two acquisition protocols:

Protocol
Flow
When to Use

'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'?

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

Method
Purpose
When Called

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

Last updated