# BlastsKit Sample RP — "Sign in with Blasts" in one file

A complete example website that signs users in with **LoginBlasts**: no
passwords, no SMS codes — the user's phone rings and they approve there.

Everything lives in one file, `server.js`. **Zero dependencies** — you only
need [Node.js 18 or newer](https://nodejs.org).

## Run it (3 steps)

1. **Get sandbox keys** (instant, free): open
   [https://api.blasts.app/dev/console](https://api.blasts.app/dev/console),
   sign in with your email, click **New sandbox key**. Copy the API key
   (`pk_test_…`) and the signing secret (`sk_test_…`).

2. **Start the server** with your keys:

   Windows (PowerShell):
   ```powershell
   $env:BLASTS_API_KEY="pk_test_YOUR_KEY"
   $env:BLASTS_SIGNING_SECRET="sk_test_YOUR_SECRET"
   node server.js
   ```

   macOS / Linux:
   ```bash
   BLASTS_API_KEY=pk_test_YOUR_KEY BLASTS_SIGNING_SECRET=sk_test_YOUR_SECRET node server.js
   ```

3. **Open [http://localhost:3000](http://localhost:3000)** and sign in with
   the **same email you used on the developer console** (sandbox keys can
   only ring their creator's phone — that's a safety feature). Your Blasts
   phone app rings; type the 6-digit code from the page onto the approval
   card; the page signs you in.

First time through, you'll be asked to **approve the connection** on your
phone once, then press Sign in again — that's the one-time consent step
every app goes through.

## What to copy into your production app

- **Secrets stay server-side.** The API key, signing secret, and
  `poll_secret` never reach the browser. The browser only sees the 6-digit
  display code and a request id.
- **The assertion is the login.** No session is created until the ES256
  assertion from `/login/status` verifies — locally against the public
  JWKS (`/.well-known/blastslogin-jwks.json`, cache ≤ 1 hour) or via
  `POST /login/verify`. `status:"approved"` alone is never trusted, and
  `assertion_unavailable:true` is a monitoring signal, not a login.
- **Check every claim**: `iss` = `https://api.blasts.app`, `aud` = *your*
  partner id (from `/me`), `exp` in the future, header `typ` =
  `blasts-login+jwt`, and dedupe on `jti` so a replayed token can't mint a
  second session.
- **`sub` is pairwise** — stable for (user, your app), useless to correlate
  across apps. Key your user table on it. The token never carries the email.

Full API reference: [https://blasts.dev/docs.html](https://blasts.dev/docs.html)
· Login guide: [https://blasts.dev/partner-login-guide.html](https://blasts.dev/partner-login-guide.html)

Questions: **AL@SavingsSites.com**
