Sample app — "Sign in with Blasts" in one file
A complete relying-party website you can run in under two minutes: connect invitation, sign-in ceremony, phone approval, local ES256 assertion verification against the public JWKS, and a real session cookie. One file. Zero dependencies. Node 18+ only.
server.js README.md package.json
Run it
1. Get sandbox keys (instant, free)
Open the Developer Console, sign in with your email, click New sandbox key. Copy the API key (pk_test_…) and signing secret (sk_test_…).
2. Start the server
# Windows (PowerShell) $env:BLASTS_API_KEY="pk_test_YOUR_KEY" $env:BLASTS_SIGNING_SECRET="sk_test_YOUR_SECRET" node server.js # macOS / Linux BLASTS_API_KEY=pk_test_YOUR_KEY BLASTS_SIGNING_SECRET=sk_test_YOUR_SECRET node server.js
3. Sign in
Open http://localhost:3000 and sign in with the same email you used on the console — sandbox keys can only ring their creator's phone (that's a safety feature, not a limitation of your future live keys). First time through you'll approve the connection on your phone once, then sign in.
What it demonstrates (copy this shape)
| Rule | Where in server.js |
|---|---|
Secrets stay server-side. API key, HMAC secret, and poll_secret never reach the browser — the page only sees the 6-digit display code and a request id, and polls your server, which polls us. | blasts(), /auth/start, /auth/poll |
The assertion is the login. No session until the ES256 JWT verifies — locally against the JWKS (cache ≤ 1 h) with iss/aud/exp/typ checks, or via POST /login/verify as the fallback. status:"approved" alone is never trusted; assertion_unavailable:true is monitoring, not authentication. | verifyAssertionLocal() |
Replay fenced. jti is deduped — a replayed assertion cannot mint a second session. | seenJtis |
Pairwise identity. sub is stable for (user, your app) and useless to correlate across apps — key your user table on it. The token never carries the email. | session creation in /auth/poll |
The flow, end to end
Browser Your server api.blasts.app User's phone
│ email │ │ │
├───────────────────►│ POST /login/connect (HMAC) │ │
│ ├──────────────────────────────────►│──── consent invite ─────►│
│ "check phone" │◄── {status:"invited"} ────────────┤ │
│◄───────────────────┤ │ (user approves │
│ sign in again │ POST /login/request (HMAC) │ the connection) │
├───────────────────►├──────────────────────────────────►│ │
│ 6-digit code │◄── {request_id, challenge, │ │
│◄───────────────────┤ poll_secret (KEPT HERE)} ─────┤──── approval card ──────►│
│ poll /auth/poll │ POST /login/status (loop) │ (user types the code) │
├───────────────────►├──────────────────────────────────►│◄─────── approve ─────────┤
│ │◄── {status:"approved", assertion} ┤ │
│ │ verify ES256 vs JWKS (local) │ │
│ session cookie │ mint session on VERIFIED claims │ │
│◄───────────────────┤ │ │
Full endpoint contracts: Login API guide · API reference · OpenAPI spec. Mobile SDKs (Swift/Kotlin/React Native) with the same ceremony embedded in-app are on the BlastsKit roadmap — this sample is the server-side pattern they all sit on.
Questions: AL@SavingsSites.com