Cómo implementar autenticación con Supabase en aplicaciones React
Supabase ofrece autenticación lista para usar (email/contraseña, magic link, OAuth, OTP) con SDKs simples e integración directa con PostgreSQL y Row Level Security (RLS). En esta guía verás cómo añadir login, registro, sesión persistente y protección de rutas en una app React.
1) Crear el proyecto de Supabase
- Entra a Supabase y crea un proyecto.
- Copia la Project URL y la anon public key (Settings > API).
- Habilita proveedores OAuth si los usarás (Auth > Providers).
2) Preparar React
# Vite + React
npm create vite@latest supa-auth -- --template react-swc
cd supa-auth
npm i @supabase/supabase-js
npm i react-router-dom
npm run dev
3) Configurar el cliente de Supabase
src/lib/supabase.js
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
persistSession: true, // guarda sesión en localStorage
autoRefreshToken: true
}
});
.env.local
VITE_SUPABASE_URL=https://xxxx.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
4) Contexto de autenticación
src/context/AuthContext.jsx
import { createContext, useContext, useEffect, useState } from "react";
import { supabase } from "../lib/supabase";
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Sesión al cargar
supabase.auth.getUser().then(({ data }) => {
setUser(data.user || null);
setLoading(false);
});
// Escuchar cambios (login/logout/refresh)
const { data: sub } = supabase.auth.onAuthStateChange((_event, session) => {
setUser(session?.user ?? null);
});
return () => sub.subscription.unsubscribe();
}, []);
const value = {
user,
loading,
signInWithEmail: (email, password) => supabase.auth.signInWithPassword({ email, password }),
signUpWithEmail: (email, password) => supabase.auth.signUp({ email, password }),
signInWithGoogle: () => supabase.auth.signInWithOAuth({ provider: "google" }),
signOut: () => supabase.auth.signOut()
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export const useAuth = () => useContext(AuthContext);
5) Rutas y protección de páginas
src/main.jsx
import React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { AuthProvider, useAuth } from "./context/AuthContext.jsx";
import Home from "./pages/Home.jsx";
import Dashboard from "./pages/Dashboard.jsx";
import Auth from "./pages/Auth.jsx";
function PrivateRoute({ children }) {
const { user, loading } = useAuth();
if (loading) return <p>Cargando...</p>;
return user ? children : <Navigate to="/auth" replace />;
}
createRoot(document.getElementById("root")).render(
<React.StrictMode>
<AuthProvider>
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/auth" element={<Auth />} />
<Route path="/dashboard" element={
<PrivateRoute><Dashboard /></PrivateRoute>
} />
</Routes>
</BrowserRouter>
</AuthProvider>
</React.StrictMode>
);
6) Pantallas de Auth
src/pages/Auth.jsx
import { useState } from "react";
import { useAuth } from "../context/AuthContext";
export default function Auth() {
const { signInWithEmail, signUpWithEmail, signInWithGoogle } = useAuth();
const [form, setForm] = useState({ email: "", password: "" });
const [mode, setMode] = useState("login");
const [error, setError] = useState("");
const onSubmit = async (e) => {
e.preventDefault();
setError("");
const fn = mode === "login" ? signInWithEmail : signUpWithEmail;
const { error } = await fn(form.email, form.password);
if (error) setError(error.message);
};
return (
<section>
<h1>{mode === "login" ? "Iniciar sesión" : "Crear cuenta"}</h1>
<form onSubmit={onSubmit}>
<input placeholder="Email" type="email"
value={form.email} onChange={e => setForm({ ...form, email: e.target.value })} />
<input placeholder="Contraseña" type="password"
value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} />
<button type="submit">Continuar</button>
</form>
<button onClick={signInWithGoogle}>Entrar con Google</button>
{error && <p style={{color: "crimson"}}>{error}</p>}
<p>
{mode === "login" ? "¿No tienes cuenta?" : "¿Ya tienes cuenta?"}
<button onClick={() => setMode(mode === "login" ? "signup" : "login")}>
{mode === "login" ? "Regístrate" : "Inicia sesión"}
</button>
</p>
</section>
);
}
7) Uso de la sesión en el panel
src/pages/Dashboard.jsx
import { useAuth } from "../context/AuthContext";
export default function Dashboard() {
const { user, signOut } = useAuth();
return (
<main>
<h2>Hola, {user?.email}</h2>
<button onClick={signOut}>Cerrar sesión</button>
</main>
);
}
8) Seguridad en la base de datos (RLS)
Activa Row Level Security en la tabla que guarda datos del usuario y crea políticas, por ejemplo para que cada usuario solo vea sus registros:
-- En el SQL editor de Supabase:
alter table profiles enable row level security;
create policy "Solo dueño"
on profiles for select
to authenticated
using ( auth.uid() = user_id );
create policy "Insertar propio perfil"
on profiles for insert
to authenticated
with check ( auth.uid() = user_id );
9) Buenas prácticas
- Usa persistSession y escucha
onAuthStateChange
para mantener el estado. - Protege rutas críticas con componentes tipo
<PrivateRoute/>
. - Si usas servidores (Next.js), valida el JWT del usuario en el backend cuando corresponda.
- Define políticas RLS antes de exponer datos en producción.
Conclusión
Con el SDK de Supabase puedes agregar autenticación completa a React en minutos. Aprovecha su soporte de sesiones, OAuth y RLS para construir apps seguras y escalables con mínima fricción.
👉 ¿Quieres que integremos Supabase Auth en tu app React con mejores prácticas de seguridad y DX?
Solicítalo aquí.