API Authentication
External Client Authentication
Use session-v2 wallet authentication when building scripts, services, and other external clients against the 6529 API.
What to use
New external clients should authenticate through session-v2 endpoints. The standard external-client mode is client_type=native, even when the client is a command-line script or backend service controlled by the wallet owner.
The older nonce, login, and redeem-refresh-token endpoints remain compatibility endpoints for older clients. New integrations should not start on those legacy endpoints.
Login flow
- Request a signable message with
signer_address,client_type=native, andchain_id=1fromGET /api/auth/session-nonce. - Sign the returned message exactly as returned in
signable_message. - Send
client_type,client_address,client_signature,server_signature, and optionalroletoPOST /api/auth/session-login. - Use the returned
access_tokenon protected API calls asAuthorization: Bearer <access_token>.
Refresh and logout
Native/script login returns an access_token for bearer auth plus a native_refresh_token and refresh_token_expires_at for long-running clients.
Refresh through POST /api/auth/session-refresh with client_type=native, client_address, and the current native_refresh_token. A successful refresh rotates the native refresh token, so replace the stored token with the new one immediately.
Logout through POST /api/auth/session-logout with client_type=native, client_address, the current native_refresh_token, and all_sessions=false unless you intend to revoke every session for that wallet.
Browser clients
This guide is for external clients. First-party browser sessions also use session-v2, but browser refresh state is handled with backend-owned HttpOnly cookies, credentials-included requests, and origin checks. Follow the app implementation rather than adapting the native/script examples directly for browser sessions.
Security rules
- Sign only
signable_messageexactly as returned. Do not trim, normalize, rebuild, JSON-stringify, or sign anoncefield. - Do not log private keys, access tokens, refresh tokens, signatures, or raw authentication responses. Store refresh tokens in a secret store appropriate for the client environment.
- Check response status codes before trusting JSON payloads, and treat authentication errors as requiring a fresh wallet signature or a clean re-login.
Node.js examples
This example requests a native session-v2 challenge, signs it, calls a protected endpoint with bearer auth, refreshes the session, and logs out.
import { Wallet } from "ethers";
import fetch from "node-fetch";
const API_BASE = "https://api.6529.io/api";
async function assertOk(response, label) {
if (!response.ok) {
throw new Error(`${label} failed with HTTP ${response.status}`);
}
}
export async function loginAndFetchFeed() {
const clientAddress = "0x...";
const clientPrivateKey = "0x..."; // Never hardcode private keys in production.
const wallet = new Wallet(clientPrivateKey);
const nonceResp = await fetch(
`${API_BASE}/auth/session-nonce?signer_address=${clientAddress}&client_type=native&chain_id=1`,
{ headers: { accept: "application/json" } }
);
await assertOk(nonceResp, "session nonce");
const { signable_message, server_signature } = await nonceResp.json();
const clientSignature = await wallet.signMessage(signable_message);
const loginResp = await fetch(`${API_BASE}/auth/session-login`, {
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/json",
},
body: JSON.stringify({
client_type: "native",
client_address: clientAddress,
client_signature: clientSignature,
server_signature,
}),
});
await assertOk(loginResp, "session login");
const session = await loginResp.json();
const feedResp = await fetch(`${API_BASE}/feed`, {
headers: {
accept: "application/json",
authorization: `Bearer ${session.access_token}`,
},
});
await assertOk(feedResp, "feed");
return {
session,
feed: await feedResp.json(),
};
}
export async function refreshNativeSession({ address, nativeRefreshToken }) {
const response = await fetch(`${API_BASE}/auth/session-refresh`, {
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/json",
},
body: JSON.stringify({
client_type: "native",
client_address: address,
native_refresh_token: nativeRefreshToken,
}),
});
await assertOk(response, "session refresh");
const refreshedSession = await response.json();
// Store refreshedSession.native_refresh_token over the previous refresh token.
return refreshedSession;
}
export async function logoutNativeSession({ address, nativeRefreshToken }) {
const response = await fetch(`${API_BASE}/auth/session-logout`, {
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/json",
},
body: JSON.stringify({
client_type: "native",
client_address: address,
native_refresh_token: nativeRefreshToken,
all_sessions: false,
}),
});
await assertOk(response, "session logout");
}