Módulo de Organizations con Auth0
Multi-tenancy para un proyecto de Next.js App Router usando Auth0 Organizations: crear/gestionar organizaciones, invitar y gestionar miembros, asignar roles básicos Owner/Admin/Member por organización, cambiar la organización activa y proteger Server Components según la pertenencia a una organización.
Este módulo es autocontenido (no importa nada del módulo de auth separado), pero
asume que ese módulo (o uno equivalente) ya monta /auth/login y /auth/logout
mediante su proxy.ts, ya que tanto el cambio de organización como la aceptación de invitaciones
funcionan volviendo a autenticar a través de esa ruta.
Este módulo no implementa roles/permisos genéricos (RBAC): solo los roles fijos Owner/Admin/Member limitados a la pertenencia a una organización. Usá tu módulo separado de roles/permisos para cualquier cosa más allá de eso.
Dependencias
@auth0/nextjs-auth0(^4.26.1 o más reciente)
Mapa de archivos
| Archivo | Dónde se usa | Para qué sirve |
|---|---|---|
types.ts | en cualquier lado | Tipos compartidos (Organization, OrganizationMember, Invitation, etc.) |
service.ts | solo interno | Cliente de la Management API, CRUD, invitaciones y asignación de roles. Nunca lo importes directamente. |
actions.ts | Server Components / Server Actions | "use server" — el único punto de entrada del lado del servidor |
client.ts | Client Components | switchOrganization, buildAcceptInvitationUrl |
.env / .env.local | raíz del proyecto | Variables de entorno |
Este módulo no incluye proxy.ts: solo puede existir un proxy por proyecto de Next.js,
y tanto el cambio de organización como la aceptación de invitaciones reutilizan la
ruta /auth/login que ya monta el proxy.ts del módulo de auth.
Uso
Listar y crear organizaciones
import { listUserOrganizations, createOrganization } from "@/lib/auth0-organizations/actions";
const orgs = await listUserOrganizations();
const created = await createOrganization({ name: "acme-inc", displayName: "Acme Inc." });
Proteger un Server Component según la organización activa
import { requireActiveOrganization } from "@/lib/auth0-organizations/actions";
export default async function TeamPage() {
const org = await requireActiveOrganization("/organizations");
return <p>You're viewing {org.displayName}</p>;
}
Selector de organización (Client Component)
"use client";
import { switchOrganization } from "@/lib/auth0-organizations/client";
export function OrgSwitcher({ organizations }: { organizations: { id: string; name: string }[] }) {
return (
<select onChange={(e) => switchOrganization(e.target.value)}>
{organizations.map((o) => (
<option key={o.id} value={o.id}>{o.name}</option>
))}
</select>
);
}
Invitar a un miembro
import { inviteMember } from "@/lib/auth0-organizations/actions";
await inviteMember({
organizationId: org.id,
email: "teammate@example.com",
roles: ["member"],
inviterName: "Jane (Acme Inc.)",
});
Aceptar / rechazar una invitación
Normalmente, aceptar es simplemente que el usuario invitado haga clic en el enlace del
email de invitación: llega a /auth/login?organization=...&invitation=...
y Auth0 completa el flujo automáticamente. acceptInvitation() en
actions.ts es solo para una pantalla de confirmación personalizada antes de esa redirección.
import { declineInvitation } from "@/lib/auth0-organizations/actions";
await declineInvitation(organizationId, invitationId);
Cambiar el rol de un miembro
import { changeMemberRole } from "@/lib/auth0-organizations/actions";
await changeMemberRole(organizationId, userId, ["admin"]);
Configuración
Estos pasos se hacen en el Auth0 Dashboard: no tienen código.
- Habilitá Organizations en la aplicación: en tu Application → pestaña Organizations, elegí cómo debe comportarse el login: No Prompt, Prompt for organization o Require organization. "Require organization" es la opción más común para apps B2B multi-tenant.
- Creá los Roles: en User Management → Roles, creá tres
roles con los nombres exactos
Owner,AdminyMember(resolveRoleIdsenservice.tscompara sin distinguir mayúsculas, pero mantené los nombres consistentes). Definí los permisos/scopes de API que debería tener cada rol: este módulo solo asigna y lee asignaciones de roles, nunca define qué puede hacer un rol. - Personalización del email de invitación: personalizá la plantilla en Branding → Email Templates → Organization Invitation si querés algo distinto al default de Auth0.
- Aplicación Machine-to-Machine: reutilizá la app M2M del módulo de
auth (o creá una nueva) autorizada para la Auth0 Management
API, con estos scopes:
create:organizations,read:organizations,update:organizations,delete:organizations,create:organization_members,read:organization_members,delete:organization_members,create:organization_invitations,read:organization_invitations,delete:organization_invitations,create:organization_member_roles,read:organization_member_roles,delete:organization_member_rolesyread:roles(se usa para resolver Owner/Admin/Member a sus Role IDs de Auth0). - Conexiones específicas por organización (opcional): si distintas organizaciones deben usar distintas conexiones de login (por ejemplo, una org usa SSO de Google Workspace y otra usa email+contraseña), habilitá las conexiones correspondientes por organización en la pestaña Connections de cada una. No hace falta cambiar nada en este módulo para eso.