Skip to main content

Overview

iStory supports two authentication methods:
  1. Wallet Authentication - Sign a message with your Web3 wallet
  2. Google OAuth - Sign in with Google via Supabase Auth
Both methods return a Bearer token that must be included in all API requests.

Authentication Flow

Wallet Authentication

1

Request Nonce

Client requests a one-time nonce from the server
2

Sign Message

User signs the message containing the nonce with their wallet
3

Verify Signature

Server verifies the signature and nonce
4

Return JWT

Server returns a custom JWT token

Google OAuth

1

OAuth Redirect

User clicks “Sign in with Google”
2

Callback

Google redirects to /api/auth/callback
3

Session Created

Supabase creates session and returns JWT

Get Nonce

Generate a server-side nonce for wallet signature verification.

Query Parameters

address
string
required
Ethereum wallet address (checksummed or lowercase)

Response

message
string
Full message to sign, including nonce and timestamp
nonce
string
UUID v4 nonce for replay prevention

Example Request

curl "https://istory.vercel.app/api/auth/nonce?address=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"

Example Response

{
  "message": "Welcome to EStory\n\nSign this message to log in securely.\n\nSite: eStory\nAddress: 0x742d35cc6634c0532925a3b844bc9e7595f0beb\n\nNo transaction · No gas fees · Completely free\n\nNonce: 550e8400-e29b-41d4-a716-446655440000\nTimestamp: 2026-03-04T10:30:00.000Z\nExpires: 2026-03-04T10:35:00.000Z",
  "nonce": "550e8400-e29b-41d4-a716-446655440000"
}
Nonces expire after 5 minutes and can only be used once.

Wallet Login

Verify wallet signature and return authentication token.

Request Body

address
string
required
Ethereum wallet address
signature
string
required
Hexadecimal signature (0x-prefixed)
message
string
required
The exact message that was signed (from /api/auth/nonce)

Response

success
boolean
Always true on successful authentication
user
object
User profile object
wallet_token
string
JWT token for API authentication (use as Bearer token)

Example Request

curl -X POST https://istory.vercel.app/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "signature": "0xabcdef1234567890...",
    "message": "Welcome to EStory...\nNonce: 550e8400-e29b-41d4-a716-446655440000..."
  }'

Example Response

{
  "success": true,
  "user": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "wallet_address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb",
    "auth_provider": "wallet",
    "is_onboarded": false
  },
  "wallet_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Error Responses


Using the Token

Once authenticated, include the token in all API requests:
const response = await fetch('https://istory.vercel.app/api/stories', {
  headers: {
    'Authorization': `Bearer ${wallet_token}`
  }
});

Token Validation

The server validates tokens using two methods:
  1. Supabase JWT (Google OAuth users)
    • Validated via supabase.auth.getUser(token)
    • Returns user.id from Supabase Auth
  2. Custom Wallet JWT (wallet-authenticated users)
    • Validated via verifyWalletToken(token) from /lib/jwt.ts
    • Returns userId from JWT payload
Tokens are validated on every request. Invalid or expired tokens return 401 Unauthorized.

Security Best Practices

Nonce Replay Prevention

Each nonce can only be used once and expires after 5 minutes

No Gas Fees

Signature verification is off-chain and completely free

Message Integrity

The signed message includes timestamp and expiry for audit trail

Wallet Ownership

Signature proves wallet ownership without revealing private keys

Implementation Notes

Nonce Storage

Nonces are stored in-memory on the server with automatic cleanup:
// lib/nonce.ts
const NONCE_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes

JWT Signing

Wallet JWTs are signed with HS256 algorithm:
// lib/jwt.ts
await SignJWT({ userId, walletAddress })
  .setProtectedHeader({ alg: 'HS256' })
  .setIssuedAt()
  .setExpirationTime('7d')
  .sign(secret);
JWT tokens expire after 7 days.

Next Steps

AI Endpoints

Use AI for transcription and analysis

Story Endpoints

Create and manage journal entries

Build docs developers (and LLMs) love