> ## Documentation Index
> Fetch the complete documentation index at: https://docs.billions.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Login with Billions

> Enable secure and decentralized authentication by leveraging the Billions Network.

## 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.

<Steps>
  <Step title="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.
  </Step>

  <Step title="Open Billions Wallet">
    The frontend takes the authentication request, encodes it in Base64, and embeds it into a [Universal Link](https://hackmd.io/@0xpulkit/billions-universal-link). This link opens the **Billions Wallet**, prompting the user to authenticate.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Wallet Posts to Callback">
    The wallet sends a POST request to your backend `/api/callback` endpoint containing the `sessionId` and the signed JWZ token.
  </Step>

  <Step title="Backend Verifies the JWZ">
    Your backend verifies the JWZ token using the **Billions Verifier**, confirming the authenticity of the user’s DID.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Access Granted">
    Your application recognizes the user, grants access, and can use the verified DID for personalized or gated experiences.
  </Step>
</Steps>

***

## Setup Instructions

Before you begin, ensure the following environment requirements are met:

### Prerequisites

* Public URL for callbacks (use [ngrok](https://ngrok.com/) for local development)

### Example Repository

The steps below walk through the [reference implementation](https://github.com/0xPolygonID/tutorial-examples/tree/main/login-with-billions) — clone it to follow along. The code snippets on this page are its `index.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.

```javascript theme={null}
// index.js
const path = require("path");
const express = require("express");
const { auth, resolver } = require("@iden3/js-iden3-auth");
const getRawBody = require("raw-body");
const cors = require('cors');

const app = express();
const port = 8080;

app.use(express.static("./static"));
app.use(cors());

// Session storage for auth requests
const requestMap = new Map();

// Routes
app.get("/api/sign-in", getAuthRequest);
app.post("/api/callback", callback);

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});
```

<Tip>
  **Testing Tip:** Use the sample frontend in `./static` to test Universal Link handling and callback processing.
  When moving to production, update URLs, enable HTTPS, and use environment variables for configuration.
</Tip>

### 2. Authentication Request Handler

Generates basic authentication requests with empty scope and stores them with unique session IDs for later verification.

```javascript theme={null}
async function getAuthRequest(req, res) {
  try {
    // Configuration - Update these for your setup
    const hostUrl = " Your public URL";
    const callbackURL = "/api/callback";
    const verifier_did = "did:iden3:billions:main:2qQ68JkRcf3xrHPQPWZei3YeVzHPP58wYNxx2mEouR"; // Your verifier DID

    // Generate unique session
    const sessionId = Date.now();
    const uri = `${hostUrl}${callbackURL}?sessionId=${sessionId}`;

    // Create basic auth request (no proofs required)
    const request = auth.createAuthorizationRequest(
      "Basic Sign In", // Reason for authentication
      verifier_did,        // Your verifier DID
      uri             // Callback URL
    );

    request.body.scope = [];

    // Store for later verification
    requestMap.set(`${sessionId}`, request);

    console.log(`Created basic auth request for session: ${sessionId}`);
    return res.status(200).json(request);

  } catch (error) {
    console.error("Error creating auth request:", error);
    return res.status(500).json({ error: "Failed to create auth request" });
  }
}
```

<Info>
  **Get Your Verifier DID:** Sign in to your [Billions Wallet](https://wallet.billions.network/) and copy your profile DID to use during setup.
</Info>

<Accordion title="How does getAuthRequest connect to the Billions Wallet?">
  This is where [**Universal Links**](https://hackmd.io/@0xpulkit/billions-universal-link) 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:**

  ```text theme={null}
  https://wallet.billions.network/#i_m=<base64_encoded_auth_request>
  ```

  * **Base URL**: `https://wallet.billions.network/` — Billions Wallet endpoint
  * **Fragment**: `#i_m=` — parameter carrying the Base64-encoded auth request
</Accordion>

### 3. Verification Callback Handler

Receives JWZ token as a callback, validates them against stored authentication requests, and confirms user DID ownership.

```javascript theme={null}
async function callback(req, res) {
  try {
    // 1. Extract session and token
    const sessionId = req.query.sessionId;
      
    if (!sessionId) {
      return res.status(400).json({ error: "Session ID is required" });
    }

    const raw = await getRawBody(req);
    const tokenStr = raw.toString().trim();
      
    if (!tokenStr) {
      return res.status(400).json({ error: "Token is required" });
    }

    // 2. Setup blockchain resolvers
    const resolvers = {
      ["billions:main"]: new resolver.EthStateResolver(
        "https://rpc-mainnet.billions.network",
        "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896"
      )
    };

    // 3. Retrieve stored auth request
    const authRequest = requestMap.get(`${sessionId}`);
    if (!authRequest) {
      return res.status(400).json({
        error: "Invalid session ID or session expired"
      });
    }

    // 4. Initialize verifier
    const verifier = await auth.Verifier.newVerifier({
      stateResolver: resolvers,
      ipfsGatewayURL: "https://ipfs.io",
    });

    // 5. Verify authentication
    const opts = {
      AcceptedStateTransitionDelay: 5 * 60 * 1000, // 5 minutes
    };

    const authResponse = await verifier.fullVerify(tokenStr, authRequest, opts);

    // 6. Clean up and respond
    requestMap.delete(`${sessionId}`);

    console.log(`Authentication successful for session: ${sessionId}`);
    console.log("User DID:", authResponse.from);

    return res.status(200).json({
      success: true,
      message: "Basic authentication successful",
      userDID: authResponse.from,
      timestamp: new Date().toISOString(),
      sessionId: sessionId
    });

  } catch (error) {
    console.error("Authentication error:", error);
    return res.status(500).json({
      error: "Authentication failed",
      details: error.message
    });
  }
}
```

You’ve successfully verified DID ownership.

To turn this into a complete login, store user session details by their DID in your database. The DID acts as a persistent identity, allowing your app to recognize returning users.

When a user logs in again with the same Billions Wallet, they’ll present the same DID, letting your application instantly identify them and deliver the right personalized experience — decentralized, secure, and private.

### Testing Steps

1. **Visit your app:** `http://localhost:8080`
2. **Test universal link:** Click "Login with Billions" button

You can test the full authentication loop — from generating the auth request to verifying the DID — ensuring your app correctly recognizes verified users.

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="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](https://ngrok.com/) 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.
  </Accordion>

  <Accordion title="&#x22;Invalid session ID or session expired&#x22;">
    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.
  </Accordion>
</AccordionGroup>

***

## Going Further: Beyond Basic Login

Once DID-based login is in place, the same identity can power other authentication experiences — crypto wallet sign-ins, verification against Billions credentials, or 2FA — all anchored to a single decentralized identity.
