bonuz SDK for Login

Your current login is a line item.
Ours is infrastructure.

Replace your app login in two days. Users still tap Continue with Google, Apple, or email.

In the background they get a self-custodial wallet and a portable on-chain identity. 1,000 users free, gas sponsored, no lock-in.

Integration guide

Integrate bonuz login.

Everything starts in the developer hub at app.bonuz.market/developers: create a project, configure it, copy the snippet, go live. This guide walks the same steps and covers what the hub does not: events, the token check on your server, CSP and WordPress.

  1. 1

    Create a project

    Start in the developer hub.

    Sign in to app.bonuz.market, open Developers and pick the brand the app belongs to (no brand yet: create one first). Press New project and fill in three things: the App name people see on the sign-in screen, the Website it runs on (optional now), and the Platform: Website / web app, Mobile app, or both. You land on the project with its key, bz_…. The key is public and only says which app is calling; the sites under Domains are what protect it. It is not a Web3Auth client id: every bonuz app shares bonuz's one Web3Auth project, so you never need one.

  2. 2

    Domains

    Add every site you sign in from.

    In the project, open Domains and add each origin: https, host and port, paths do not matter. That includes http://localhost:3000 with the exact port you run on. Local addresses are approved the moment you add them; bonuz enables a real domain by hand, usually within a day. Until a site is approved, sign-in on it shows a blocker message instead of the login flow. Mobile apps send no site and use the project key alone.

  3. 3

    Sign-in, menu, wallet

    Choose what people see.

    Everything here is a setting, not code. The SDK fetches it on page load, so a change reaches your app without a redeploy.

    Sign-in

    Methods and their order, app name, logo, accent colour, theme, terms and privacy links, with a live preview.

    Profile menu

    What people see when they tap their avatar: built-in rows, your own links, the order.

    Wallet

    Wallet window on or off, send, fund, custom tokens, test networks, which chain it opens on.

    Settings

    Project name, platform, key, archive.

    Sign-in methods are email (a one-time code), Google and Apple. Each one creates the same bonuz account and wallet.

  4. 4

    Copy the snippet

    The code the Overview tab hands you.

    Open Overview in your project and copy the snippet for your stack: the key is already filled in there. Below is the same code with bz_… in its place. Nothing to install for a website: the widget loads from bonuz.id and renders the button inside the div, in a shadow DOM, so your CSS and ours never collide. For React, LoginModal comes from @bonuz/sdk/auth/ui; pair it with ConnectButton, because a ConnectButton alone signs in with Google only. The @bonuz/sdk on npm today is an unrelated 0.1.0; 2.x ships inside the bonuz monorepo until it is published.

    WordPress (as a Custom HTML block), Webflow, Shopify, plain HTML: paste and done.

    index.html
    <!-- bonuz login: paste where the sign-in button should appear (WordPress: in a Custom HTML block) -->
    <div data-bonuz-login></div>
    <script type="module" src="https://bonuz.id/sdk/v1/bonuz.js" data-project="bz_…"></script>
    
    <script>
      // window.bonuz exists as soon as the script starts. bonuz:ready says the widget is up, not that
      // anyone is signed in; the user arrives with bonuz:login (on sign-in, and when a saved session is restored)
      window.addEventListener("bonuz:ready", (e) => console.log("bonuz ready for", e.detail.project));
      window.addEventListener("bonuz:login", (e) => console.log("signed in", e.detail.user)); // {id, handle, name, walletAddress}
      window.addEventListener("bonuz:logout", () => console.log("signed out"));
    
      // For your own API: a fresh token, refreshed if it was about to expire
      async function callMyApi() {
        const token = await window.bonuz.getFreshToken(); // waits for a restoring session; null when nobody is signed in
        return fetch("/api/me", { headers: { Authorization: "Bearer " + token } });
      }
      // window.bonuz.getToken() is the same thing without waiting: the current token, or null
    </script>

    In the hub these come with your own project key filled in, plus an AI prompt tab you can paste into Claude Code, Cursor, Copilot or ChatGPT to set it all up for you.

  5. 5

    window.bonuz and the three events

    Listen for the user, then fetch a token.

    Everything the widget knows reaches your page through window.bonuz and three events on window. bonuz:ready fires once when the widget is up (also when the config fetch fails, so your page can degrade). bonuz:login carries the user and fires on a fresh sign-in and when a returning visitor is restored. bonuz:logout fires when the session ends.

    app.js
    // window.bonuz exists from the moment the script runs. login() and logout()
    // called before the widget is up are queued; the getters answer at once.
    window.addEventListener('bonuz:ready', (e) => {
      // The widget is up. e.detail.project is your project name, or null if the
      // config fetch failed. It does not mean anyone is signed in yet: a returning
      // visitor's session is still being restored. Take the user from bonuz:login.
      console.log('bonuz ready for', e.detail.project)
    })
    
    // Fires on a fresh sign-in AND when a returning visitor's session is restored
    window.addEventListener('bonuz:login', async (e) => {
      showSignedIn(e.detail.user)
      const token = await window.bonuz.getFreshToken() // Promise<string | null>
      await fetch('/api/session', { method: 'POST', body: JSON.stringify({ token }) })
    })
    
    window.addEventListener('bonuz:logout', () => showSignedOut())
    
    // Also on window.bonuz:
    //   login()      opens the sign-in (a full-page redirect, no popup)
    //   logout()     ends the session
    //   getToken()   sync: the current access token, or null
    //   getUser()    sync: { id, handle, name, walletAddress }, or null
    //                (null for a returning visitor until the session is restored)
  6. 6

    Verify the token on your server

    One GET to the bonuz API.

    bonuz publishes no JWKS or public key, so do not decode or verify the token yourself. Ask the bonuz API who it belongs to: a 200 returns { id, walletAddress, handle, email }, a 401 means invalid, expired or logged out, a 404 means the account is gone. The answer confirms the person, not which app issued the sign-in.

    server.ts
    // Your server. The token arrived from the browser.
    const res = await fetch('https://admin.bonuz.xyz/api/v2/auth/me', {
      headers: { Authorization: `Bearer ${token}` },
    })
    if (res.status === 401) return unauthorized() // invalid, expired or logged out
    if (res.status === 404) return unauthorized() // the account is gone
    if (!res.ok) return tryAgainLater()
    
    const { id, walletAddress, handle, email } = await res.json()
    // That answer is who is signed in. Key your own records on id; handles can change.
    // It confirms the person, not which app issued the sign-in, so never grant admin rights from it alone.
    // Rate limited per server IP: remember a 200 for up to a minute (never past the token's exp).
  7. 7

    CSP and COOP

    The minimum your host page must allow.

    If your site sends a Content-Security-Policy, these are the floors. The RPC hosts are the ones the bonuz wallet reads balances from; add any others your own code calls. Email, Google and Apple sign-in redirect to the sign-in page and back, so they work with any Cross-Origin-Opener-Policy. Only the qID passkey window, which only bonuz apps offer, needs an opener: plain same-origin would break it there, so same-origin-allow-popups (or no COOP) is the safe default.

    headers
    Content-Security-Policy:
      script-src  'self' https://bonuz.id https://*.web3auth.io 'unsafe-eval' 'wasm-unsafe-eval';
      style-src   'self' https://bonuz.id;   /* add 'unsafe-inline' only for very old browsers */
      connect-src 'self' https://bonuz.id https://admin.bonuz.xyz https://*.web3auth.io wss://*.web3auth.io
                  https://mainnet.base.org https://sepolia.base.org https://*.publicnode.com;   /* the wallet's RPCs */
      frame-src   https://*.web3auth.io;
      img-src     'self' data: https:;   /* sign-in logos */
    
    Cross-Origin-Opener-Policy: same-origin-allow-popups   /* safe default, or no COOP. Email, Google and Apple sign-in
                                                              redirect and work with any COOP; only the qID passkey window
                                                              (bonuz's own apps) needs an opener, which same-origin breaks */
  8. 8

    WordPress

    Paste it as a Custom HTML block.

    Use one Custom HTML block containing the div and the script tag, or a header/footer plugin that pastes raw HTML. Enqueueing the script from functions.php does not work: WordPress prints neither the type="module" (classic enqueue) nor the data-project attribute (module enqueue) the widget needs.

    Custom HTML block
    <!-- One Custom HTML block where the sign-in button should appear -->
    <div data-bonuz-login></div>
    <script type="module" src="https://bonuz.id/sdk/v1/bonuz.js" data-project="bz_…"></script>
    
    <!-- Enqueueing from the theme does not work: wp_enqueue_script() drops
         type="module", and wp_enqueue_script_module() prints no data-project, so the
         widget never starts. A header or footer plugin that pastes raw HTML works
         the same as the block above. -->
  9. 9

    Go live

    Sign in once. Watch it fill.

    The Overview checklist tracks it for you: project created, website added, bonuz enables your site, snippet pasted and one sign-in done. From the first sign-in, Users lists everyone who signed in to your app (their public bonuz profile, never their email) and Analytics shows monthly active users, sign-ins and how many arrived with a bonuz ID from another app. The first 1,000 monthly active users of each project are free, whichever way they sign in.

