Authentication
Three ways to authenticate. Pick the one that fits your setup.
- API Key — Simplest. Generate once, use as a Bearer token. Best for scripts, prototyping, and simple integrations.
- Session Token — Authenticate once, get a 1-hour JWT. Best for WebSocket connections and interactive use.
- Per-Request Signature — Most secure. Sign every request with your keypair. Best for high-security bots and agents.
API Key (Simplest)
Generate a persistent API key and use it as a Bearer token. No signing, no sessions — just a header.
Generate a Key
Requires an initial wallet signature to create:
POST /account/api-key
# Requires wallet signature auth (see Per-Request Signature below)
# Response:
{ "apiKey": "nod_abc123..." }
Use the Key
curl -X POST https://rpc.nodius.xyz/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer nod_abc123..." \
-d '{"jsonrpc":"2.0","id":1,"method":"getSlot"}'
Keys don't expire, but generating a new one invalidates the previous key.
Session Token
Authenticate once with a challenge-response flow to get a 1-hour JWT. Ideal for WebSocket connections.
Flow
- Request a challenge:
POST /auth/challengewith{"pubkey": "YOUR_PUBKEY"} - Sign and verify: Sign the returned challenge string, then
POST /auth/verifywith the signature - Use the token: Include
Authorization: Bearer TOKENin subsequent requests
# Step 1: Get challenge
curl -X POST https://rpc.nodius.xyz/auth/challenge \
-H "Content-Type: application/json" \
-d '{"pubkey": "YOUR_PUBKEY"}'
# Response: { "challenge": "nodius:challenge:abc123:1712000000" }
# Step 2: Sign the challenge with your keypair, then verify
curl -X POST https://rpc.nodius.xyz/auth/verify \
-H "Content-Type: application/json" \
-d '{"pubkey": "YOUR_PUBKEY", "signature": "SIGNED_CHALLENGE_BASE58"}'
# Response: { "token": "eyJhbG...", "expiresAt": 1712003600 }
const rpc = new NodiusClient({
endpoint: "https://rpc.nodius.xyz",
keypair,
auth: "session", // Handles challenge/verify automatically
});
// SDK manages token lifecycle
const slot = await rpc.call("getSlot");
Token details: Expires after 1 hour. No refresh — re-authenticate when it expires. Revoke early with POST /auth/logout.
WebSocket auth: Use Authorization: Bearer TOKEN header, or Sec-WebSocket-Protocol: auth.TOKEN for browser clients.
Per-Request Wallet Signature
Sign every request with your Solana keypair. No tokens stored, no state — maximum security for automated systems.
Required Headers
| Header | Value |
|---|---|
X-Pubkey | Your Solana public key (base58) |
X-Signature | Ed25519 signature of payload (base58) |
X-Timestamp | Unix timestamp (must be ±30s of server time) |
X-Nonce | Random 16-char hex string |
Signing Payload
v1 (default): {timestamp}:{nonce}:{sha256_of_body}
v2 (enhanced): {method}:{path}:{timestamp}:{nonce}:{sha256_of_body}
Requires additional headers: X-Auth-Version: 2, X-Http-Method, X-Http-Path
TypeScript Implementation
import { createHash, randomBytes } from "crypto";
import { sign } from "tweetnacl";
import bs58 from "bs58";
function signRequest(keypair: Keypair, body: string) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = randomBytes(8).toString("hex");
const bodyHash = createHash("sha256").update(body).digest("hex");
const payload = \`\${timestamp}:\${nonce}:\${bodyHash}\`;
const signature = sign.detached(
new TextEncoder().encode(payload),
keypair.secretKey
);
return {
"X-Pubkey": keypair.publicKey.toBase58(),
"X-Signature": bs58.encode(Buffer.from(signature)),
"X-Timestamp": timestamp,
"X-Nonce": nonce,
};
}
Replay Protection
- Nonce cache: 60 seconds. Reusing a (pubkey, nonce) pair returns
403. - Clock skew: Requests outside ±30s return
401. Use NTP to keep your clock synced.
SDK Auth
The SDK handles all auth modes automatically:
const rpc = new NodiusClient({
endpoint: "https://rpc.nodius.xyz",
keypair,
auth: "signature-v2", // "signature", "signature-v2", or "session"
});