# Se connecter avec Beeuni

Protégez l’accès à votre application avec l’identité Beeuni via une seule route, et agissez au nom de l’utilisateur connecté grâce au SDK.

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

« Se connecter avec Beeuni » fait de Beeuni le fournisseur d’identité d’une application que vous exploitez :
un tableau de bord, un outil interne, un produit vertical construit sur Beeuni. Vos utilisateurs
se connectent avec leur compte Beeuni, votre serveur sait qui ils sont, et chaque
appel Beeuni effectué par votre application s’exécute au nom de cet utilisateur avec ses
attributions de rôles. Tout le flux se trouve dans `@beeuni/sdk`. Votre application ne stocke jamais de
jeton Beeuni dans le navigateur.

Il s’agit du protocole standard OAuth 2.1 (code d’autorisation + PKCE), fourni par l’API Beeuni
à l’adresse `api.beeuni.ai`.

> Vous développez une App **hébergée par Beeuni** (`*.apps.beeuni.ai`) ? Vous n’avez besoin de rien de
> tout cela. Le contrôle d’accès des Apps a déjà authentifié le visiteur — récupérez ses informations avec
> `beeuniAppViewerToken()` / `readAppViewer()`, et obtenez des jetons de connexion pour
> l’App ou une App qu’elle utilise avec `beeuniToken()`. Voir
> [Jetons de connexion pour les Apps](#sign-in-tokens-for-apps) ci-dessous.

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

## 1. Enregistrer votre application

Accédez à **Account → Tokens → OAuth apps → Register app**, ou appelez le 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)
```

L’enregistrement nécessite `token.create` sur le compte. Les URI de redirection sont comparées
octet par octet ; `https` est obligatoire, sauf sur `localhost`.

| Portée | Accorde à l’application |
|---|---|
| `profile` | L’identifiant, l’adresse e-mail et les appartenances aux comptes de l’utilisateur (`GET /v1/accounts/me`). |
| `email` | L’adresse e-mail (un alias pour les clients au format OIDC). |
| `beeuni` | La possibilité d’agir au nom de l’utilisateur sur toute l’API Beeuni — projets, sessions, fichiers, vérifications IAM. Sans cette portée, le jeton sert uniquement à l’identité. |

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

## 2. Monter le gestionnaire

```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 };
```

Le gestionnaire dessert toutes les routes sous `basePath` (dérivé de l’URI de
redirection — `/api/beeuni/auth` ci-dessus) :

| Chemin | Action |
|---|---|
| `/signin?return_to=/path` | Lance la connexion (PKCE S256 + état dans un cookie de 10 minutes) et redirige vers Beeuni. |
| `/callback` | Échange le code, définit le cookie de session chiffré `HttpOnly`, puis redirige vers `return_to`. |
| `/refresh?return_to=` | Renouvelle la paire de jetons et redirige. Utilisé par `requireViewer`. |
| `/signout?return_to=` | Révoque le jeton de renouvellement auprès de Beeuni et supprime le cookie. |
| `/me` | Le visiteur au format JSON, ou `401`. Renouvelle le jeton à la volée lorsque le jeton d’accès a expiré. |
| `/proxy/*` | Transmet à l’API Beeuni au nom du visiteur. Le `backendUrl` du SDK navigateur. |

`return_to` est toujours limité à un chemin de même origine.

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

## 3. Protéger les pages et agir au nom de l’utilisateur

```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()` est en lecture seule et ne consomme jamais le jeton de renouvellement à usage unique ; utilisez
`requireViewer()` dans le middleware pour qu’une page ne s’affiche jamais comme déconnectée pour un utilisateur
dont le jeton de renouvellement est encore valide.

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

## 4. Le navigateur

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

Le client navigateur envoie un jeton bearer sentinelle ; `/proxy` le remplace par le
véritable jeton du visiteur sur le serveur. `useSession`, `beeuni.project(id).sessions.*` et
tous les autres appels du SDK fonctionnent sans modification par son intermédiaire.

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

## Ce que voit l’utilisateur

La première fois, Beeuni affiche un écran de consentement indiquant le nom de votre application et les portées.
Beeuni mémorise la décision pour chaque utilisateur et chaque application ; les connexions suivantes redirigent donc
directement vers votre application. Révoquer une application supprime tous les jetons qu’elle a émis.

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

## Découverte

`GET https://api.beeuni.ai/.well-known/oauth-authorization-server` (également
sous `/v1/oauth/.well-known/…`) publie les points de terminaison pour un client OAuth
générique. Le SDK n’en a pas besoin — il déduit chaque point de terminaison de `backendUrl`.

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

## Jetons de connexion pour les Apps

Le code d’une App hébergée par Beeuni, ainsi que les Apps qu’elle utilise, accepte un second type
de jeton : un JWT ES256 court que Beeuni émet pour une seule App. Aucun flux OAuth, aucune
redirection, aucun secret client.

| | Valeur |
|---|---|
| Émetteur | `<API origin>/v1/projects/<projectId>`, un par projet |
| Découverte | `<issuer>/.well-known/openid-configuration` et `<issuer>/jwks.json`, publics |
| Audience | L’identifiant de l’App à laquelle le jeton est destiné |
| Durée de validité | 15 minutes |
| Claims | `sub`, `email`, `name`, `picture`, `groups`, `group_ids`, `role`, `account_id`, `project_id` ; le jeton d’un agent ajoute `kind: "agent"` et ne contient ni groupes ni rôle |

Chaque App contient les valeurs permettant de vérifier ses jetons : `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'] });
```

Dans une App serveur, Beeuni injecte `BEEUNI_AUTH_ISSUER` (l’émetteur du projet),
`BEEUNI_AUTH_AUDIENCE` (l’identifiant de l’App elle-même) et `BEEUNI_AUTH_JWKS` (le jeu de
clés du projet, sous forme d’URI `data:`), de sorte que `verifyBeeuniToken(bearer)` ne nécessite aucune option.
Ne définissez pas `BEEUNI_AUTH_*` vous-même : le préfixe `BEEUNI_` est réservé et
`beeuni validate` le rejette.

`verifyBeeuniToken` refuse un jeton lorsqu’aucune audience n’est définie (via l’option ou
`BEEUNI_AUTH_AUDIENCE`) : une seule clé de projet signe les jetons de toutes les Apps du
projet ; c’est donc `aud` qui empêche l’utilisation du jeton d’une autre App. Passez
`audience: false` uniquement pour accepter un jeton destiné à n’importe quelle App du projet.

`beeuniToken` lit `GET /_beeuni/token?audience=<slug or id>` sur l’origine
propre de l’App. L’audience est l’App elle-même (par défaut) ou une App qu’elle utilise
(`beeuni apps link <app> --uses <other>`) ; toute autre audience reçoit
`403 app_not_linked`. Un membre, un script ou un agent en émet plutôt un via l’API :
`beeuni.project(projectId).apps.token(appId)`, ou
`beeuni apps token <app>`. Le contrat complet, le montage des bindings et la
configuration du backend Convex : [SDK → Apps](/docs/fr/sdk/apps) et
[Apps → Apps utilisées par une App](/docs/fr/feature-flags/apps#apps-an-app-uses-sign-in-tokens-and-bindings).
