# How Supabase Auth Powers the GPT-Image 2 Website: Architecture and Implementation

> Discover how Supabase Auth secures the GPT-Image 2 website with dual-layer authentication, including public and service-role clients. Learn about JWT validation and secure API operations.

- Repository: [苍何/awesome-gpt-image-2](https://github.com/freestylefly/awesome-gpt-image-2)
- Tags: architecture
- Published: 2026-09-06

---

**The GPT-Image 2 site uses a dual-layer Supabase authentication system: a lightweight public client for the browser UI and a privileged service-role client for secure server-side API operations, with JWT validation via `getAuthContext` on every protected endpoint.**

The [freestylefly/awesome-gpt-image-2](https://github.com/freestylefly/awesome-gpt-image-2) repository demonstrates a production-ready pattern for handling user authentication in a modern AI image generation platform. This article breaks down how Supabase Auth is implemented across both client and server layers, with specific file paths and code patterns extracted directly from the source.

## Architecture Overview: Dual-Layer Supabase Auth

The GPT-Image 2 website splits Supabase Auth responsibilities across two distinct layers:

| Layer | Purpose | Key File | Authentication Method |
|-------|---------|----------|----------------------|
| **Client-side** | UI interactions, user session management | [`src/supabaseClient.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/src/supabaseClient.js) | Anonymous key (public) |
| **Server-side** | Protected API routes, privileged database operations | [`api/_lib/supabase.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/supabase.js) | Service-role key (admin) |

This separation ensures the browser operates with minimal privileges while the backend performs security-sensitive actions with elevated permissions.

## Client-Side Supabase Auth Implementation

The front-end initializes Supabase with environment variables exposed to the browser, using the **anonymous key** for unprivileged operations.

### Public Client Configuration

```javascript
// src/supabaseClient.js – public client for browser
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const isSupabaseConfigured = Boolean(supabaseUrl && supabaseAnonKey);

export const supabase = isSupabaseConfigured
  ? createClient(supabaseUrl, supabaseAnonKey, {
      auth: {
        autoRefreshToken: true,
        detectSessionInUrl: true,
        persistSession: true,
      },
    })
  : null;

```

The `isSupabaseConfigured` helper guards all Supabase-dependent UI code, preventing runtime errors when environment variables are missing.

### Typical Client-Side Usage Pattern

```javascript
// Fetch current user and communicate with protected API
if (supabase) {
  const { data: { user } } = await supabase.auth.getUser();
  if (user) {
    const token = supabase.auth.session()?.access_token;
    
    const resp = await fetch('/api/me', {
      headers: { Authorization: `Bearer ${token}` },
    });
    
    const { profile } = await resp.json();
    // Render profile in UI
  }
}

```

The client retrieves the JWT from the active session and transmits it via the `Authorization` header to backend endpoints.

## Server-Side Supabase Auth: Secure API Protection

Protected API routes rely on [`api/_lib/supabase.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/supabase.js), which exposes **service-role** functionality and JWT validation.

### Core Server-Side Exports

| Export | Function |
|--------|----------|
| `getSupabaseConfig()` | Reads `VITE_SUPABASE_URL` / `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` from environment |
| `isSupabaseServerConfigured()` | Boolean guard for server-only operations |
| `getSupabaseAdminClient()` | Returns singleton service-role Supabase client |
| `getBearerToken(req)` | Parses `Authorization` header |
| `getAuthContext(req, { allowAnonymous })` | Validates JWT, ensures profile exists, returns auth context |
| `ensureProfileForUser(user)` | Upserts profile row on first login |
| `getProfileById(userId)` | Fetches profile with membership data |

### JWT Validation Flow in `getAuthContext`

Every protected endpoint follows this sequence:

1. Extract `Authorization: Bearer <jwt>` from request headers
2. Verify token via `client.auth.getUser`
3. Ensure user profile exists via `ensureProfileForUser`
4. Return `{ user, profile, token, client }` or error payload

### Protected Endpoint Example: Generation Status

```javascript
// api/generation/status.js – protected endpoint
import { getAuthContext } from '../_lib/supabase.js';

export default async function handler(req, res) {
  // 1️⃣ Validate JWT and build auth context
  const auth = await getAuthContext(req);
  if (auth.error) {
    return res.status(auth.status || 401).json({
      ok: false,
      error: auth.error,
      loginRequired: true,
    });
  }

  // 2️⃣ Use admin client for privileged data access
  const reservation = await findPlatformGeneration(
    auth.client,
    taskId,
    auth.user.id
  );
  // ... continue processing
}

```

Source: [api/generation/status.js lines 35-38](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generation/status.js#L35-L38)

Failure to authenticate returns **401 `AUTH_REQUIRED`** with `loginRequired: true`, triggering the UI to display a login screen.

## Complete Authentication Flow

The end-to-end Supabase Auth flow in GPT-Image 2 operates as follows:

1. **User login**: Front-end Supabase UI returns a JWT
2. **Token storage**: Browser persists JWT (localStorage/Supabase-managed)
3. **Request transmission**: Every `/api/*` call includes `Authorization: Bearer <jwt>`
4. **Server validation**: `getAuthContext` verifies JWT and builds profile context
5. **Access enforcement**: Invalid or missing tokens return 401, triggering re-authentication

## Key Implementation Files in the Repository

| File | Role |
|------|------|
| [[`src/supabaseClient.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/src/supabaseClient.js)](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/src/supabaseClient.js) | Public browser client with auto-refresh and session persistence |
| [[`api/_lib/supabase.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/supabase.js)](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/supabase.js) | Server auth utilities and service-role client |
| [[`api/generation/status.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generation/status.js)](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generation/status.js) | Protected status endpoint using `getAuthContext` |
| [[`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js)](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) | Image generation with auth validation |
| [[`api/me.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/me.js)](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/me.js) | Profile retrieval endpoint |

## Summary

- **Dual-client architecture**: Anonymous key for browser, service-role key for server
- **Centralized auth logic**: `getAuthContext` in [`api/_lib/supabase.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/supabase.js) handles all JWT validation
- **Automatic profile provisioning**: `ensureProfileForUser` creates profiles on first login
- **Consistent error handling**: 401 responses with `loginRequired` flag drive UI state
- **Defensive programming**: `isSupabaseConfigured` and `isSupabaseServerConfigured` guards prevent runtime failures

## Frequently Asked Questions

### How does GPT-Image 2 prevent unauthorized API access?

Every protected endpoint calls `getAuthContext(req)`, which validates the JWT against Supabase and returns an error object if authentication fails. The handler checks `auth.error` and returns 401 before executing any privileged operations.

### What happens when a user first logs in to the site?

The `ensureProfileForUser` function automatically creates a profile row in the database. This upsert pattern guarantees that every authenticated user has a corresponding profile record without requiring manual registration steps.

### Can the site operate without Supabase configured?

Yes. Both client and server layers include configuration guards: `isSupabaseConfigured` on the front-end and `isSupabaseServerConfigured` for API routes. These booleans conditionally disable Supabase-dependent features, allowing the codebase to run in development or fallback modes.

### Why use a service-role key on the server instead of verifying the user's JWT directly?

The service-role key enables **privileged operations** that exceed the user's permissions, such as reading other users' public generations or modifying subscription status. The JWT validation step (`client.auth.getUser`) authenticates the user identity, while the service-role client performs the actual database work with elevated privileges.