Accedi con Beeuni
Proteggi l'accesso alla tua app con l'identità Beeuni tramite un'unica route e agisci come l'utente autenticato tramite l'SDK.
“Accedi con Beeuni” rende Beeuni il provider di identità per un’app che gestisci:
una dashboard, uno strumento interno, un prodotto verticale basato su Beeuni. I tuoi utenti
accedono con il loro account Beeuni, il tuo server sa chi sono e ogni
chiamata Beeuni effettuata dalla tua app viene eseguita come quell’utente, con le sue
assegnazioni di ruolo. L’intero flusso risiede in @beeuni/sdk. La tua app non memorizza mai un
token Beeuni nel browser.
È OAuth 2.1 standard (codice di autorizzazione + PKCE), fornito dall’API Beeuni
su api.beeuni.ai.
1. Registra la tua app
Vai a Account → Tokens → OAuth apps → Register app, oppure chiama l’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)
La registrazione richiede token.create sull’account. Gli URI di reindirizzamento vengono confrontati
byte per byte; https è obbligatorio tranne su localhost.
| Ambito | Concede all’app |
|---|---|
profile |
L’id, l’email e le appartenenze agli account dell’utente (GET /v1/accounts/me). |
email |
L’indirizzo email (un alias per i client conformi a OIDC). |
beeuni |
La possibilità di agire come l’utente sull’intera API Beeuni: progetti, sessioni, file, verifiche IAM. Senza questo ambito, il token serve solo per l’identità. |
2. Monta l’handler
// 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 };
L’handler gestisce ogni route sotto basePath (derivato dall’URI di
reindirizzamento: /api/beeuni/auth qui sopra):
| Percorso | Funzione |
|---|---|
/signin?return_to=/path |
Avvia l’accesso (PKCE S256 + state in un cookie di 10 minuti) e reindirizza a Beeuni. |
/callback |
Scambia il codice, imposta il cookie di sessione crittografato HttpOnly e reindirizza a return_to. |
/refresh?return_to= |
Ruota la coppia di token e reindirizza. Usato da requireViewer. |
/signout?return_to= |
Revoca il token di aggiornamento su Beeuni ed elimina il cookie. |
/me |
L’utente corrente in formato JSON, oppure 401. Esegue l’aggiornamento direttamente quando il token di accesso è scaduto. |
/proxy/* |
Inoltra all’API Beeuni come utente corrente. Il backendUrl dell’SDK per il browser. |
return_to è sempre limitato a un percorso della stessa origine.
3. Proteggi l’accesso alle pagine e agisci come l’utente
// 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() è di sola lettura e non consuma mai il token di aggiornamento monouso; usa
requireViewer() nel middleware affinché una pagina non venga mai renderizzata come se l’utente non fosse autenticato
quando il suo token di aggiornamento è ancora valido.
4. Il browser
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" />;
}
Il client del browser invia un bearer sentinella; /proxy lo sostituisce con il
token reale dell’utente corrente sul server. useSession, beeuni.project(id).sessions.* e
ogni altra chiamata SDK funzionano senza modifiche tramite questo meccanismo.
Cosa vede l’utente
La prima volta, Beeuni mostra una schermata di consenso che indica il nome della tua app e gli ambiti. Beeuni ricorda la decisione per ogni utente e app, quindi gli accessi successivi reindirizzano direttamente all’app. La revoca di un’app elimina tutti i token che ha emesso.
Discovery
GET https://api.beeuni.ai/.well-known/oauth-authorization-server (anche
sotto /v1/oauth/.well-known/…) pubblica gli endpoint per un client OAuth
generico. L’SDK non ne ha bisogno: ricava ogni endpoint da backendUrl.
Token di accesso per le Apps
Il codice in un’App ospitata da Beeuni e nelle Apps che utilizza accetta un secondo tipo di token: un JWT ES256 breve che Beeuni emette per una singola App. Nessun flusso OAuth, nessun reindirizzamento, nessun segreto client.
| Valore | |
|---|---|
| Emittente | <API origin>/v1/projects/<projectId>, uno per progetto |
| Discovery | <issuer>/.well-known/openid-configuration e <issuer>/jwks.json, pubblici |
| Destinatario | L’id dell’App a cui è destinato il token |
| Durata | 15 minuti |
| Claim | sub, email, name, picture, groups, group_ids, role, account_id, project_id; il token di un agente aggiunge kind: "agent" e non ha gruppi né ruolo |
Ogni App contiene i valori per verificare i propri token: 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'] });
In un’App server, Beeuni inietta BEEUNI_AUTH_ISSUER (l’emittente del progetto),
BEEUNI_AUTH_AUDIENCE (l’id dell’App stessa) e BEEUNI_AUTH_JWKS (il set di chiavi del progetto,
come URI data:), quindi verifyBeeuniToken(bearer) non richiede opzioni.
Non impostare BEEUNI_AUTH_* autonomamente: il prefisso BEEUNI_ è riservato e
beeuni validate lo rifiuta.
verifyBeeuniToken rifiuta un token quando non è impostato alcun destinatario (tramite l’opzione o
BEEUNI_AUTH_AUDIENCE): una sola chiave del progetto firma i token di tutte le Apps del
progetto, quindi è aud a impedire l’accesso con il token di un’altra App. Passa
audience: false solo per accettare un token destinato a qualsiasi App del progetto.
beeuniToken legge GET /_beeuni/token?audience=<slug or id> sull’origine
dell’App stessa. Il destinatario è l’App stessa (per impostazione predefinita) o un’App che utilizza
(beeuni apps link <app> --uses <other>); per qualsiasi altro destinatario, la risposta è
403 app_not_linked. Un membro, uno script o un agente ne emette invece uno tramite l’API:
beeuni.project(projectId).apps.token(appId), oppure
beeuni apps token <app>. Il contratto completo, il montaggio dei binding e la
configurazione del backend Convex: SDK → Apps e
Apps → Apps utilizzate da un’App.