Claude
Skills
Sign in
Back

supabase-auth-storage-realtime-core

Included with Lifetime
$97 forever

Implement Supabase Auth (signUp, signIn, OAuth, session management), Storage (upload, download, signed URLs, bucket policies), and Realtime (Postgres changes, broadcast, presence). Use when building user auth flows, file upload features, or live-updating UIs with Supabase. Trigger with phrases like "supabase auth", "supabase storage upload", "supabase realtime subscribe", "supabase oauth", "supabase file upload", "supabase presence", "supabase rls storage".

Backend & APIssaassupabaseauthstoragerealtimerls

What this skill does

# Supabase Auth + Storage + Realtime Core

## Overview

Implement the three pillars that turn a Supabase database into a full application backend: user authentication (email/password, OAuth, magic links, session lifecycle), file storage (uploads, downloads, signed URLs, bucket-level RLS policies), and real-time subscriptions (Postgres change events, client-to-client broadcast, presence tracking). Every operation integrates with Row-Level Security through `auth.uid()`.

## Prerequisites

- Supabase project created at [supabase.com/dashboard](https://supabase.com/dashboard)
- `@supabase/supabase-js` v2 installed (`npm install @supabase/supabase-js`)
- `SUPABASE_URL` and `SUPABASE_ANON_KEY` available from project Settings > API
- For Python: `pip install supabase` (wraps `postgrest-py`, `gotrue-py`, `storage3`, `realtime-py`)

## Instructions

### Step 1: Auth — User Registration, Login, and OAuth

Initialize the client and implement the three primary auth flows: email/password, OAuth provider, and passwordless magic link.

**TypeScript**

```typescript
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!
)

// ── Sign up a new user ──
const { data: signUpData, error: signUpError } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'secure-password-123',
  options: {
    data: { username: 'newuser', full_name: 'New User' },  // → raw_user_meta_data
  },
})
// If email confirmation enabled: data.user exists but data.session is null
// If email confirmation disabled: both data.user and data.session are present

// ── Sign in with password ──
const { data: signInData, error: signInError } = await supabase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'secure-password-123',
})
const { user, session } = signInData
// session.access_token → JWT for authenticated API calls

// ── Sign in with OAuth (Google) ──
const { data: oauthData, error: oauthError } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://myapp.com/auth/callback',
    queryParams: { access_type: 'offline', prompt: 'consent' },
  },
})
// Redirect user to oauthData.url in the browser

// ── Sign in with GitHub ──
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: { redirectTo: 'https://myapp.com/auth/callback' },
})

// ── Passwordless magic link ──
const { error: otpError } = await supabase.auth.signInWithOtp({
  email: '[email protected]',
  options: { emailRedirectTo: 'https://myapp.com/auth/callback' },
})

// ── Handle OAuth/magic link callback (in /auth/callback route) ──
const { data: { session: cbSession }, error: cbError } =
  await supabase.auth.exchangeCodeForSession(code)
```

**Session management — every app needs these:**

```typescript
// Get current session (reads from local storage, no network call)
const { data: { session } } = await supabase.auth.getSession()

// Get current user (validates JWT against server)
const { data: { user } } = await supabase.auth.getUser()

// Listen for auth state changes — critical for reactive UIs
const { data: { subscription } } = supabase.auth.onAuthStateChange(
  (event, session) => {
    // event: 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED'
    //        'INITIAL_SESSION' | 'PASSWORD_RECOVERY' | 'MFA_CHALLENGE_VERIFIED'
    console.log('Auth event:', event, session?.user?.email)
  }
)
// Clean up when component unmounts
subscription.unsubscribe()

// Sign out (clears session from storage)
await supabase.auth.signOut()

// Password reset (sends email with reset link)
await supabase.auth.resetPasswordForEmail('[email protected]', {
  redirectTo: 'https://myapp.com/auth/reset-password',
})
```

**Python**

```python
from supabase import create_client

supabase = create_client(
    "https://your-project.supabase.co",
    "your-anon-key"
)

# Sign up
result = supabase.auth.sign_up({
    "email": "[email protected]",
    "password": "secure-password-123",
    "options": {"data": {"username": "newuser"}},
})

# Sign in with password
result = supabase.auth.sign_in_with_password({
    "email": "[email protected]",
    "password": "secure-password-123",
})
access_token = result.session.access_token

# Get current session
session = supabase.auth.get_session()

# Sign out
supabase.auth.sign_out()
```

### Step 2: Storage — Upload, Download, and Secure with Bucket Policies

Supabase Storage organizes files into buckets. Public buckets serve files via CDN URLs; private buckets require signed URLs or authenticated requests.

**TypeScript**

```typescript
// ── Upload a file ──
const file = new File(['hello world'], 'hello.txt', { type: 'text/plain' })
const { data, error } = await supabase.storage
  .from('avatars')  // bucket name
  .upload('user123/avatar.png', file, {
    cacheControl: '3600',
    upsert: false,       // true → overwrite existing
    contentType: 'image/png',
  })
// data.path → 'user123/avatar.png'

// ── Download a file ──
const { data: blob, error: dlError } = await supabase.storage
  .from('avatars')
  .download('user123/avatar.png')
// blob is a Blob object — use URL.createObjectURL(blob) for display

// ── Get public URL (public buckets only, no auth required) ──
const { data: { publicUrl } } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/avatar.png')
// publicUrl → 'https://<project>.supabase.co/storage/v1/object/public/avatars/user123/avatar.png'

// ── Create signed URL (private buckets, time-limited access) ──
const { data: signedUrlData, error: signError } = await supabase.storage
  .from('documents')
  .createSignedUrl('reports/q4-2025.pdf', 3600)  // expires in 1 hour
// signedUrlData.signedUrl → one-time use URL with token parameter

// ── List files in a path ──
const { data: files, error: listError } = await supabase.storage
  .from('documents')
  .list('reports', {
    limit: 100,
    offset: 0,
    sortBy: { column: 'name', order: 'asc' },
  })

// ── Delete files ──
const { error: removeError } = await supabase.storage
  .from('documents')
  .remove(['reports/old-report.pdf', 'reports/draft.docx'])
```

**Bucket RLS policies — enforce access control in SQL migrations:**

```sql
-- Create buckets (run in a migration or SQL editor)
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);   -- public: anyone can read

INSERT INTO storage.buckets (id, name, public)
VALUES ('documents', 'documents', false);  -- private: signed URLs only

-- Allow authenticated users to upload to their own folder
-- Convention: store files at <user_id>/filename.ext
CREATE POLICY "avatar_upload"
  ON storage.objects FOR INSERT
  WITH CHECK (
    bucket_id = 'avatars'
    AND auth.uid()::text = (storage.foldername(name))[1]
  );

-- Allow anyone to view avatars (public bucket)
CREATE POLICY "avatar_public_read"
  ON storage.objects FOR SELECT
  USING (bucket_id = 'avatars');

-- Allow users to manage only their own documents (all operations)
CREATE POLICY "documents_user_crud"
  ON storage.objects FOR ALL
  USING (
    bucket_id = 'documents'
    AND auth.uid()::text = (storage.foldername(name))[1]
  )
  WITH CHECK (
    bucket_id = 'documents'
    AND auth.uid()::text = (storage.foldername(name))[1]
  );

-- Allow users to delete only files they uploaded
CREATE POLICY "documents_owner_delete"
  ON storage.objects FOR DELETE
  USING (
    bucket_id = 'documents'
    AND auth.uid() = owner
  );
```

**Python**

```python
# Upload
with open("report.pdf", "rb") as f:
    result = supabase.storage.from_("documents").upload(
        "user123/report.pdf", f,
        {"content-type": "application/pdf", "cache-control": "3600"}
    )

# Download
data = supabase.storage.from_("documents").download("user123/report.pdf")

# Public URL
url = supabase.storage.from_("avatars").get_public_url("user123/avatar.png")

# Signed URL (3600 seconds)
result = supabase.storage.from_(

Related in Backend & APIs