Documentation
Basic technical documentation for integrating Nodping with your website or app.
1. Authenticator
The 'Add to Nodping' deep link enables external websites/apps to seamlessly hand off a 2FA TOTP MFA secret directly into a user's Authenticator tab, eliminating manual additions. At the time of writing, you can replace 'nodping.app' with '581881.xyz' as a fallback for personal non-commercial use.
Model
- URL Hash Interception: Nodping captures the
#otpauth=...fragment viawindow.location.hash. - URI Decoding: The app extracts and decodes standard
otpauth://totp/...parameters. - History Scrubbing: The hash fragment is scrubbed from web browser's history immediately via
history.replaceStatebefore rendering, preventing secrets from leaking in history, logs, or referrer headers. - User Prompt: Nodping displays a confirmation prompt:
Add <issuer> (<account>) into Authenticator?. - Encrypted Storage: Upon user's approval, the account is saved directly to a local IndexedDB AES-256-GCM vault.
Specifications
Construct deep links using the following structure:
https://nodping.app/#otpauth=<urlencoded-otpauth-uri>
Standard otpauth:// URI parameter structure:
otpauth://totp/Issuer:account_name?secret=BASE32SECRET&issuer=Issuer&algorithm=SHA1&digits=6&period=30
Examples
Add to Nodping
<a href="https://nodping.app/#otpauth=otpauth%3A%2F%2Ftotp%2FAcmeCorp%3Auser%40example.com%3Fsecret%3DJBSWY3DPEHPK3PXP%26issuer%3DAcmeCorp"
target="_blank"
rel="noopener noreferrer"
class="nodping-button">
<img src="https://opengraph.raspberald.com/preview.png" alt="Nodping™ logo" class="nodping-logo" />
<span>Add to Nodping</span>
</a>
<style>
.nodping-button {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 10px 16px;
background: #0f172a;
color: #ffffff;
font-weight: 600;
font-size: 14px;
border-radius: 8px;
text-decoration: none;
transition: background 0.2s ease;
}
.nodping-button:hover {
background: #1e293b;
}
.nodping-logo {
width: 20px;
height: 20px;
object-fit: contain;
border-radius: 4px;
}
</style>
function generateNodpingUrl(issuer, accountEmail, base32Secret) {
const otpauth = `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(accountEmail)}?secret=${base32Secret}&issuer=${encodeURIComponent(issuer)}`;
return `https://nodping.app/#otpauth=${encodeURIComponent(otpauth)}`;
}
// Example usage:
const nodpingLink = generateNodpingUrl("MySaaS", "alex@example.com", "JBSWY3DPEHPK3PXP");
// Output: https://nodping.app/#otpauth=otpauth%3A%2F%2Ftotp%2FMySaaS%3Aalex%40example.com%3Fsecret%3DJBSWY3DPEHPK3PXP%26issuer%3DMySaaS
2. Nodping Accounts
The Nodping API keys allow external servers and client applications to securely fetch a user's Nodping username, Nodping number, and Nostr public identifier using a user-generated value. At the time of writing, you can replace 'nodping.app' with '581881.xyz' as a fallback for personal non-commercial use.
Architecture
- Zero-Knowledge Generation: Keys are generated locally in-browser via Web Crypto with the prefix
np_live_followed by 64 hexadecimal characters i.e. 72 total characters. - One-Way Hash Storage: Servers store only
SHA-256(apiKey). A compromised database never exposes raw keys. - CAPTCHA Bypass: Requests to
/api/v1/verifyroute around human-verification challenges, enabling reliable server-to-server calls. - Access Restrictions: Users can bind validation permissions to specific domain names (
Originheader) or IP addresses. - Rate Limits: 60 requests each minute per key. 10 invalid attempts trigger a 15-minute lockout with a
Retry-Afterheader per IP address.
Endpoint
Request
| Header | Value Format | Requirement | Description |
|---|---|---|---|
Authorization |
Bearer np_live_<64-hex-chars> |
Required | Your user's API key. |
Origin |
https://example.com |
Optional | Checked only if Origin-based restrictions are set on the key. |
curl -X GET https://nodping.app/api/v1/verify \
-H "Authorization: Bearer np_live_4f8b2c1a890123456789abcdef0123456789abcdef0123456789abcdef012345"
Responses
{
"valid": true,
"username": "alice",
"nodping_number": "10042",
"npub": "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqp87vvd",
"created_at": 1791480000000
}
// 401 Unauthorized (Invalid format, unknown key, or revoked key)
{ "valid": false, "error": "Invalid API key." }
// 403 Forbidden (Origin or IP not permitted)
{ "valid": false, "error": "This domain name or IP address is not allowed for this key." }
// 429 Too Many Requests (Rate limit exceeded)
{ "valid": false, "error": "Too many requests for this key.", "retryAfter": 45 }
Backend
import fetch from "node-fetch";
async function verifyNodpingUser(apiKey, serviceOrigin = null) {
// Validate exact format: np_live_ + 64 hex characters (72 chars total)
if (!/^np_live_[0-9a-fA-F]{64}$/.test(apiKey)) {
throw new Error("Invalid key format: expected np_live_ prefix with 64 hex characters.");
}
const headers = {
Authorization: `Bearer ${apiKey}`,
};
if (serviceOrigin) {
headers["Origin"] = serviceOrigin;
}
const response = await fetch("https://nodping.app/api/v1/verify", {
method: "GET",
headers,
});
if (response.status === 401) {
throw new Error("Invalid or revoked Nodping API key.");
}
if (response.status === 403) {
throw new Error("Origin or IP address is not authorized for this Nodping key.");
}
if (response.status === 429) {
const retryAfter = response.headers.get("retry-after") || 60;
throw new Error(`Rate limit reached. Retry after ${retryAfter} seconds.`);
}
if (!response.ok) {
throw new Error(`Verification failed with HTTP ${response.status}`);
}
return await response.json();
}