VeztaVezta

Authentication

Wallet-based auth, JWT tokens, and refresh flow

Vezta uses wallet-based authentication supporting both Solana and EVM chains. Instead of passwords, users sign a message with their wallet to prove ownership, and the backend issues JWT tokens.

Auth Flow

  1. Request nonce -- Client sends wallet address and chain, backend returns a unique nonce
  2. Sign message -- Client signs the nonce with their wallet (Phantom, MetaMask, etc.)
  3. Verify signature -- Backend verifies the signature, creates or finds the user, and issues tokens
  4. Use access token -- Client includes the JWT in the Authorization: Bearer header for subsequent requests
  5. Refresh when expired -- Access tokens expire after 15 minutes; use the refresh endpoint to get a new one

Endpoints

MethodPathAuthDescription
POST/api/v1/auth/noncePublicRequest a nonce for wallet signature
POST/api/v1/auth/verifyPublicVerify wallet signature and receive JWT
POST/api/v1/auth/refreshPublicRefresh an expired access token
POST/api/v1/auth/validate-keyPublicValidate a beta access key
POST/api/v1/auth/logoutBearerLogout and invalidate all refresh tokens

Request Nonce

Generate a nonce that the user must sign with their wallet.

// POST /api/v1/auth/nonce
{
  "walletAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "chain": "solana"
}
// Response 201
{
  "nonce": "vezta-auth-1711234567890-abc123def456"
}

Verify Wallet

Submit the signed nonce to authenticate. On success, the backend returns an access token in the response body and sets an httpOnly refresh_token cookie. A non-httpOnly logged_in=1 marker cookie is also set for frontend route protection.

// POST /api/v1/auth/verify
{
  "walletAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "chain": "solana",
  "signature": "3ASDf...base58signature",
  "nonce": "vezta-auth-1711234567890-abc123def456",
  "referralCode": "FRIEND123"
}
// Response 201
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "clxyz...",
    "walletAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "chain": "solana",
    "username": null,
    "avatarUrl": null
  }
}

Refresh Token

Exchange a refresh token for a new access token. The backend accepts the refresh token either in the request body or from the httpOnly cookie. Token rotation is applied -- each refresh issues a new refresh token and invalidates the old one.

// POST /api/v1/auth/refresh
{
  "refreshToken": "previous-refresh-token-value"
}
// Response 200
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "new-refresh-token-value",
  "user": {
    "id": "clxyz...",
    "walletAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "chain": "solana"
  }
}

Token Details

  • Access token: JWT, 15-minute expiry, stored in memory (not localStorage)
  • Refresh token: Opaque token, 7-day expiry, stored as httpOnly cookie with SameSite=None and Secure=true in production
  • JWT payload: { id, walletAddress, chain }

Notes

  • The referralCode field in the verify request is optional and only used on first-time sign-up
  • The chain field accepts solana or evm
  • All auth endpoints except logout are public (no JWT required)
  • Failed signature verification returns a 401 Unauthorized error

On this page