What your app receives.

The script tag exposes id, handle, name and wallet address. The React hook also exposes the sign-in email. Sign-in itself happens on the sign-in provider's page (Web3Auth, Google or Apple), so one-time codes and passwords never touch your page.

Script tag

id, handle, name, walletAddress

The user object carries no email. Those four fields are what the host page gets.

React hook

id, handle, name, walletAddress, email (optional)

The React path also exposes the sign-in email on user.email. Store it only if you need it.

Your server

id, walletAddress, handle, email

What GET /api/v2/auth/me returns for that token. Key your own data on the id; handles can change.

What you ship now vs. what you ship with bonuz.

Same UX. Fundamentally different infrastructure underneath.

Old stack

Login as a line item

  • ✕Email + password. Breach risk on you.
  • ✕Magic link vendor lock-in.
  • ✕No wallet. No on-chain identity. No reach beyond your app.
  • ✕Loyalty is a database row, not a portable asset.
  • ✕Seed phrases scare 95% of your users away.

bonuz SDK login

Login as infrastructure

  • ✓Continue with Google, Apple, or email. Same UX your users already know.
  • ✓Self-custodial wallet (MPC/TSS) provisioned silently in the background.
  • ✓Portable bonuz ID: verified socials, attestations, and graph from day one.
  • ✓Gas sponsored. Users never see a fee or a seed phrase.
  • ✓No lock-in. Your users stay yours, but now they are also yours on-chain.

