Token导航 LogoToken导航TokenDH.com
研究检索操作浏览器github未标认证来源可访问clear审计通过

supabase-auth-storage-realtime-coreSupabase auth storage realtime core 搜索

Agent Skill

用于辅助安全审计、权限检查、凭据风险、认证流程和常见漏洞排查。它适合让 Agent 梳理敏感配置、检查依赖风险、分析鉴权逻辑或生成安全复核清单。使用时不能把工具输出直接当最终结论,涉及密钥、令牌、用户数据或生产系统时,应先确认最小权限、脱敏方式和操作边界。

总安装

870

周安装

37

GitHub Stars

2,089

下载量

305
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:supabase-auth-storage-realtime-core(Supabase auth storage realtime core 搜索)
来源仓库:https://github.com/jeremylongshore/claude-code-plugins-plus-skills
仓库路径:skills/supabase-auth-storage-realtime-core
安装命令:
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill supabase-auth-storage-realtime-core
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill supabase-auth-storage-realtime-core

简介

辅助安全审计和认证流程分析,支持常见漏洞排查。

  • 适合梳理敏感配置和检查依赖风险。
  • 通过 npx 命令从指定 GitHub 仓库安装并使用该技能。
  • 涉及密钥或用户数据时需确认最小权限和操作边界。
  • supabase-auth-storage-realtime-core 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

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-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

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: 'user@example.com',
  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: 'user@example.com',
  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: 'user@example.com',
  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('user@example.com', {
  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": "user@example.com",
    "password": "secure-password-123",
    "options": {"data": {"username": "newuser"}},
})

# Sign in with password
result = supabase.auth.sign_in_with_password({
    "email": "user@example.com",
    "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

Broadcast (client-to-client, no database involved):

const room = supabase.channel('collab-room')

// Listen for cursor movements from other users
room.on('broadcast', { event: 'cursor-move' }, ({ payload }) => {
  console.log(`User ${payload.userId} at (${payload.x}, ${payload.y})`)
})

// Subscribe first, then send
room.subscribe((status) => {
  if (status === 'SUBSCRIBED') {
    // Send cursor position to all other clients
    room.send({
      type: 'broadcast',
      event: 'cursor-move',
      payload: { userId: 'abc', x: 120, y: 340 },
    })
  }
})

Presence (track who is online):

const room = supabase.channel('app-presence')

// Sync event fires whenever the presence state changes
room.on('presence', { event: 'sync' }, () => {
  const state = room.presenceState()
  // state → { 'user-abc': [{ user_id: 'abc', online_at: '...' }], ... }
  const onlineUsers = Object.keys(state)
  console.log('Online:', onlineUsers.length, 'users')
})

room.on('presence', { event: 'join' }, ({ key, newPresences }) => {
  console.log('Joined:', key, newPresences)
})

room.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
  console.log('Left:', key, leftPresences)
})

// Subscribe and track this user's presence
room.subscribe(async (status) => {
  if (status === 'SUBSCRIBED') {
    await room.track({
      user_id: currentUser.id,
      username: currentUser.email,
      online_at: new Date().toISOString(),
    })
  }
})

// Untrack when leaving (e.g., on component unmount)
await room.untrack()

Python Realtime:

# Python realtime uses callbacks (requires running event loop)
def handle_insert(payload):
    print("New row:", payload["new"])

channel = supabase.channel("room")
channel.on_postgres_changes(
    event="INSERT",
    schema="public",
    table="messages",
    callback=handle_insert,
)
channel.subscribe()

# When done
supabase.remove_channel(channel)

Output

  • Auth: user registration, password login, OAuth flow (Google/GitHub), magic link, session lifecycle with onAuthStateChange, password reset
  • Storage: file upload/download, public URLs for CDN-served assets, time-limited signed URLs for private files, bucket-level RLS policies using auth.uid() and storage.foldername()
  • Realtime: Postgres change subscriptions with server-side filters, broadcast channels for client-to-client messaging, presence tracking for online status

Error Handling

ErrorCauseSolution
AuthApiError: User already registeredDuplicate email signupUse signInWithPassword or check existence first
AuthApiError: Invalid login credentialsWrong email or passwordVerify credentials; check email confirmation status
AuthApiError: Email not confirmedUser has not clicked confirmation linkResend with resend({type: 'signup', email})
StorageApiError: Bucket not foundBucket does not existCreate via dashboard or INSERT INTO storage.buckets
StorageApiError: new row violates row-level securityRLS policy blocking the operationVerify storage.objects policies match the user and bucket
StorageApiError: The resource already existsFile exists and upsert: falseSet upsert: true to overwrite or use a unique path
Realtime: channel error or TIMED_OUTNetwork issues or Realtime not enabledCheck ALTER PUBLICATION supabase_realtime ADD TABLE <name>
Realtime: too many channelsExceeded concurrent channel limitUnsubscribe unused channels with removeChannel()

Examples

Full auth + protected upload flow:

// 1. Sign in
const { data: { session } } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure-password-123',
})

// 2. Upload avatar to user's folder (RLS enforces ownership)
const { data } = await supabase.storage
  .from('avatars')
  .upload(`${session.user.id}/avatar.png`, file, { upsert: true })

// 3. Get public URL for display
const { data: { publicUrl } } = supabase.storage
  .from('avatars')
  .getPublicUrl(`${session.user.id}/avatar.png`)

// 4. Subscribe to profile changes in real time
const channel = supabase
  .channel('profile-updates')
  .on('postgres_changes', {
    event: 'UPDATE',
    schema: 'public',
    table: 'profiles',
    filter: `id=eq.${session.user.id}`,
  }, (payload) => {
    console.log('Profile updated:', payload.new)
  })
  .subscribe()

Resources

Next Steps

For common Supabase errors and debugging, see supabase-common-errors. For database queries and CRUD operations, see supabase-crud-core.

适合场景

01

研究助手

02

事实核查

03

知识库问答

04

带来源的搜索总结

能力概览

能力 1

组合搜索和大模型调用

能力 2

支持多来源检索和总结

能力 3

强调引用来源和事实核查

能力 4

适合研究型 Agent 流程

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenCode

27.43%
按下载量换算84

Claude Code

21.12%
按下载量换算64

Antigravity

16.98%
按下载量换算52

Gemini CLI

12.34%
按下载量换算38

kilo

7.34%
按下载量换算22

windsurf

3.5%
按下载量换算11

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills