supabase-auth-storage-realtime-core
Execute Supabase secondary workflow: Auth + Storage + Realtime. Use when implementing secondary use case, or complementing primary workflow. Trigger with phrases like "supabase auth storage realtime", "implement full stack features with supabase".
Install
mkdir -p .claude/skills/supabase-auth-storage-realtime-core && curl -L -o skill.zip "https://mcp.directory/api/skills/download/8792" && unzip -o skill.zip -d .claude/skills/supabase-auth-storage-realtime-core && rm skill.zipInstalls to .claude/skills/supabase-auth-storage-realtime-core
About this skill
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
@supabase/supabase-jsv2 installed (npm install @supabase/supabase-js)SUPABASE_URLandSUPABASE_ANON_KEYavailable from project Settings > API- For Python:
pip install supabase(wrapspostgrest-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
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:
// 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
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
// ── 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:
-- 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
# 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_("documents").create_signed_url(
"user123/report.pdf", 3600
)
signed_url = result["signedURL"]
# List files
files = supabase.storage.from_("documents").list("user123")
# Delete
supabase.storage.from_("documents").remove(["user123/old-file.pdf"])
Step 3: Realtime — Postgres Changes, Broadcast, and Presence
Supabase Realtime provides three channel types: database change listeners, client-to-client broadcast, and presence tracking for online status.
Postgres Changes (listen to INSERT/UPDATE/DELETE on tables):
// Subscribe to new messages in a chat table
const channel = supabase
.channel('chat-room')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'messages',
},
(payload) => {
console.log('New message:', payload.new)
// payload.new → full row data
// payload.old → null for INSERT
}
)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'messages',
filter: 'room_id=eq.42', // server-side filter
},
(payload) => {
console.log('Updated:', payload.old, '→', payload.new)
}
)
.on(
'postgres_changes',
{
event: 'DELETE',
schema: 'public',
table: 'messages',
},
(payload) => {
console.log('Deleted:', payload.old)
// payload.new → null for DELETE
}
)
.subscribe((status) => {
console.log('Channel status:', status)
// 'SUBSCRIBED' | 'TIMED_OUT' | 'CLOSED' | 'CHANNEL_ERROR'
})
// Enable Realtime on the table (required one-time setup in SQL)
// ALTER PUBLICATION supabase_realtime ADD TABLE messages;
// Unsubscribe when done
supabase.removeChannel(channel)
RLS integration — Realtime respects row-level security:
-- Only receive changes for rows the user owns
CREATE POLICY "users_own_messages"
ON messages FOR SELECT
USING (auth.uid() = user_id);
-- The Realtime listener will only fire for rows passing this policy
`
---
*Content truncated.*
More by jeremylongshore
View all skills by jeremylongshore →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
pdf-to-markdown
aliceisjustplaying
Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.
Related MCP Servers
Browse all serversPica is automated workflow software for business process automation, integrating actions across services via a unified i
Tenzir: Execute cybersecurity data workflows with OCSF-compatible pipelines to retrieve structured security events for e
Connect Blender to Claude AI for seamless 3D modeling. Use AI 3D model generator tools for faster, intuitive, interactiv
Security scanner for AI agents, MCP servers, and agent skills. Automatically scan code for vulnerabilities, license issu
Semgrep is a leading code analysis tool that scans code for vulnerabilities, helping developers fix issues swiftly withi
AIPo Labs — dynamic search and execute any tools available on ACI.dev for fast, flexible AI-powered workflows.
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.