Entrar com Beeuni
Proteja o acesso ao seu próprio app com a identidade Beeuni usando uma única rota e atue como o usuário autenticado por meio do SDK.
“Entrar com Beeuni” torna o Beeuni o provedor de identidade de um app que você executa:
um painel, uma ferramenta interna, um produto vertical criado sobre o Beeuni. Seus usuários
entram com a conta Beeuni, seu servidor sabe quem eles são, e cada
chamada ao Beeuni feita pelo seu app é executada como esse usuário, com as atribuições de função
dele. Todo o fluxo fica em @beeuni/sdk. Seu app nunca armazena um
token Beeuni no navegador.
É o OAuth 2.1 padrão (código de autorização + PKCE), servido pela API Beeuni
em api.beeuni.ai.
1. Registre seu app
Acesse Account → Tokens → OAuth apps → Register app ou chame o 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)
O registro exige token.create na conta. Os URIs de redirecionamento são comparados
byte a byte; https é obrigatório, exceto em localhost.
| Escopo | Concede ao app |
|---|---|
profile |
O id, o e-mail e as associações a contas do usuário (GET /v1/accounts/me). |
email |
O endereço de e-mail (um alias para clientes com formato OIDC). |
beeuni |
Atuar como o usuário em toda a API Beeuni — projetos, sessões, arquivos, verificações de IAM. Sem ele, o token serve apenas para identidade. |
2. Monte o 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 };
O handler atende todas as rotas em basePath (derivado do URI de
redirecionamento — /api/beeuni/auth acima):
| Caminho | Ação |
|---|---|
/signin?return_to=/path |
Inicia o login (PKCE S256 + state em um cookie de 10 minutos) e redireciona para o Beeuni. |
/callback |
Troca o código, define o cookie de sessão criptografado HttpOnly e redireciona para return_to. |
/refresh?return_to= |
Rotaciona o par de tokens e redireciona. Usado por requireViewer. |
/signout?return_to= |
Revoga o token de atualização no Beeuni e remove o cookie. |
/me |
O visitante em JSON, ou 401. Atualiza durante a própria chamada quando o token de acesso expirou. |
/proxy/* |
Encaminha para a API Beeuni como o visitante. O backendUrl do SDK do navegador. |
return_to é sempre restrito a um caminho da mesma origem.
3. Restrinja o acesso às páginas e atue como o usuário
// 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() é somente leitura e nunca consome o token de atualização de uso único; use
requireViewer() no middleware para que uma página nunca seja renderizada como desconectada para um usuário
cujo token de atualização ainda é válido.
4. O navegador
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" />;
}
O cliente do navegador envia um bearer sentinela; /proxy o substitui pelo
token real do visitante no servidor. useSession, beeuni.project(id).sessions.* e
todas as outras chamadas do SDK funcionam sem alterações por meio dele.
O que o usuário vê
Na primeira vez, o Beeuni mostra uma tela de consentimento com o nome do seu app e os escopos. O Beeuni lembra a decisão por usuário e app, então os logins seguintes redirecionam diretamente de volta. Revogar um app exclui todos os tokens que ele emitiu.
Descoberta
GET https://api.beeuni.ai/.well-known/oauth-authorization-server (também
em /v1/oauth/.well-known/…) publica os endpoints para um cliente OAuth
genérico. O SDK não precisa disso — ele deriva todos os endpoints de backendUrl.
Tokens de login para Apps
O código em um App hospedado pelo Beeuni, e nos Apps que ele usa, aceita um segundo tipo de token: um JWT ES256 curto que o Beeuni emite para um App. Sem fluxo OAuth, sem redirecionamento, sem segredo do cliente.
| Valor | |
|---|---|
| Emissor | <API origin>/v1/projects/<projectId>, um por projeto |
| Descoberta | <issuer>/.well-known/openid-configuration e <issuer>/jwks.json, públicos |
| Público-alvo | O id do App ao qual o token se destina |
| Validade | 15 minutos |
| Claims | sub, email, name, picture, groups, group_ids, role, account_id, project_id; o token de um agente adiciona kind: "agent" e não tem grupos nem função |
Cada App contém os valores que verificam seus 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'] });
Em um App de servidor, o Beeuni injeta BEEUNI_AUTH_ISSUER (o emissor do projeto),
BEEUNI_AUTH_AUDIENCE (o id do próprio App) e BEEUNI_AUTH_JWKS (o conjunto de
chaves do projeto, como um URI data:), então verifyBeeuniToken(bearer) não precisa de opções.
Não defina BEEUNI_AUTH_* por conta própria: o prefixo BEEUNI_ é reservado e
beeuni validate o rejeita.
verifyBeeuniToken recusa um token quando nenhum público-alvo está definido (a opção ou
BEEUNI_AUTH_AUDIENCE): uma chave do projeto assina os tokens de todos os Apps do
projeto, então aud é o que impede a entrada do token de outro App. Passe
audience: false apenas para aceitar um token de qualquer App do projeto.
beeuniToken lê GET /_beeuni/token?audience=<slug or id> na origem do
próprio App. O público-alvo é o próprio App (o padrão) ou um App que ele usa
(beeuni apps link <app> --uses <other>); qualquer outro retorna
403 app_not_linked. Um membro, um script ou um agente emite um token pela API
em vez disso: beeuni.project(projectId).apps.token(appId), ou
beeuni apps token <app>. O contrato completo, a montagem dos bindings e a
configuração do backend Convex: SDK → Apps e
Apps → Apps que um App usa.