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.
« 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.
1. Enregistrer votre application
Accédez à Account → Tokens → OAuth apps → Register app, ou appelez le 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)
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é. |
2. Monter le gestionnaire
// 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 };
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.
3. Protéger les pages et agir au nom de l’utilisateur
// 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() 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.
4. Le navigateur
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.
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.
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.
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.
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 et
Apps → Apps utilisées par une App.