Skip to main content

API Authentication

This guide covers authentication methods for the Artbase API.

Overview

Artbase uses different authentication methods depending on the API type:

API TypeAuth MethodUse Case
PublicNoneGallery browsing, public data
User APIsSupabase AuthUser-specific actions
Server APIsAPI KeyServer-to-server
WebhooksSignature verificationIncoming webhooks

Supabase Authentication

Most user-facing APIs use Supabase Auth with JWT tokens.

Getting a Token

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'password',
});

// Get session token
const token = data.session?.access_token;

Using the Token

Include the token in the Authorization header:

curl -X GET "https://api.artbase.studio/v1/products" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token Refresh

Tokens expire after 1 hour. Refresh automatically:

// Supabase client handles refresh automatically
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'TOKEN_REFRESHED') {
// New token available
}
});

API Keys

For server-to-server communication, use API keys.

Generating Keys

  1. Go to Creator Hub > Settings > API
  2. Click Generate New Key
  3. Copy the key (only shown once)

Using API Keys

curl -X GET "https://api.artbase.studio/v1/products" \
-H "Authorization: Bearer sk_live_abc123..."

Key Security

  • Never expose keys in client-side code
  • Store keys in environment variables
  • Rotate keys periodically
  • Use least-privilege keys when possible

Key Scopes

ScopeAccess
read:productsRead products
write:productsCreate/update products
read:ordersRead orders
write:ordersUpdate orders
adminFull access

Webhook Verification

Verify incoming webhooks using signatures.

Stripe Webhooks

import Stripe from 'stripe';

const stripe = new Stripe(STRIPE_SECRET_KEY);

export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('stripe-signature');

const event = stripe.webhooks.constructEvent(
body,
signature,
STRIPE_WEBHOOK_SECRET
);

// Process event
}

Custom Webhooks

For webhooks you send:

import crypto from 'crypto';

function verifySignature(payload: string, signature: string, secret: string) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}

Organization Context

Most APIs require organization context:

Query Parameter

GET /api/products?org_id=org_abc123
X-Organization-ID: org_abc123

From Session

The server extracts org from user's membership.

Error Responses

401 Unauthorized

{
"error": "Unauthorized",
"message": "Invalid or missing authentication token"
}

Causes:

  • Missing Authorization header
  • Expired token
  • Invalid token format

403 Forbidden

{
"error": "Forbidden",
"message": "You don't have permission to access this resource"
}

Causes:

  • User lacks required role
  • Accessing another organization's data
  • API key lacks required scope

Best Practices

Token Storage

// Good: HttpOnly cookie (for web)
// Good: Secure storage (for mobile)
// Bad: localStorage (XSS vulnerable)

Key Rotation

  1. Generate new key
  2. Update all services
  3. Verify new key works
  4. Revoke old key

Least Privilege

Create keys with minimal scopes:

// Good: specific scopes
const key = await createApiKey({
scopes: ['read:products', 'read:orders']
});

// Bad: admin access for simple tasks
const key = await createApiKey({
scopes: ['admin']
});