Skip to main content

Example Repository

A complete Next.js implementation showing this integration approach.
This guide covers integrating Sonar with a backend that stores OAuth tokens server-side. All Sonar API calls are made via server actions, so tokens never reach the browser.
For SPAs without a backend, see the simpler Frontend-only approach.

Backend access model

There is no server-side, shared, or dev API key. Every entity-scoped Sonar API call is authenticated with the end user’s OAuth access token. This is the single most common misunderstanding when building a backend against Sonar.
Entity-scoped data can only be read with the token of the user it belongs to. This includes:
  • Registration and KYC status
  • Eligibility and pre-purchase checks
  • Entity information
  • Generating purchase permits
Because there is no shared key, your backend must persist each user’s OAuth token and refresh it when it expires. See Token storage for persistence and Defining server actions for the refresh flow (the example app demonstrates refreshing expired tokens).

What needs a token, and what doesn’t

Committed amounts live on the sale contract and can be read directly from the chain without any token. See Reading commitment data. Registration, KYC, and eligibility status are only available through the authenticated Sonar API, so they must be fetched per user with that user’s (refreshed) token. See Authentication for how tokens are obtained.

How it works

Installation

Note: Unlike the frontend-only approach, you don’t need @echoxyz/sonar-react since you’ll be making API calls through your backend.

Example storage interfaces

Token storage

You’ll need a way to persist OAuth tokens on the server, keyed by user ID. In production, you should use a database. For demonstration purposes, the example below uses in-memory storage.
See the example app token-store.ts for the full implementation.

PKCE verifier storage

During the OAuth flow, you need to store the PKCE code verifier between the initial redirect and the callback. This must be stored server-side and associated with the OAuth state parameter, along with the user ID to verify session consistency.
See the example app pkce-store.ts for the full implementation with expiry handling.

Implementing the OAuth flow

The OAuth flow uses two server actions: one to generate the authorization URL, and one to handle the callback.
The createSonarClient helper used below is defined in the Server actions section.

Starting the OAuth flow

When the user clicks “Connect with Sonar”, a server action generates the authorization URL with PKCE parameters:

Handling the OAuth callback

After the user authenticates with Echo, they’re redirected to a callback page. This page extracts the code and state parameters and calls a server action to complete the flow:
The callback page itself is a client component that calls the server action:
See the example app auth.ts for the full implementation.

Server actions for Sonar API requests

All Sonar API requests go through server actions so that access tokens are never exposed to the browser.

Sonar client factory

Create helper functions to instantiate and refresh the Sonar client:

Defining server actions

Create a createSonarServerAction wrapper that handles session authentication and automatic token refresh:
With the wrapper in place, each server action is simple to define:
See the example app sonar.ts for the full implementation.

Frontend implementation

With the server actions in place, the client-side components can be implemented as follows:

Authentication button

The auth button calls the getSonarAuthorizationUrl server action and redirects to the returned URL:

Fetching the state of a user’s Sonar entities

For the other Sonar API calls, we recommend wrapping these in React hooks. Then these hooks can be called in a very similar way to if you were calling the Sonar API directly via the @echoxyz/sonar-react library. See the frontend-only guide for an example.

Running pre-purchase checks and generating purchase permits

Also create a hook for the purchase flow:

Submitting the purchase transaction

Once you have a purchase permit, submit it to your sale contract. This is the same as the frontend-only approach since the contract interaction happens directly from the browser—see the frontend-only guide for the full implementation.