Authentication

Authentication (SIWE)

Authenticate using Sign-In with Ethereum (SIWE). This exchanges a wallet signature for a secure session cookie used in write endpoints (images, metadata, projects, comments).

Endpoints

GET
/api/auth/nonce?address={wallet_address}
Generates a one-time nonce for the given wallet address. Must be called before signing.
Response
{ "nonce": "a1b2c3d4e5f6" }
POST
/api/auth/verify
Verifies the signed SIWE message. Returns a Set-Cookie header with your session.

Request Body

Response
{
  "message": "the prepared SIWE message string",
  "signature": "0x..."
}

Response

Response
{ "ok": true, "address": "0x..." }
200 OK422 Invalid nonce or signature

Node.js Example

Example Code
import { ethers } from "ethers";
import { SiweMessage } from "siwe";

const BASE_URL = "https://launchonbasis.com";

async function authenticate() {
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY);
  const address = await wallet.getAddress();

  // 1. Get nonce
  const nonceRes = await fetch(`${BASE_URL}/api/auth/nonce?address=${address}`);
  const { nonce } = await nonceRes.json();

  // 2. Create & sign SIWE message
  const message = new SiweMessage({
    domain: "launchonbasis.com",
    address,
    statement: "Sign in to Basis API.",
    uri: BASE_URL,
    version: "1",
    chainId: 56,
    nonce,
  });

  const preparedMessage = message.prepareMessage();
  const signature = await wallet.signMessage(preparedMessage);

  // 3. Verify — returns session cookie
  const verifyRes = await fetch(`${BASE_URL}/api/auth/verify`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ message: preparedMessage, signature }),
  });

  // Extract the session cookie for subsequent requests
  const sessionCookie = verifyRes.headers.get("set-cookie");
  return sessionCookie;
}

Python Example

Example Code
import requests
from eth_account import Account
from eth_account.messages import encode_defunct
from datetime import datetime
import os

BASE_URL = "https://launchonbasis.com"
private_key = os.environ["PRIVATE_KEY"]
account = Account.from_key(private_key)
address = account.address

# 1. Get nonce
nonce = requests.get(f"{BASE_URL}/api/auth/nonce?address={address}").json()["nonce"]

# 2. Construct & sign SIWE message
issued_at = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
message = (
    f"launchonbasis.com wants you to sign in with your Ethereum account:\n"
    f"{address}\n\nSign in to Basis API.\n\n"
    f"URI: {BASE_URL}\nVersion: 1\nChain ID: 56\n"
    f"Nonce: {nonce}\nIssued At: {issued_at}"
)
signature = Account.sign_message(
    encode_defunct(text=message), private_key=private_key
).signature.hex()

# 3. Verify — session object stores the cookie automatically
session = requests.Session()
session.post(f"{BASE_URL}/api/auth/verify", json={"message": message, "signature": signature})
# Use 'session' for all subsequent requests