Mit Beeuni anmelden
Sichere deine eigene App mit einer einzigen Route über die Beeuni-Identität ab und handle über das SDK als angemeldeter Nutzer.
„Mit Beeuni anmelden“ macht Beeuni zum Identitätsanbieter für eine App, die du betreibst:
ein Dashboard, ein internes Tool oder ein auf Beeuni aufgebautes Branchenprodukt. Deine Nutzer
melden sich mit ihrem Beeuni-Konto an, dein Server weiß, wer sie sind, und jeder
Beeuni-Aufruf deiner App wird als dieser Nutzer mit dessen
Rollenzuweisungen ausgeführt. Der gesamte Ablauf liegt in @beeuni/sdk. Deine App speichert nie ein
Beeuni-Token im Browser.
Es handelt sich um standardmäßiges OAuth 2.1 (Autorisierungscode + PKCE), bereitgestellt von der Beeuni API
unter api.beeuni.ai.
1. Registriere deine App
Gehe zu Account → Tokens → OAuth apps → Register app oder rufe das SDK auf:
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)
Die Registrierung erfordert token.create für das Konto. Redirect-URIs werden
Byte für Byte verglichen; https ist erforderlich, außer auf localhost.
| Scope | Gewährt der App |
|---|---|
profile |
Die ID, E-Mail-Adresse und Kontomitgliedschaften des Nutzers (GET /v1/accounts/me). |
email |
Die E-Mail-Adresse (ein Alias für OIDC-kompatible Clients). |
beeuni |
Handeln als Nutzer über die gesamte Beeuni API — Projekte, Sitzungen, Dateien, IAM-Prüfungen. Ohne diesen Scope dient das Token nur zur Identifizierung. |
2. Binde den Handler ein
// 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 };
Der Handler bedient jede Route unter basePath (abgeleitet von der Redirect-
URI — oben /api/beeuni/auth):
| Pfad | Funktion |
|---|---|
/signin?return_to=/path |
Startet die Anmeldung (PKCE S256 + State in einem 10-Minuten-Cookie) und leitet zu Beeuni weiter. |
/callback |
Tauscht den Code aus, setzt das verschlüsselte Sitzungscookie HttpOnly und leitet zu return_to weiter. |
/refresh?return_to= |
Rotiert das Tokenpaar und leitet weiter. Wird von requireViewer verwendet. |
/signout?return_to= |
Widerruft das Refresh-Token bei Beeuni und löscht das Cookie. |
/me |
Der Betrachter als JSON oder 401. Aktualisiert direkt, wenn das Zugriffstoken abgelaufen ist. |
/proxy/* |
Leitet als Betrachter an die Beeuni API weiter. Das backendUrl des Browser-SDKs. |
return_to ist immer auf einen Pfad derselben Origin beschränkt.
3. Schütze Seiten und handle als Nutzer
// 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() ist schreibgeschützt und verbraucht nie das einmalig verwendbare Refresh-Token; verwende
requireViewer() in der Middleware, damit eine Seite für einen Nutzer,
dessen Refresh-Token noch gültig ist, nie im abgemeldeten Zustand gerendert wird.
4. Der 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" />;
}
Der Browser-Client sendet einen Sentinel-Bearer; /proxy ersetzt ihn auf dem Server durch das
echte Token des Betrachters. useSession, beeuni.project(id).sessions.* und
alle anderen SDK-Aufrufe funktionieren darüber unverändert.
Was der Nutzer sieht
Beim ersten Mal zeigt Beeuni einen Zustimmungsbildschirm mit dem Namen deiner App und den Scopes. Beeuni merkt sich die Entscheidung pro Nutzer und App, sodass spätere Anmeldungen direkt zurückleiten. Der Widerruf einer App löscht jedes von ihr ausgestellte Token.
Discovery
GET https://api.beeuni.ai/.well-known/oauth-authorization-server (auch
unter /v1/oauth/.well-known/…) veröffentlicht die Endpunkte für einen generischen OAuth-
Client. Das SDK benötigt dies nicht — es leitet jeden Endpunkt aus backendUrl ab.
Anmeldetokens für Apps
Code in einer von Beeuni gehosteten App und in den von ihr genutzten Apps akzeptiert eine zweite Art von Token: ein kurzes ES256-JWT, das Beeuni für eine einzelne App ausstellt. Kein OAuth-Ablauf, keine Weiterleitung, kein Client-Secret.
| Wert | |
|---|---|
| Aussteller | <API origin>/v1/projects/<projectId>, einer pro Projekt |
| Discovery | <issuer>/.well-known/openid-configuration und <issuer>/jwks.json, öffentlich |
| Zielgruppe | Die ID der App, für die das Token bestimmt ist |
| Gültigkeitsdauer | 15 Minuten |
| Claims | sub, email, name, picture, groups, group_ids, role, account_id, project_id; das Token eines Agenten enthält zusätzlich kind: "agent" und hat keine Gruppen oder Rolle |
Jede App enthält die Werte zur Prüfung ihrer 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'] });
In einer Server-App fügt Beeuni BEEUNI_AUTH_ISSUER (den Projektaussteller),
BEEUNI_AUTH_AUDIENCE (die eigene ID der App) und BEEUNI_AUTH_JWKS (den
Schlüsselsatz des Projekts als data:-URI) ein, sodass verifyBeeuniToken(bearer) keine Optionen benötigt.
Setze BEEUNI_AUTH_* nicht selbst: Das Präfix BEEUNI_ ist reserviert und
beeuni validate weist es zurück.
verifyBeeuniToken lehnt ein Token ab, wenn keine Zielgruppe gesetzt ist (die Option oder
BEEUNI_AUTH_AUDIENCE): Ein Projektschlüssel signiert die Tokens jeder App im
Projekt, daher verhindert aud die Annahme eines Tokens einer anderen App. Übergib
audience: false nur, um ein Token für eine beliebige App des Projekts zu akzeptieren.
beeuniToken liest GET /_beeuni/token?audience=<slug or id> auf der
eigenen Origin der App. Die Zielgruppe ist die App selbst (Standard) oder eine von ihr genutzte App
(beeuni apps link <app> --uses <other>); jede andere führt zu
403 app_not_linked. Ein Mitglied, ein Skript oder ein Agent stellt stattdessen eines über die API
aus: beeuni.project(projectId).apps.token(appId) oder
beeuni apps token <app>. Der vollständige Vertrag, die Einbindung der Bindings und die
Einrichtung des Convex-Backends: SDK → Apps und
Apps → Von einer App genutzte Apps.