src/
├── lib/
│ └── supabaseClient.ts # Supabase client singleton
├── contexts/
│ └── AuthContext.tsx # Auth provider with React context
├── components/
│ ├── auth/
│ │ ├── LoginPage.tsx # Magic link login form
│ │ ├── LoginPage.module.css
│ │ ├── RequireAuth.tsx # Protected route wrapper
│ │ └── AuthCallback.tsx # Handles OAuth callback
│ └── pages/
│ ├── SavedPage.tsx # Example protected page
│ └── SavedPage.module.css
migrations/
└── create_saved_artists_table.sql # Database table for saved artists
Go to your Supabase project → Authentication → URL Configuration
- Local dev:
http://localhost:5173 - Production:
https://yourdomain.com
http://localhost:5173/auth/callbackhttps://yourdomain.com/auth/callback(for production)
- Magic links work in SPAs, but you need the callback route (
/auth/callback) - The redirect URL must match exactly (including protocol and port)
- For local dev, use
http://localhost:5173(nothttp://127.0.0.1:5173)
Run the migration in Supabase SQL Editor:
-- See: migrations/create_saved_artists_table.sqlThis creates:
saved_artiststable with RLS policies- Users can only read/write their own saved artists
Ensure these are in your .env:
VITE_SUPABASE_URL=your-project-url
VITE_SUPABASE_ANON_KEY=your-anon-keyIf you have existing code importing from src/services/supabaseClient.ts, update to:
import { supabase } from "../lib/supabaseClient";Wrap any route that requires auth:
<Route
path="/saved"
element={
<RequireAuth>
<SavedPage />
</RequireAuth>
}
/>import { useAuth } from "../contexts/AuthContext";
function MyComponent() {
const { user, signOut } = useAuth();
if (user) {
return <button onClick={signOut}>Sign Out</button>;
}
}import { supabase } from "../lib/supabaseClient";
import { useAuth } from "../contexts/AuthContext";
function ArtistCard({ artistId }) {
const { user } = useAuth();
const toggleSave = async () => {
if (!user) return;
const { error } = await supabase
.from("saved_artists")
.insert({ user_id: user.id, artist_id: artistId });
};
}- User visits protected route → redirected to
/login - User enters email → magic link sent
- User clicks link in email → redirected to
/auth/callback - Supabase handles token exchange → user authenticated
AuthCallbackredirects to original destination (or/saved)
- Start dev server:
npm run dev - Visit
http://localhost:5173/saved(should redirect to login) - Enter email, check inbox
- Click magic link → should redirect back to
/saved