Authentication Guide

Overview

The Flotac REST API supports two authentication methods:

  1. JWT Bearer Tokens - From Supabase Auth (recommended for user-facing applications)
  2. API Keys - For server-to-server integrations and automation

All API endpoints require authentication except /health and / (root).

Authentication Methods

1. JWT Bearer Tokens (Supabase Auth)

Obtaining a JWT Token

JWT tokens are obtained through Supabase Auth by authenticating users via email/password, OAuth providers, or magic links.

Sign Up a New User:

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

const supabase = createClient(
  'https://chwjbbvcqsxxgaytqlix.supabase.co',
  'YOUR_ANON_KEY'
);

const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure_password_123',
  options: {
    data: {
      company_id: 'your-company-uuid', // Required for multi-tenancy
      role: 'company_user'
    }
  }
});

if (data.session) {
  const jwt = data.session.access_token;
  console.log('JWT Token:', jwt);
}

Sign In an Existing User:

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

if (data.session) {
  const jwt = data.session.access_token;
  // Use this token for API requests
}

Using JWT in API Requests:

const BASE_URL = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway';

const response = await fetch(`${BASE_URL}/api/v1/customers`, {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${jwt}`,
    'Content-Type': 'application/json'
  }
});

const result = await response.json();

Token Refresh

JWT tokens expire after 1 hour. Use Supabase's automatic token refresh:

// Automatic refresh (recommended)
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'TOKEN_REFRESHED') {
    const newJwt = session.access_token;
    // Update your token storage
  }
});

// Manual refresh
const { data, error } = await supabase.auth.refreshSession();
if (data.session) {
  const newJwt = data.session.access_token;
}

2. API Keys

API keys are intended for server-to-server integrations, automation scripts, and third-party applications.

API Key Format

flotac_api_key_(live|test)_[64_hexadecimal_characters]

Examples:

  • Live: flotac_api_key_live_a1b2c3d4e5f6...
  • Test: flotac_api_key_test_x9y8z7w6v5u4...

Using API Keys in Requests

API keys are sent in the Authorization header as Bearer tokens:

curl -X GET "https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway/api/v1/customers" \
  -H "Authorization: Bearer flotac_api_key_live_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json"

JavaScript:

const FLOTAC_API_KEY = 'flotac_api_key_live_a1b2c3d4e5f6...';
const BASE_URL = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway';

const response = await fetch(`${BASE_URL}/api/v1/customers`, {
  headers: {
    'Authorization': `Bearer ${FLOTAC_API_KEY}`,
    'Content-Type': 'application/json'
  }
});

Python:

import requests

FLOTAC_API_KEY = 'flotac_api_key_live_a1b2c3d4e5f6...'
BASE_URL = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway'

response = requests.get(
    f'{BASE_URL}/api/v1/customers',
    headers={'Authorization': f'Bearer {FLOTAC_API_KEY}'}
)

Multi-Tenant Authentication

Company ID Scoping

All API requests are automatically scoped to a company using company_id:

  1. JWT Tokens: Extracted from user_metadata.company_id
  2. API Keys: Extracted from api_keys.company_id table

How It Works

Request Flow:

1. Client sends request with JWT/API Key
2. Auth middleware extracts company_id
3. All database queries filter by company_id
4. Response contains only company-scoped data

Security Implications

  • No cross-company data leakage: Users can ONLY access data for their company
  • No RLS policies: Security enforced at application level
  • Mandatory company_id: All requests must have valid company context

Security Best Practices

1. Protect Your Credentials

DO:

  • Store API keys in environment variables
  • Use .env files (excluded from version control)
  • Rotate API keys regularly (annually or on compromise)
  • Use different keys for development and production

DON'T:

  • Hard-code API keys in source code
  • Commit keys to version control
  • Share keys via email or Slack
  • Use production keys in test environments

2. Token Storage

Browser Applications:

  • Store JWT in httpOnly cookies (preferred)
  • Or use sessionStorage (cleared on tab close)
  • Avoid localStorage for sensitive tokens

Server Applications:

  • Store API keys in environment variables
  • Use secrets management (AWS Secrets Manager, HashiCorp Vault)

3. HTTPS Only

All API requests MUST use HTTPS. The API gateway rejects HTTP requests.

4. Rate Limiting

The API implements rate limiting per company_id:

  • Default: 100 requests per minute
  • Burst: Up to 200 requests allowed
  • Exceeded: HTTP 429 with Retry-After header

Handle Rate Limits:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || 60;
      console.log(`Rate limited. Retrying after ${retryAfter}s...`);
      await sleep(retryAfter * 1000);
      continue;
    }

    return response;
  }
  throw new Error('Max retries exceeded');
}

Troubleshooting

401 Unauthorized

Possible Causes:

  1. Missing Authorization header
  2. Invalid or expired JWT token
  3. Invalid API key
  4. API key revoked or expired

Solutions:

  1. Verify header format: Authorization: Bearer <token>
  2. Refresh JWT token (1-hour expiry)
  3. Check API key format and hash
  4. Verify API key is active

403 Forbidden

Possible Causes:

  1. Missing company_id in user metadata
  2. API key lacks required permissions
  3. Attempting to access another company's data

Solutions:

  1. Ensure user signup includes company_id in metadata
  2. Check API key permissions in database
  3. Verify company_id matches requested resource

429 Rate Limit Exceeded

Solutions:

  1. Implement exponential backoff
  2. Cache responses client-side
  3. Batch requests where possible
  4. Contact support for rate limit increase

Next Steps