Overview
Enables applications to let users sign in seamlessly using their Decentralized Identifiers (DIDs). Think of it as “Login with Google” — but trustless, privacy-preserving, and self-sovereign.Key Capabilities
- Authenticates DID Ownership: Authenticates users by cryptographically proving ownership of their DID
- Enables Trustless Authentication: Removes reliance on centralized identity providers.
- Delivers a Familiar Web2 Experience: Sign-in feels like any social login — the user clicks a button and approves in their wallet, with no passwords or seed phrases to manage.
Authentication Flow
The Login with Billions process verifies user identity through DID ownership.1
Initiate Sign-In
The user selects Login with Billions in the application. This triggers a backend call to
/api/sign-in, which creates a new authentication request session.2
Open Billions Wallet
The frontend takes the authentication request, encodes it in Base64, and embeds it into a Universal Link. This link opens the Billions Wallet, prompting the user to authenticate.
3
User Approves
The user approves the sign-in in the Billions Wallet. The wallet generates a
signed JWZ (JSON Web Zero-knowledge) token — a verifiable proof of DID ownership.4
Wallet Posts to Callback
The wallet sends a POST request to your backend
/api/callback endpoint containing the sessionId and the signed JWZ token.5
Backend Verifies the JWZ
Your backend verifies the JWZ token using the Billions Verifier, confirming the authenticity of the user’s DID.
6
Session Created
Upon successful verification, the user’s DID is confirmed. Create or update their session in your database — the DID acts as a persistent identity for all future sign-ins.
7
Access Granted
Your application recognizes the user, grants access, and can use the verified DID for personalized or gated experiences.
Setup Instructions
Before you begin, ensure the following environment requirements are met:Prerequisites
- Public URL for callbacks (use ngrok for local development)
Example Repository
The steps below walk through the reference implementation — clone it to follow along. The code snippets on this page are itsindex.js, and the repo includes the ./static frontend used in the testing steps.
1. Server Configuration
Sets up Express server with required middleware and routes for handling authentication requests and callbacks.2. Authentication Request Handler
Generates basic authentication requests with empty scope and stores them with unique session IDs for later verification.Get Your Verifier DID: Sign in to your Billions Wallet and copy your profile DID to use during setup.
How does getAuthRequest connect to the Billions Wallet?
How does getAuthRequest connect to the Billions Wallet?
This is where Universal Links come into play. Your frontend takes the auth request returned by
getAuthRequest(), encodes it in Base64, and embeds it into a Universal Link, which redirects the user to the Billions Wallet. The wallet processes the request, prompts the user to sign, and then posts a signed JWZ (JSON Web Zero-knowledge) token to your callback endpoint. That JWZ is what your backend verifies to confirm DID ownership — delivering a seamless login flow across web and mobile.Example link format:- Base URL:
https://wallet.billions.network/— Billions Wallet endpoint - Fragment:
#i_m=— parameter carrying the Base64-encoded auth request
3. Verification Callback Handler
Receives JWZ token as a callback, validates them against stored authentication requests, and confirms user DID ownership.Testing Steps
- Visit your app:
http://localhost:8080 - Test universal link: Click “Login with Billions” button
Troubleshooting
The callback endpoint never fires
The callback endpoint never fires
The wallet POSTs the JWZ token to your
/api/callback endpoint from outside your machine — so hostUrl must be a publicly reachable URL, not localhost. For local development, run ngrok and use the ngrok HTTPS URL as hostUrl. If the callback still doesn’t arrive, confirm the auth request’s callback URI includes the sessionId query parameter."Invalid session ID or session expired"
"Invalid session ID or session expired"
Auth requests are stored in an in-memory
Map, so restarting the server clears all pending sessions — any link generated before the restart will fail. Each session is also deleted after its first callback (requestMap.delete), so a verification link can’t be replayed. In both cases, start a fresh sign-in to generate a new auth request.