Authentication & Authorization
PIP AI uses Supabase Auth for authentication and Row Level Security (RLS) for authorization.
Authentication Flow
sequenceDiagram
participant U as User
participant App as Nuxt App
participant Auth as Supabase Auth
participant DB as PostgreSQL
U->>App: Navigate to protected route
App->>App: auth middleware checks session
alt No session
App->>U: Redirect to /auth/login
U->>Auth: Sign in (email/password)
Auth->>Auth: Validate credentials
Auth->>U: Return JWT + refresh token
U->>App: Redirect to callback
App->>App: Store session
end
App->>DB: Query with JWT (RLS enforced)
DB->>App: Filtered resultsUser Roles
| Role | Capabilities |
|---|---|
| Admin | Full access to all data, manage users, upload specs, access all projects |
| User | Create/manage own projects, access specs for brands in their projects only |
First User Setup
The first user to register is automatically promoted to admin via the pip_ai_handle_new_user() trigger.
JWT Custom Claims
A JWT hook (pip_ai_custom_access_token_hook) adds custom claims to every access token:
{
"role": "admin",
"is_active": true
}Auth Middleware
app/middleware/auth.ts
Checks for a valid Supabase session. Redirects unauthenticated users to /auth/login.
When NUXT_PUBLIC_DEV_AUTH_BYPASS=true, this middleware is completely skipped.
app/middleware/admin.ts
Restricts routes to admin users by checking the user's role via the pip_ai_check_is_admin() RPC function.
Row Level Security (RLS)
All tables have RLS enabled. Policies use helper functions to avoid infinite recursion:
Key Helper Functions
| Function | Purpose |
|---|---|
pip_ai_check_is_admin() | SECURITY DEFINER function checking admin role (avoids RLS recursion) |
pip_ai_is_project_member(project_id) | Check if current user is a member of a project |
pip_ai_can_edit_project(project_id) | Check if current user can edit a project (owner/editor/admin) |
pip_ai_get_project_role(project_id) | Get user's role in a project |
Policy Summary
| Data | Who Can Read | Who Can Write |
|---|---|---|
| Projects | Members + Admin | Owner/Editor + Admin |
| PIP Items | Project members + Admin | Project editors + Admin |
| Spec Sections | Admin + users with matching brand projects | Admin only |
| Matches | Via PIP item membership | Via PIP item edit access |
| Floor Plans | Project members + Admin | Project editors + Admin |
| User Profiles | Own profile + Admin | Own profile + Admin |
Brand Isolation
Users can only read pip_ai_spec_sections where the brand_name matches a brand they have projects for:
brand_name IN (
SELECT DISTINCT brand_name
FROM pip_ai_projects
WHERE created_by = auth.uid()
)Dev Auth Bypass
For local development, set:
NUXT_PUBLIC_DEV_AUTH_BYPASS=trueEffects:
- Auth middleware skipped
- Supabase redirect disabled in
nuxt.config.ts - Mock user session created
- Client-side RLS effectively bypassed
DANGER
Never enable dev auth bypass in production.
Server-Side Auth
Server routes (server/api/) use the service role key to bypass RLS:
const client = await serverSupabaseServiceRole(event)Webhook callbacks authenticate via a shared secret header:
const secret = event.headers.get('x-webhook-secret')Project Membership
Projects support collaborative access with three roles:
| Role | View | Edit | Delete | Manage Members |
|---|---|---|---|---|
| Owner | Yes | Yes | Yes | Yes |
| Editor | Yes | Yes | No | No |
| Viewer | Yes | No | No | No |
When a project is created, the creator is automatically added as owner via a database trigger.