Developer hub
Developer Documentation & Quickstart
Integrate Digi-ID authentication into any back-end service using standard message verification libraries.
Core Concepts
- Challenge Nonce: A random, time-bound string tied to the login session to prevent replay attacks.
- Domain Binding: Key derivation uses your exact URI domain, ensuring keys generated for one website cannot authenticate on another.
- Stateless Verification: Verification uses standard ECDSA signature checking on the server without pinging the blockchain or remote nodes.
Quickstart · ~5 minutes
Integrate passwordless authorization in three steps.
Your server issues a challenge nonce, the wallet signs it, and your server verifies the signature locally. Choose your backend language to view code samples.
-
Step 1
Install a library
Verification needs no DigiByte node and no network calls.
npm install digiid express# PHP 7.4+ with the GMP extension git clone --recursive https://github.com/DigiByte-Core/digiid-php php -m | grep -i gmpnpm install qrcode # render the challenge as a QR code -
Step 2
Issue a challenge
Create a random nonce, remember it for ~90 seconds, and show the
digiid://URI as a QR code and a link.// server.js — issue a challenge (Express) import crypto from 'node:crypto'; import express from 'express'; import DigiID from 'digiid'; const CALLBACK = 'www.example.com/digiid/callback'; // host + path, no scheme const pending = new Map(); // nonce -> { sessionId, expires, address } const app = express(); app.post('/digiid/challenge', (req, res) => { const nonce = crypto.randomBytes(16).toString('hex'); pending.set(nonce, { sessionId: req.sessionID, expires: Date.now() + 90_000 }); const { uri } = new DigiID({ nonce, callback: CALLBACK }); res.json({ uri, nonce }); // digiid://www.example.com/digiid/callback?x=… });<?php // login.php — issue a challenge (digiid-php) require_once __DIR__ . '/DigiID.php'; session_start(); $digiid = new DigiID(); $nonce = $digiid->generateNonce(); // 32 hex chars from a CSPRNG // Bind the nonce to this session with a short expiry (see "Nonces & replay"). $db->prepare('INSERT INTO digiid_nonces (nonce, session_id, expires_at) VALUES (?, ?, ?)') ->execute([$nonce, session_id(), time() + 90]); $uri = $digiid->buildURI('https://www.example.com/digiid/callback.php', $nonce); // Render $uri as a link and as a QR code (e.g. with endroid/qr-code).// login.js — show the challenge in the browser import QRCode from 'qrcode'; const { uri, nonce } = await fetch('/digiid/challenge', { method: 'POST' }).then((r) => r.json()); // Tappable on mobile (opens the wallet), scannable on desktop. document.querySelector('#digiid-link').href = uri; await QRCode.toCanvas(document.querySelector('#digiid-qr'), uri, { width: 240 }); // Wait for the wallet to call back, then continue the session. const poll = setInterval(async () => { const { state } = await fetch(`/digiid/status?nonce=${nonce}`).then((r) => r.json()); if (state === 'verified') { clearInterval(poll); location.assign('/account'); } }, 2000); -
Step 3
Verify the callback
The wallet POSTs
address,uriandsignature. Check the URI, the nonce and the signature, then consume the nonce.// server.js — verify the wallet callback (Express) app.post('/digiid/callback', express.json(), express.urlencoded({ extended: false }), (req, res) => { const { address, uri, signature } = req.body; if (!address || !uri || !signature) return res.status(400).json({ error: 'missing fields' }); const digiid = new DigiID({ address, uri, signature, callback: CALLBACK }); const challenge = pending.get(digiid.nonce); if (!digiid.uriValid() || !challenge || challenge.expires < Date.now()) { return res.status(410).json({ error: 'unknown or expired challenge' }); } if (!digiid.signatureValid()) return res.status(401).json({ error: 'invalid signature' }); pending.delete(digiid.nonce); // single use: a replay now fails sessions.login(challenge.sessionId, address); // address = the user's ID for your site res.json({ message: 'Digi-ID verified' }); });<?php // callback.php — verify the wallet callback (digiid-php) require_once __DIR__ . '/DigiID.php'; $digiid = new DigiID(); // Wallets send JSON; manual signing tools send form fields. $input = json_decode(file_get_contents('php://input'), true) ?? $_POST; $address = (string) ($input['address'] ?? ''); $signature = (string) ($input['signature'] ?? ''); $uri = (string) ($input['uri'] ?? ''); $nonce = $digiid->extractNonce($uri); $row = $db->prepare('SELECT session_id FROM digiid_nonces WHERE nonce = ? AND expires_at > ? AND address IS NULL'); $row->execute([$nonce, time()]); $challenge = $row->fetch(); $expectedUri = $digiid->buildURI('https://www.example.com/digiid/callback.php', $nonce); if (!$challenge || $uri !== $expectedUri || !$digiid->isMessageSignatureValidSafe($address, $signature, $uri)) { http_response_code(401); exit(json_encode(['error' => 'invalid or expired Digi-ID challenge'])); } // Consume the nonce and attach the verified address to the waiting session. $db->prepare('UPDATE digiid_nonces SET address = ? WHERE nonce = ?')->execute([$address, $nonce]); echo json_encode(['message' => 'Digi-ID verified']);// Verify in the browser or any JS runtime with digiid-core (packages/digiid-core in the digi-id.io repo) import { verifyCallback } from './digiid-core/src/index.ts'; const result = verifyCallback( { address, uri, signature }, // POST body from the wallet { callback: 'https://www.example.com/digiid/callback' } ); if (result.valid) { // result.nonce -> look up and consume the pending challenge // result.address -> the user's Digi-ID for your site } else { console.warn('Rejected:', result.reason); // bad_request | uri_mismatch | unsecure | bad_signature }
SDKs & plugins
Official SDKs & Reference Libraries
Pure-cryptography libraries that handle key derivation, URI parsing, and message signature verification without external API dependencies.
-
Maintained
digiid-core
TypeScript · browser & Node
URI helpers, key derivation and signature verification that run in browsers and Node. Powers this site's demo and playground; tested against the published vectors.
Copy packages/digiid-core from the digi-id.io repositorySource on GitHub -
Community
digiid-js
JavaScript · Node.js
Lightweight npm package for Express, Fastify, and Next.js backends. Includes native TypeScript definitions.
npm install digiidSource on GitHub -
Community
digiid-php
PHP
Supports PHP 7.4+ with GMP extension. Drop-in integration for standard web servers.
git clone https://github.com/DigiByte-Core/digiid-phpSource on GitHub -
Unmaintained
Digi-ID for WordPress
WordPress plugin
Enables passwordless sign-in for WP-Admin and subscriber logins out of the box.
Upload to wp-content/plugins and activateSource on GitHub -
Unmaintained
Digi-ID for phpBB
phpBB extension
Native forum authentication plugin that replaces standard login forms.
Copy to ext/DigiByte/digiid and enable in the ACPSource on GitHub
Guides
Implementation Guides
Architectural details for session security, key management, and edge cases.
-
15 min
Add Digi-ID login to a web app
Challenge, QR code, wallet callback and session hand-off in a small Express app.
Read guide -
10 min
Nonces & Replay Prevention
How to store, time-box (60–90 seconds), and consume nonces to eliminate replay attacks.
Read guide -
15 min
Cross-Device Polling vs. WebSockets
Comparing short-polling HTTP endpoints against WebSocket connections for real-time QR authorization updates.
Read guide -
10 min
Add Digi-ID to WordPress
Install and configure the WordPress plugin, then let users link their wallets.
Read guide -
20 min
Account Binding & Recovery
Strategies for linking public wallet addresses to existing user databases and handling secondary device pairing.
Read guide -
45 min
Build a door-access prototype
A kiosk screen per door, one stable callback and an allow-list of Digi-ID addresses.
Read guide -
60 min
Support Digi-ID in your wallet
Parse digiid:// URIs, derive per-site keys and post signed callbacks.
Read guide
Examples & demos
Code Examples & Sandbox
Working reference implementations to clone and test locally.
-
Sandbox demo
The full protocol in your browser with a simulated wallet — challenge, key derivation, signature and verification.
-
PHP login example
A complete MySQL-backed login flow with polling, callback and session hand-off.
-
Signature playground
Paste a callback from your wallet or server logs and see exactly which check passes or fails.
Protocol reference
How Digi-ID works, precisely.
Digi-ID is an open protocol for authentication with public-key cryptography, derived from the BitID draft. It works much like keyless SSH, with a wallet as the user-friendly key manager. Only the user's public address is ever stored by a service.
Challenge URI
Before granting access, the service shows a QR code (and, on mobile, a link) containing:
digiid://www.example.com/callback?x=NONCE| Part | Meaning |
|---|---|
digiid:// | Scheme that opens a Digi-ID wallet when tapped or scanned. |
www.example.com/callback | Callback URL without scheme. HTTPS is assumed and strongly recommended. |
x=NONCE | Unique, unpredictable value linked to the user's session. Never reuse it. |
u=1 (optional) | Tells the wallet to POST over plain HTTP. For local development only — never in production. Digi-ID is not a substitute for TLS. |
Prefix the QR code with “Sign in with Digi-ID” so users know what they are scanning. The DigiByte “D” in the QR code is optional.
Callback
After the user confirms with their PIN or fingerprint, the wallet POSTs to the callback URL (JSON body; some tools send form fields):
POST /callback HTTP/1.1
Host: digiid.digibyteprojects.com
Content-Type: application/json
{
"address": "DJDAkjie6nrW6RpFZSTpNUXsZ9JE2x6p1o",
"uri": "digiid://digiid.digibyteprojects.com/callback?x=c6140375e5bae71e",
"signature": "H3tlK3RciMR60PuE0jE6JuClqgh+E8MmMz+n+4+P7FAIVuUccs+npnIa6Gz8XAJeOilTWpLNbomXtVDe4gR0CNk="
}| Field | Description |
|---|---|
address | The user's DigiByte address for your site (starts with D). Use it as the stable user ID. |
uri | The exact challenge URI that was signed. |
signature | Base64, 65-byte compact recoverable ECDSA signature of uri. |
Respond with 200 on success. Wallets show a success or error message to the user based on the response.
Verification
- Parse
uri; confirm the scheme isdigiidand host + path equal your callback. - Reject
u=1in production. - Look up the nonce: it must exist, be unexpired (≈90 s) and unused.
- Verify the signature: hash
"\x19DigiByte Signed Message:\n" + varint(len) + uriwith double SHA-256, recover the public key, derive the P2PKH address (version byte0x1E) and compare withaddress. - Mark the nonce as used and attach
addressto the waiting session.
No DigiByte node or external service is required — the libraries implement all of this locally.
Key derivation
Wallets derive a separate key per site, so users can't be tracked across services. The derivation input is the callback URL with its scheme and path, without the query string (for example https://www.example.com/callback). Keep one stable callback URL per site so user addresses stay the same.
- Concatenate the little-endian 32-bit
index(usually0) with the callback URL. - Compute SHA-256 of the result.
- Split the first 128 bits into four little-endian 32-bit numbers A, B, C, D.
- Set the highest bit of each (hardened).
- Derive BIP32 node
m/13'/A'/B'/C'/D'.
Test vector
| BIP39 seed | myth glimpse mystery abstract embark net faint hospital catch hint develop state |
|---|---|
| Digi-ID URI | digiid://digiid.digibyteprojects.com/callback?x=c6140375e5bae71e |
| Index | 0 |
| SHA-256 | 460ebbfd1df7410106d62d41f802f769bc00db825e9cf8c649cb069dec35e8fb |
| BIP32 path | m/2147483661/4256894534/2168583965/3241006598/3925279480 |
| Address | DJDAkjie6nrW6RpFZSTpNUXsZ9JE2x6p1o |
The callback example above is a real signature from this vector — paste it into the playground to check it.
Security checklist
- Serve the callback over HTTPS; never accept
u=1in production. - Generate nonces from a CSPRNG (≥128 bits), bind them to a session, expire them after ~90 s and delete them after first use.
- Compare the signed URI with the one you issued — host, path and nonce.
- Rate-limit the callback and challenge endpoints.
- Treat the address as an identifier, not an authorization: map it to roles in your own system.
- Offer a recovery path (e.g. link a second wallet) — a lost seed means a lost key.
More in the security model and FAQ.
Playground
Check a signature, right here.
Paste a callback payload to see which checks pass. Everything runs in your browser; nothing is sent anywhere.
Only paste public values: an address, a URI and a signature. Never paste a seed phrase or private key into any website.
Brand & UI kit
Make it recognisable.
A consistent “Sign in with Digi-ID” button and QR presentation help users trust the flow.
QR code guidance
- Label it “Sign in with Digi-ID” above the code.
- At least 200 px, dark modules on a light background, quiet zone intact.
- Also render the URI as a tappable link for mobile users.
- Show a countdown and regenerate after expiry.