Autenticación y gestión de usuarios
Recomendado: FireCMS Pro y Cloud
Sección titulada «Recomendado: FireCMS Pro y Cloud»Antes de implementar autenticación personalizada, recomendamos encarecidamente considerar FireCMS Pro o FireCMS Cloud, que incluyen:
- ✅ Sistema de gestión de usuarios integrado
- ✅ Permisos basados en roles (Admin, Editor, Viewer)
- ✅ Interfaz de gestión de equipos
- ✅ Sistema de invitación de usuarios
- ✅ Permisos granulares a nivel de colección y campo
- ✅ Registros de auditoría y seguimiento de actividad de usuarios
- ✅ Funcionalidades de seguridad de nivel enterprise
Estas soluciones proporcionan un sistema completo de autenticación y autorización listo para usar, ahorrándote tiempo significativo de desarrollo y asegurando las mejores prácticas de seguridad.
Más información sobre gestión de usuarios en FireCMS Pro →
Parte 1: Gestión básica de usuarios
Sección titulada «Parte 1: Gestión básica de usuarios»Esta sección cubre cómo crear una colección users para gestionar usuarios. Esta es la base para implementar permisos.
Crear una colección “Users”
Sección titulada «Crear una colección “Users”»Esta colección almacenará tus usuarios.
import { buildCollection, buildProperty } from "@firecms/core";
export type User = { name: string; email: string;};
export const usersCollection = buildCollection<User>({ name: "Users", singularName: "User", path: "users", properties: { name: buildProperty({ name: "Name", validation: { required: true }, dataType: "string" }), email: buildProperty({ name: "Email", validation: { required: true, email: true }, dataType: "string" }) }});Parte 2: Permisos basados en roles
Sección titulada «Parte 2: Permisos basados en roles»Ahora, añadamos un role a nuestros usuarios y usémoslo para controlar el acceso.
Paso 1: Actualizar la colección “Users” con roles
Sección titulada «Paso 1: Actualizar la colección “Users” con roles»Añade una propiedad role a tu tipo User y colección.
import { buildCollection, buildProperty } from "@firecms/core";
export enum UserRole { admin = "admin", editor = "editor", viewer = "viewer",}
export type User = { name: string; email: string; role: UserRole;}
export const usersCollection = buildCollection<User>({ name: "Users", singularName: "User", path: "users", properties: { name: buildProperty({ name: "Name", validation: { required: true }, dataType: "string" }), email: buildProperty({ name: "Email", validation: { required: true, email: true }, dataType: "string" }), role: buildProperty({ name: "Role", validation: { required: true }, dataType: "string", enumValues: { admin: "Admin", editor: "Editor", viewer: "Viewer" } }) }});Paso 2: Implementar un autenticador basado en roles
Sección titulada «Paso 2: Implementar un autenticador basado en roles»Primero, crea un nuevo archivo llamado src/custom_authenticator.ts. Este archivo contendrá tu lógica de autenticación.
src/custom_authenticator.ts
import { Authenticator } from "@firecms/core";import { FirebaseUserWrapper } from "@firecms/firebase";import { User } from "./collections/users"; // Asegúrate de importar tu tipo User
export const roleBasedAuthenticator: Authenticator<FirebaseUserWrapper> = async ({ user, authController, dataSourceDelegate}) => { if (!user?.email) return false;
try { const userEntities = await dataSourceDelegate.fetchCollection<User>({ path: "users", filter: { email: ["==", user.email] } });
if (userEntities && userEntities.length > 0) { const member = userEntities[0].values; authController.setExtra({ role: member.role }); return true; }
return false; } catch (error) { console.error("Error de autenticación:", error); return false; }};Ahora, importa el roleBasedAuthenticator en tu App.tsx y pásalo al componente FirebaseCMSApp.
src/App.tsx
import { FirebaseCMSApp } from "@firecms/firebase";import { roleBasedAuthenticator } from "./custom_authenticator";import { usersCollection } from "./collections/users"; // Asegúrate de importar tus colecciones
// ... otras importaciones
function App() { // ... otra lógica del componente
return ( <FirebaseCMSApp name={"My App"} authentication={roleBasedAuthenticator} collections={[usersCollection, /* ...otras colecciones */]} // ... otras props /> );}
export default App;Paso 3: Aplicar permisos a las colecciones
Sección titulada «Paso 3: Aplicar permisos a las colecciones»Usa el callback permissions en tus colecciones para controlar el acceso basado en el rol del usuario.
import { buildCollection } from "@firecms/core";import { UserRole } from "./collections/users";
export const postsCollection = buildCollection({ name: "Posts", path: "posts", permissions: ({ authController }) => { const userRole = authController.extra?.role; return { read: true, // Todos los roles pueden leer edit: userRole === UserRole.admin || userRole === UserRole.editor, create: userRole === UserRole.admin || userRole === UserRole.editor, delete: userRole === UserRole.admin }; }, // ... propiedades});Parte 3: Usar Firebase Custom Claims para permisos
Sección titulada «Parte 3: Usar Firebase Custom Claims para permisos»Una alternativa a almacenar roles en Firestore es usar los custom claims de Firebase Authentication.
Paso 1: Establecer Custom Claims
Sección titulada «Paso 1: Establecer Custom Claims»Necesitas establecer custom claims para un usuario desde un entorno backend usando el Firebase Admin SDK. Esto típicamente se hace en una Cloud Function.
// Ejemplo de Cloud Function para establecer un claim de rolimport * as functions from "firebase-functions";import * as admin from "firebase-admin";
admin.initializeApp();
export const setUserRole = functions.https.onCall(async (data, context) => { if (!context.auth?.token.admin) { throw new functions.https.HttpsError("permission-denied", "Debes ser admin para establecer roles."); }
const { uid, role } = data; await admin.auth().setCustomUserClaims(uid, { role });
return { message: `¡Éxito! Al usuario ${uid} se le ha dado el rol de ${role}.` };});Paso 2: Implementar un autenticador basado en Claims
Sección titulada «Paso 2: Implementar un autenticador basado en Claims»Este autenticador lee los custom claims del token de ID del usuario.
import { Authenticator } from "@firecms/core";import { FirebaseUserWrapper } from "@firecms/firebase";
export const claimsAuthenticator: Authenticator<FirebaseUserWrapper> = async ({ user, authController}) => { if (!user) return false;
try { const idTokenResult = await user.firebaseUser.getIdTokenResult(true); // Forzar refresco const role = idTokenResult.claims.role || "viewer"; // Por defecto 'viewer' si no hay claim de rol authController.setExtra({ role }); return true; } catch (error) { console.error("Error de autenticación:", error); return false; }};Paso 3: Usar Claims en las colecciones
Sección titulada «Paso 3: Usar Claims en las colecciones»La implementación de permissions es la misma que con el enfoque basado en roles, ya que el rol se extrae y se coloca en authController.extra.
import { buildCollection } from "@firecms/core";
export const articlesCollection = buildCollection({ name: "Articles", path: "articles", permissions: ({ authController }) => { const userRole = authController.extra?.role; return { read: true, edit: userRole === "admin" || userRole === "editor", create: userRole === "admin" || userRole === "editor", delete: userRole === "admin" }; }, // ... propiedades});Mejores prácticas de seguridad
Sección titulada «Mejores prácticas de seguridad»- Reglas de seguridad de Firestore: Siempre aplica reglas de seguridad en tu backend. Los permisos del lado del cliente son para propósitos de UI/UX y pueden ser eludidos.
- Validación del lado del servidor: Para operaciones críticas, valida los permisos en un servidor.
- Principio de menor privilegio: Otorga a los usuarios el nivel mínimo de acceso que necesitan.