auth-setup
npx skills add https://github.com/chloezhu010/vibe-ship --skill auth-setup
Agent 安装分布
Skill 文档
Auth Setup
Add Supabase Auth (magic link) and configure Resend for production email delivery.
This skill is called by the
vibe-shiporchestrator.FRAMEWORK=nextjsorvite.
lib/supabase.ts(Next.js) orsrc/lib/supabase.ts(Vite) and all Supabase env vars were created in the previoussupabase-setupstep.
1. Review Supabase Auth approach
This skill uses Supabase Auth (built into @supabase/supabase-js) â not Better Auth or any third-party auth library. Key patterns:
- Magic link OTP via
supabase.auth.signInWithOtp() - Session management via
supabase.auth.onAuthStateChange() - Always pass
emailRedirectTo: window.location.originso links work in both local dev and production
2. Enable Supabase Auth providers
Email is enabled by default on new Supabase projects. No dashboard action needed.
3. Add auth to the codebase
If FRAMEWORK = nextjs:
Install: npm install @supabase/auth-helpers-nextjs
Create middleware.ts at the project root:
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(req: NextRequest) {
const res = NextResponse.next()
const supabase = createMiddlewareClient({ req, res })
await supabase.auth.getSession()
return res
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}
Create app/login/page.tsx if it doesn’t exist:
'use client'
import { useState } from 'react'
import { supabase } from '@/lib/supabase'
export default function LoginPage() {
const [email, setEmail] = useState('')
const [sent, setSent] = useState(false)
async function handleLogin(e: React.FormEvent) {
e.preventDefault()
await supabase.auth.signInWithOtp({
email,
options: { emailRedirectTo: window.location.origin },
})
setSent(true)
}
if (sent) return <p>Check your email for a magic link.</p>
return (
<form onSubmit={handleLogin}>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} required />
<button type="submit">Send magic link</button>
</form>
)
}
If FRAMEWORK = vite:
Create src/contexts/AuthContext.tsx:
import { createContext, useContext, useEffect, useState } from 'react'
import { Session } from '@supabase/supabase-js'
import { supabase } from '@/lib/supabase'
const AuthContext = createContext<{ session: Session | null }>({ session: null })
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [session, setSession] = useState<Session | null>(null)
useEffect(() => {
supabase.auth.getSession().then(({ data }) => setSession(data.session))
const { data: { subscription } } = supabase.auth.onAuthStateChange((_e, s) => setSession(s))
return () => subscription.unsubscribe()
}, [])
return <AuthContext.Provider value={{ session }}>{children}</AuthContext.Provider>
}
export const useAuth = () => useContext(AuthContext)
Wrap <App /> in src/main.tsx with <AuthProvider>.
Update src/main.tsx to wrap your app:
// Before
import App from './App'
ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
// After
import App from './App'
import { AuthProvider } from './contexts/AuthContext'
ReactDOM.createRoot(document.getElementById('root')!).render(
<AuthProvider><App /></AuthProvider>
)
Create src/pages/Login.tsx:
import { useState } from 'react'
import { supabase } from '@/lib/supabase'
export default function Login() {
const [email, setEmail] = useState('')
const [sent, setSent] = useState(false)
async function handleLogin(e: React.FormEvent) {
e.preventDefault()
await supabase.auth.signInWithOtp({
email,
options: { emailRedirectTo: window.location.origin },
})
setSent(true)
}
if (sent) return <p>Check your email for a magic link.</p>
return (
<form onSubmit={handleLogin}>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} required />
<button type="submit">Send magic link</button>
</form>
)
}
Wire the auth gate into the root component. Scan for App.tsx or App.jsx and add at the top of the component body:
import { useAuth } from './contexts/AuthContext'
import Login from './pages/Login'
export default function App() {
const { session } = useAuth()
if (!session) return <Login />
// existing app content...
}
Also add a sign-out button. Scan for a header, nav, or avatar area and add:
<button onClick={() => supabase.auth.signOut()}>Sign out</button>
If no clear header exists, add it at the top of the main app content area.
4. Add row-level security policies
For each table created in supabase-setup, write RLS policies to supabase/migrations/002_rls_policies.sql. Replace items with each actual table name:
-- Users can read their own rows
create policy "Users can read own items"
on items for select
using (auth.uid() = user_id);
-- Users can insert their own rows
create policy "Users can insert own items"
on items for insert
with check (auth.uid() = user_id);
-- Users can update their own rows
create policy "Users can update own items"
on items for update
using (auth.uid() = user_id);
-- Users can delete their own rows
create policy "Users can delete own items"
on items for delete
using (auth.uid() = user_id);
Repeat for every table that has a user_id column, then push:
supabase db push
No dashboard visit needed.
5. Configure Resend for production email
Apply resend/email-best-practices.
Ask: “Please create a free account at resend.com, verify your domain (Resend will walk you through adding a DNS record â takes ~5 minutes), then add your API key to .env.local:
RESEND_API_KEY=<your key here>
Let me know when it’s saved.”
Wait for the user to confirm before continuing.
Add to supabase/config.toml â these are two separate top-level sections, not nested:
[auth.email.smtp]
enabled = true
host = "smtp.resend.com"
port = 465
user = "resend"
pass = "env(RESEND_API_KEY)"
admin_email = "noreply@resend.dev"
sender_name = "<app name>"
Find the existing [auth.rate_limit] section in config.toml and update email_sent there (do not add a duplicate section):
[auth.rate_limit]
email_sent = 100
Ask the user to push to the hosted project by running this in their terminal:
supabase config push
No dashboard visit needed.
6. Validate
Ask user to:
- Run
npm run dev - Navigate to the login page
- Enter their own email and wait for the magic link email (check spam if not in inbox)
Only report success after the user confirms the email arrived:
Report: “â Auth setup complete. Magic link email delivered via Resend.”