How Supabase Powers Authentication and Billing in Awesome-GPT-Image-2
Supabase serves as the central backend infrastructure for the Awesome-GPT-Image-2 project, handling client-side authentication, server-side admin operations, and persistent storage for user profiles, credits, and subscriptions.
In the freestylefly/awesome-gpt-image-2 repository, Supabase functions as the complete backend-as-a-service platform that enables user authentication, session management, and monetization features. The implementation splits Supabase capabilities between browser-based client operations and privileged server-side admin functions, creating a secure architecture for managing paid image-generation limits and community features.
The Dual Architecture: Client and Server Roles
The project utilizes Supabase in two distinct modes that work in tandem to provide secure, scalable user management.
Client-Side Authentication and Session Handling
On the frontend, the application initializes a standard Supabase client in src/supabaseClient.js using the public URL and anonymous key from Vite environment variables. This client manages automatic token refresh, URL-based session detection, and persistent authentication states for logged-in users.
The export includes a configuration checker (isSupabaseConfigured) that allows the application to degrade gracefully when Supabase credentials are absent. When configured, the client enables OAuth and magic link authentication flows directly within the browser.
// Front-end: create a Supabase client (only if env vars are present)
import { supabase, isSupabaseConfigured } from './supabaseClient.js';
if (isSupabaseConfigured) {
// Example: sign-in with magic link
await supabase.auth.signInWithOtp({ email: 'user@example.com' });
}
Server-Side Admin Operations
For API routes, the project employs a privileged Supabase admin client instantiated in api/_lib/supabase.js using the service-role key. This server-side client bypasses row-level security (RLS) policies to perform administrative tasks such as verifying bearer tokens, creating user profiles, and managing billing data.
Key server-side functions include:
getAuthContext(req)– Extracts and validates Bearer tokens from incoming requestsensureProfileForUser– Creates or updates user records in theprofilestablegetMembershipForUser– Retrieves subscription status and credit balancesnormalizeProfile– Formats raw database rows into frontend-compatible objects
// Back-end: protect an API route
import { getAuthContext, isSupabaseServerConfigured } from '../_lib/supabase.js';
export async function handler(req, res) {
if (!isSupabaseServerConfigured()) {
return res.status(500).json({ error: 'SERVER_NOT_CONFIGURED' });
}
const { user, profile, error, status } = await getAuthContext(req);
if (error) return res.status(status).json({ error });
// `user` is the Supabase auth user, `profile` contains credits, membership, etc.
res.json({
message: `Hello ${profile.fullName}!`,
credits: profile.creditBalance
});
}
Authentication Flow and Session Management
The integration follows a strict flow that ensures security while maintaining flexibility for deployments without backend configuration.
Configuration Detection
Before initializing any Supabase functionality, both client and server check for required environment variables. The isSupabaseConfigured function checks for SUPABASE_URL and the anon key on the frontend, while isSupabaseServerConfigured validates the service-role key on the backend. If checks fail, the application disables Supabase features and returns specific error codes, allowing the static site to function without database connectivity.
Token Validation and Profile Resolution
Every protected API route begins with a call to getAuthContext(req), which performs three critical operations:
- Extracts the
Bearertoken from the HTTP Authorization header - Validates the token against Supabase Auth (
client.auth.getUser) - Ensures a corresponding profile exists via
ensureProfileForUser, creating one if necessary
This context object—containing { user, profile, token, client }—becomes the authority for all subsequent permission checks and database operations.
Data Persistence Models
Supabase stores persistent data across several interconnected tables:
profiles– Core user data including display names, avatar URLs, and credit balancesuser_memberships– Subscription tiers, expiration dates, and feature flags- Audit tables – Transaction logs for billing events and image generation quotas
Functions like formatMembership and normalizeProfile transform these raw SQL rows into structured objects consumed by the React frontend.
Implementing Credit and Billing Systems
The admin client enables secure financial operations that would be impossible with client-side keys alone.
Checking Credit Balances
Server-side functions query the profiles table to retrieve current credit balances before allowing expensive operations like AI image generation. The implementation demonstrates direct table access with the service-role client:
// Server-side: fetch a user's credit balance
import { getSupabaseAdminClient } from '../_lib/supabase.js';
async function getUserCredits(userId) {
const client = getSupabaseAdminClient();
const { data } = await client
.from('profiles')
.select('credit_balance')
.eq('id', userId)
.single();
return data?.credit_balance ?? 0;
}
Webhook Handling
The api/_lib/supabase.js module also supports payment webhooks (including Alipay integrations), using the admin client to update membership records and credit balances upon successful transactions. These operations occur in api/billing/* routes that verify webhook signatures before modifying database state.
Summary
- Supabase provides the complete backend infrastructure for Awesome-GPT-Image-2, replacing the need for a custom authentication server or database API layer.
- Dual client architecture separates public authentication (browser) from privileged operations (server) using distinct API keys and initialization patterns in
src/supabaseClient.jsandapi/_lib/supabase.js. - Automatic profile provisioning ensures every authenticated user has a corresponding database record with default credits and membership status.
- Configuration-aware initialization allows the application to run in static/demo mode without Supabase credentials, returning
SERVER_NOT_CONFIGUREDerrors only when backend features are explicitly requested. - Row-level security bypass via the service-role key enables secure credit management and billing webhooks while keeping the frontend client restricted to user-owned data.
Frequently Asked Questions
What is the difference between the Supabase client and admin client in this project?
The client (in src/supabaseClient.js) runs in the browser with an anonymous key and respects row-level security policies, handling login flows and session persistence for individual users. The admin client (in api/_lib/supabase.js) runs on the server with a service-role key, bypassing all security policies to manage user profiles, verify tokens, and process billing webhooks across the entire database.
How does the application handle missing Supabase configuration?
Both frontend and backend implement configuration checks—isSupabaseConfigured and isSupabaseServerConfigured respectively—that verify the presence of SUPABASE_URL and appropriate API keys. If these checks fail, the Supabase client returns null on the frontend and API routes return HTTP 500 with SERVER_NOT_CONFIGURED, allowing the site to function as a static demo without database connectivity.
What database tables does Supabase manage for user data?
The schema includes a profiles table storing user metadata and credit balances, and a user_memberships table tracking subscription tiers and expiration dates. Migration files in supabase/migrations/*.sql define these structures, while utility functions like normalizeProfile and formatMembership translate database rows into application objects.
How are authentication tokens validated in API routes?
Protected endpoints call getAuthContext(req) from api/_lib/supabase.js, which extracts the Bearer token from the Authorization header and validates it against Supabase Auth using client.auth.getUser. This function also ensures the user has a corresponding profile record via ensureProfileForUser, returning a complete context object containing the user identity, profile data, and authenticated client for subsequent database operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →