Inicia sesión con Beeuni
Restringe el acceso a tu propia aplicación mediante la identidad de Beeuni con una sola ruta y actúa como el usuario que ha iniciado sesión a través del SDK.
“Iniciar sesión con Beeuni” convierte a Beeuni en el proveedor de identidad de una aplicación que ejecutas:
un panel, una herramienta interna o un producto vertical creado sobre Beeuni. Tus usuarios
inician sesión con su cuenta de Beeuni, tu servidor sabe quiénes son y cada
llamada a Beeuni que hace tu aplicación se ejecuta como ese usuario con sus
asignaciones de roles. Todo el flujo está en @beeuni/sdk. Tu aplicación nunca almacena un
token de Beeuni en el navegador.
Es OAuth 2.1 estándar (código de autorización + PKCE), servido por la API de Beeuni
en api.beeuni.ai.
1. Registra tu aplicación
Ve a Account → Tokens → OAuth apps → Register app o llama al SDK:
const app = await beeuni.iam.oauthClients.create(accountId, {
name: 'Dashboards',
client_type: 'confidential', // 'public' for a browser/native app (PKCE only, no secret)
redirect_uris: ['https://dashboards.example.com/api/beeuni/auth/callback'],
scopes: ['profile', 'email', 'beeuni'],
});
// app.client_id, app.client_secret (shown once)
El registro requiere token.create en la cuenta. Los URI de redirección se comparan
byte a byte; https es obligatorio excepto en localhost.
| Ámbito | Concede a la aplicación |
|---|---|
profile |
El id, el correo electrónico y las pertenencias a cuentas del usuario (GET /v1/accounts/me). |
email |
La dirección de correo electrónico (un alias para clientes con formato OIDC). |
beeuni |
Actuar como el usuario en toda la API de Beeuni: proyectos, sesiones, archivos y comprobaciones de IAM. Sin él, el token solo sirve para identificar al usuario. |
2. Monta el controlador
// lib/beeuni-auth.ts
import { createBeeuniAuth } from '@beeuni/sdk/server';
export const auth = createBeeuniAuth({
backendUrl: 'https://api.beeuni.ai/v1',
clientId: process.env.BEEUNI_OAUTH_CLIENT_ID!,
clientSecret: process.env.BEEUNI_OAUTH_CLIENT_SECRET, // omit for a public client
redirectUri: 'https://dashboards.example.com/api/beeuni/auth/callback',
cookieSecret: process.env.BEEUNI_AUTH_COOKIE_SECRET!, // ≥ 32 chars; encrypts the session cookie
});
// app/api/beeuni/auth/[...beeuni]/route.ts (Next.js App Router)
import { auth } from '@/lib/beeuni-auth';
const handle = (request: Request) => auth.handler(request);
export { handle as GET, handle as POST };
El controlador atiende todas las rutas bajo basePath (derivado del URI de
redirección: /api/beeuni/auth arriba):
| Ruta | Función |
|---|---|
/signin?return_to=/path |
Inicia el inicio de sesión (PKCE S256 + estado en una cookie de 10 minutos) y redirige a Beeuni. |
/callback |
Intercambia el código, establece la cookie de sesión cifrada HttpOnly y redirige a return_to. |
/refresh?return_to= |
Rota el par de tokens y redirige. Lo usa requireViewer. |
/signout?return_to= |
Revoca el token de actualización en Beeuni y elimina la cookie. |
/me |
El usuario que accede en JSON, o 401. Actualiza en la misma operación cuando el token de acceso ha caducado. |
/proxy/* |
Reenvía a la API de Beeuni como el usuario que accede. El backendUrl del SDK del navegador. |
return_to siempre está limitado a una ruta del mismo origen.
3. Protege las páginas y actúa como el usuario
// middleware.ts — every page needs a viewer
import { auth } from '@/lib/beeuni-auth';
export async function middleware(request: Request) {
const gate = await auth.requireViewer(request);
if (gate.response) return gate.response; // 302 → /refresh or /signin
}
export const config = { matcher: ['/((?!api/beeuni/auth|_next).*)'] };
// a server component / route handler
const viewer = await auth.viewer(request); // { userId, email, accounts, scopes, token, expiresAt } | null
const beeuni = await auth.beeuni(request); // request-scoped client acting as the viewer
const projects = await beeuni.projects.list();
const allowed = await beeuni.iam.can(accountId, viewer!.userId, { action: 'project.write', resourceType: 'project', resourceId });
viewer() es de solo lectura y nunca consume el token de actualización de un solo uso; usa
requireViewer() en el middleware para que una página nunca se renderice sin sesión para un usuario
cuyo token de actualización siga siendo válido.
4. El navegador
import { createBeeuni } from '@beeuni/sdk';
import { SignInWithBeeuni, useBeeuniViewer } from '@beeuni/sdk/react';
const beeuni = createBeeuni(auth.clientConfig()); // backendUrl = '/api/beeuni/auth/proxy'
function Header() {
const { status, viewer } = useBeeuniViewer();
if (status === 'signed-in') return <span>{viewer.email}</span>;
return <SignInWithBeeuni className="button" />;
}
El cliente del navegador envía un token bearer centinela; /proxy lo sustituye por el
token real del usuario en el servidor. useSession, beeuni.project(id).sessions.* y
las demás llamadas al SDK funcionan sin cambios a través de él.
Qué ve el usuario
La primera vez, Beeuni muestra una pantalla de consentimiento con el nombre de tu aplicación y los ámbitos. Beeuni recuerda la decisión por usuario y aplicación, así que los siguientes inicios de sesión redirigen directamente de vuelta. Revocar una aplicación elimina todos los tokens que emitió.
Descubrimiento
GET https://api.beeuni.ai/.well-known/oauth-authorization-server (también
en /v1/oauth/.well-known/…) publica los endpoints para un cliente OAuth
genérico. El SDK no lo necesita: deriva cada endpoint de backendUrl.
Tokens de inicio de sesión para Apps
El código de una App alojada en Beeuni, y de las Apps que esta usa, acepta un segundo tipo de token: un JWT ES256 de corta duración que Beeuni emite para una App. Sin flujo OAuth, sin redirección y sin secreto de cliente.
| Valor | |
|---|---|
| Emisor | <API origin>/v1/projects/<projectId>, uno por proyecto |
| Descubrimiento | <issuer>/.well-known/openid-configuration y <issuer>/jwks.json, públicos |
| Audiencia | El id de la App a la que está destinado el token |
| Duración | 15 minutos |
| Declaraciones | sub, email, name, picture, groups, group_ids, role, account_id, project_id; el token de un agente añade kind: "agent" y no tiene grupos ni rol |
Cada App incluye los valores que verifican sus tokens: app.auth.issuer,
app.auth.audience, app.auth.jwks_uri.
import { beeuniToken, beeuniBinding, verifyBeeuniToken, requireBeeuniMember } from '@beeuni/sdk';
// In the App's browser code: a token fetcher for this App, or an App it uses.
const forThisApp = beeuniToken();
const forDb = beeuniToken({ audience: 'db' });
const db = beeuniBinding('db'); // { url: '<origin>/_beeuni/apps/db', token: forDb }
// On a server that receives the token:
const member = await verifyBeeuniToken(bearer, {
issuer: app.auth.issuer,
audience: app.auth.audience,
jwks: app.auth.jwks_uri,
});
requireBeeuniMember(member, { roles: ['owner', 'admin'] });
En una App de servidor, Beeuni inyecta BEEUNI_AUTH_ISSUER (el emisor del proyecto),
BEEUNI_AUTH_AUDIENCE (el id de la propia App) y BEEUNI_AUTH_JWKS (el conjunto de
claves del proyecto, como un URI data:), así que verifyBeeuniToken(bearer) no necesita opciones.
No establezcas BEEUNI_AUTH_* por tu cuenta: el prefijo BEEUNI_ está reservado y
beeuni validate lo rechaza.
verifyBeeuniToken rechaza un token cuando no se ha establecido ninguna audiencia (la opción o
BEEUNI_AUTH_AUDIENCE): una clave del proyecto firma los tokens de todas las Apps del
proyecto, así que aud es lo que impide aceptar el token de otra App. Pasa
audience: false solo para aceptar un token destinado a cualquier App del proyecto.
beeuniToken lee GET /_beeuni/token?audience=<slug or id> en el origen
propio de la App. La audiencia es la propia App (por defecto) o una App que esta usa
(beeuni apps link <app> --uses <other>); cualquier otra devuelve
403 app_not_linked. Un miembro, un script o un agente obtiene uno a través de la API
en su lugar: beeuni.project(projectId).apps.token(appId) o
beeuni apps token <app>. El contrato completo, el montaje de los bindings y la
configuración del backend de Convex: SDK → Apps y
Apps → Apps que usa una App.