# 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.

Canonical page: https://beeuni.ai/docs/es/sdk/sign-in

"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`.

> ¿Estás creando una App **alojada en Beeuni** (`*.apps.beeuni.ai`)? No necesitas nada de
> esto. El control de acceso de Apps ya autenticó al usuario que accede: consulta sus datos con
> `beeuniAppViewerToken()` / `readAppViewer()` y obtén tokens de inicio de sesión para la
> App o una App que esta use con `beeuniToken()`. Consulta
> [Tokens de inicio de sesión para Apps](#sign-in-tokens-for-apps) más abajo.

<a id="1-register-your-app"></a>

## 1. Registra tu aplicación

Ve a **Account → Tokens → OAuth apps → Register app** o llama al SDK:

```ts
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. |

<a id="2-mount-the-handler"></a>

## 2. Monta el controlador

```ts
// 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
});
```

```ts
// 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.

<a id="3-gate-pages-and-act-as-the-user"></a>

## 3. Protege las páginas y actúa como el usuario

```ts
// 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).*)'] };
```

```ts
// 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.

<a id="4-the-browser"></a>

## 4. El navegador

```tsx
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.

<a id="what-the-user-sees"></a>

## 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ó.

<a id="discovery"></a>

## 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`.

<a id="sign-in-tokens-for-apps"></a>

## 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`.

```ts
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](/docs/es/sdk/apps) y
[Apps → Apps que usa una App](/docs/es/feature-flags/apps#apps-an-app-uses-sign-in-tokens-and-bindings).
