Authentication

Three ways to authenticate. Pick the one that fits your setup.

Which method should I use?
  • 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

  1. Request a challenge: POST /auth/challenge with {"pubkey": "YOUR_PUBKEY"}
  2. Sign and verify: Sign the returned challenge string, then POST /auth/verify with the signature
  3. Use the token: Include Authorization: Bearer TOKEN in subsequent requests
curl
TypeScript
# 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

HeaderValue
X-PubkeyYour Solana public key (base58)
X-SignatureEd25519 signature of payload (base58)
X-TimestampUnix timestamp (must be ±30s of server time)
X-NonceRandom 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

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"
});