Three phases. Start shipping today.

Drop in the login first. Expand into engagement and the graph when you're ready.

1

Drop-in login

~ 2 days

Replace your existing auth with the bonuz SDK login button. Users keep the same flow. You get wallets, bonuz IDs, and session handling out of the box.

2

Activate engagement

~ 1 week

Start issuing DNFTs: loyalty cards, passes, vouchers, certificates. Gas sponsored. State machine handled by the protocol.

3

Plug into the graph

Ongoing

Query the social graph, read cross-app attestations, and let users bring reputation in from every other bonuz integration.

Login vendor vs. bonuz SDK login

A side-by-side of what you pay for and what you get.

Integration time
✕Weeks of auth + wallet + identity work
✓Minutes. One script tag, or one SDK call.
User flow
✕Email / password / magic link
✓Continue with Google, Apple, or email. Unchanged.
Wallet
✕None, or a separate extension
✓Self-custodial, provisioned silently
Identity
✕Scoped to your app
✓Portable bonuz ID, works everywhere
Loyalty / passes
✕Another SaaS vendor
✓Built in. On-chain. User-owned.
Gas fees
✕Passed to user, or not supported
✓Sponsored by developer wallets
Lock-in
✕Vendor owns the user record
✓Open protocol, no lock-in
Cost
✕Per-user pricing for a login and nothing more
✓First 1,000 monthly active users free. Login, wallet and identity in one.

Live on-chain. Today.

Verifiable, audited, and already securing real users.

Identity registry

0x9220070245b67130977FdF32acA4acdF6aD163cC

Attestation protocol

0xe6461B0C86bF85023a5f359BAdF7012d2ef5ad7e

Engagement / DNFT core

0x7034625223C3fc8628214cd0504fdE7Cf5CEDA63

The obvious questions.

Answered before you ask.

Will this change how my users log in?

No. The button still says Continue with Google, Apple, or email. The wallet and bonuz ID are provisioned silently in the background.

Am I locking myself into bonuz?

No. The SDK is free, the protocol is open, and the user owns their keys. If you ever walk away, your users keep their wallet and identity.

Who pays gas?

You do, from a sponsored developer wallet. Your users never see a fee. Gas costs on our target chains are a fraction of a cent per action.

Is it secure?

MPC/TSS key management with MFA. Smart contracts audited by Hacken (10/10 security score). No single point of failure, no custody risk for you.

What does it cost?

The first 1,000 monthly active users of each app are free, whichever way they sign in. Above that, paid plans are coming, and sign-in is never cut off for anyone. Gas you choose to sponsor is separate.

Swap your login today.

Keep your UX. Gain a self-custodial wallet, a portable on-chain identity, and a full engagement layer.

Join our E-Mail list and stay up to date about new releases and launches!

We promise not to spam you. We never share your details with third parties