Organizaciones

Multi-tenancy con organizaciones, roles y permisos para aplicaciones B2B.

Conceptos

  • Organizacion - Un grupo/empresa/equipo que agrupa usuarios
  • Miembro - Un usuario que pertenece a una organizacion
  • Rol - Define los permisos del miembro (admin, member, viewer, etc.)
  • Invitacion - Permite agregar nuevos miembros por email

Modelo de Organizacion

interface Organization {
  id: string;                  // ID unico
  name: string;                // Nombre de la organizacion
  slug: string;                // URL-friendly identifier
  logoUrl?: string;            // Logo de la organizacion
  publicMetadata: Record<string, any>;
  privateMetadata: Record<string, any>;
  createdAt: string;
  updatedAt: string;
}

interface Membership {
  id: string;
  userId: string;
  organizationId: string;
  role: 'admin' | 'member' | 'viewer';
  publicMetadata: Record<string, any>;
  createdAt: string;
}

interface Invitation {
  id: string;
  email: string;
  organizationId: string;
  role: string;
  status: 'pending' | 'accepted' | 'expired';
  expiresAt: string;
  createdAt: string;
}

Cliente SDK

Obtener organizacion activa

import { useOrganization } from '@auris/react';

function OrgDashboard() {
  const { organization, membership, isLoaded } = useOrganization();

  if (!isLoaded) return <Loading />;
  if (!organization) return <NoOrgSelected />;

  return (
    <div>
      <h1>{organization.name}</h1>
      <p>Tu rol: {membership.role}</p>
    </div>
  );
}

Listar organizaciones del usuario

import { useOrganizationList } from '@auris/react';

function OrgSwitcher() {
  const { organizations, setActive, isLoaded } = useOrganizationList();

  if (!isLoaded) return <Loading />;

  return (
    <select onChange={(e) => setActive(e.target.value)}>
      {organizations.map((org) => (
        <option key={org.id} value={org.id}>
          {org.name}
        </option>
      ))}
    </select>
  );
}

Crear organizacion

import { useOrganization } from '@auris/react';

function CreateOrg() {
  const { create } = useOrganization();

  const handleCreate = async () => {
    const org = await create({
      name: 'Mi Empresa',
      slug: 'mi-empresa',
    });
    console.log('Creada:', org.id);
  };

  return <button onClick={handleCreate}>Crear Organizacion</button>;
}

Backend SDK

Crear organizacion

const org = await auris.organizations.create({
  name: 'Acme Inc',
  slug: 'acme',
  createdBy: 'user_xxxxx', // Usuario que sera admin
  publicMetadata: {
    plan: 'enterprise',
    industry: 'healthcare',
  },
});

Listar organizaciones

const { data, totalCount } = await auris.organizations.list({
  limit: 20,
  offset: 0,
  query: 'acme', // Buscar por nombre
});

Obtener organizacion

// Por ID
const org = await auris.organizations.get('org_xxxxx');

// Por slug
const org = await auris.organizations.getBySlug('acme');

Actualizar organizacion

const org = await auris.organizations.update('org_xxxxx', {
  name: 'Acme Corporation',
  publicMetadata: {
    plan: 'enterprise-plus',
  },
});

Eliminar organizacion

await auris.organizations.delete('org_xxxxx');

Gestion de Miembros

Listar miembros

const members = await auris.organizations.listMembers('org_xxxxx', {
  limit: 50,
  role: 'admin', // Filtrar por rol (opcional)
});

for (const member of members) {
  console.log(member.user.email, member.role);
}

Agregar miembro

await auris.organizations.addMember('org_xxxxx', {
  userId: 'user_yyyyy',
  role: 'member',
});

Actualizar rol

await auris.organizations.updateMember('org_xxxxx', 'user_yyyyy', {
  role: 'admin',
});

Remover miembro

await auris.organizations.removeMember('org_xxxxx', 'user_yyyyy');

Invitaciones

Crear invitacion

const invitation = await auris.organizations.createInvitation('org_xxxxx', {
  email: 'nuevo@email.com',
  role: 'member',
  // La invitacion expira en 7 dias por defecto
});

// Se envia automaticamente un email al usuario

Listar invitaciones pendientes

const invitations = await auris.organizations.listInvitations('org_xxxxx', {
  status: 'pending',
});

Revocar invitacion

await auris.organizations.revokeInvitation('org_xxxxx', 'inv_xxxxx');

Aceptar invitacion (cliente)

import { useOrganization } from '@auris/react';

function AcceptInvitation({ token }) {
  const { acceptInvitation } = useOrganization();

  const handleAccept = async () => {
    await acceptInvitation(token);
    // Usuario ahora es miembro de la organizacion
  };

  return <button onClick={handleAccept}>Aceptar Invitacion</button>;
}

Roles y Permisos

Roles predefinidos

  • admin - Control total de la organizacion
  • member - Acceso estandar
  • viewer - Solo lectura

Verificar permisos

import { useOrganization } from '@auris/react';

function AdminPanel() {
  const { membership } = useOrganization();

  if (membership?.role !== 'admin') {
    return <div>No tienes permisos</div>;
  }

  return <div>Panel de administracion</div>;
}

Roles personalizados

// En el dashboard de Auris, puedes definir roles custom:
{
  "roles": [
    {
      "key": "billing_admin",
      "name": "Administrador de Facturacion",
      "permissions": ["billing.read", "billing.write", "invoices.read"]
    },
    {
      "key": "support",
      "name": "Soporte",
      "permissions": ["users.read", "tickets.read", "tickets.write"]
    }
  ]
}

// Asignar rol custom
await auris.organizations.updateMember('org_xxxxx', 'user_yyyyy', {
  role: 'billing_admin',
});

Organizacion activa en el servidor

// Next.js App Router
import { auth } from '@auris/nextjs/server';

export default async function OrgPage() {
  const { orgId, orgRole, userId } = await auth();

  if (!orgId) {
    return <div>Selecciona una organizacion</div>;
  }

  // Verificar permisos
  if (orgRole !== 'admin') {
    return <div>No autorizado</div>;
  }

  return <div>Dashboard de organizacion</div>;
}

Multi-tenancy en la base de datos

// Filtrar datos por organizacion
app.get('/api/projects', async (req, res) => {
  const { orgId } = req.auth;

  if (!orgId) {
    return res.status(400).json({ error: 'Organizacion requerida' });
  }

  const projects = await db.projects.findMany({
    where: { organizationId: orgId },
  });

  res.json(projects);
});

Webhooks de organizaciones

  • organization.created
  • organization.updated
  • organization.deleted
  • organization.member_added
  • organization.member_removed
  • organization.member_updated
  • organization.invitation_created
  • organization.invitation_accepted