@geekapps/auth-nextjs
v0.22.0
Published
SDK Next.js para consumir o Geekapps Auth/IDP.
Readme
@geekapps/auth-nextjs
SDK Next.js para consumir o Geekapps Auth/IDP.
Instalação
bun add @geekapps/auth-nextjsServer
// src/lib/auth.ts
import { configureGeekappsAuth } from "@geekapps/auth-nextjs/server";
export const auth = configureGeekappsAuth({
issuer: process.env.GEEKAPPS_AUTH_ISSUER!,
clientId: process.env.GEEKAPPS_AUTH_CLIENT_ID!,
clientSecret: process.env.GEEKAPPS_AUTH_CLIENT_SECRET,
redirectUri: process.env.GEEKAPPS_AUTH_REDIRECT_URI!,
scopes: ["openid", "profile", "email"],
});// app/api/auth/[...routes]/route.ts
import { auth } from "@/lib/auth";
export const GET = auth.handlers.GET;
export const POST = auth.handlers.POST;
export const PATCH = auth.handlers.PATCH;
export const DELETE = auth.handlers.DELETE;Client
import { GeekappsAuthProvider } from "@geekapps/auth-nextjs/client";
export function Providers({ children }: { children: React.ReactNode }) {
return <GeekappsAuthProvider>{children}</GeekappsAuthProvider>;
}Hooks principais:
useAuth()useOrg()— organizações do próprio usuário (as que ele já é membro/dono)useAccounts()useUsers()useRoles()usePermissions()useSessions()useMembers()— membros e convites de uma organizaçãousePublicOrganizations()— descoberta de organizações públicas do app, separado deuseOrg()useJoinRequests()— solicitações de entrada em organizações públicasuseInvites()— aceite/recusa de convite por email neste próprio app
Organizações: membros, convites e organizações públicas
import { useMembers } from "@geekapps/auth-nextjs/client";
const { listMembers, addMember, updateMember, inviteMember, listInvites, revokeInvite } = useMembers();
const members = await listMembers(org.id);
// redirectUri é obrigatório — precisa estar cadastrado em Application.redirectUris,
// e este próprio app implementa a rota que trata o ?invite_token= recebido lá.
const result = await inviteMember(org.id, {
email: "[email protected]",
roleId: "role-uuid",
redirectUri: "https://meuapp.com/invites/accept",
});import { useInvites } from "@geekapps/auth-nextjs/client";
// Na sua própria rota /invites/accept?invite_token=...
const { getInvitePreview, acceptInvite, rejectInvite } = useInvites();
const preview = await getInvitePreview(token); // funciona sem sessão
if (!preview.alreadyAccepted && !preview.currentAccount) {
// sem sessão — chame signIn() (de useAuth()) e volte pra essa rota depois
}
const result = await acceptInvite(token); // loginRequired:true se não houver sessão
await rejectInvite(token); // não exige loginimport { usePublicOrganizations, useJoinRequests } from "@geekapps/auth-nextjs/client";
const { listPublicOrganizations } = usePublicOrganizations();
const publicOrgs = await listPublicOrganizations();
const { requestToJoin, getMyJoinRequest, listJoinRequests, approveJoinRequest } = useJoinRequests();
await requestToJoin(org.id, { message: "Trabalho na região" });Organizações com CNPJ / Tax ID (type: "legal")
Toda organização tem um type: "standard" (padrão, sem documento) ou "legal" (pessoa jurídica, associada a um Tax ID). O Tax ID é único por aplicação e não muda depois da criação.
taxIdType: "br_cnpj"(padrão) — valida os dígitos verificadores (inclusive CNPJ alfanumérico) e busca automaticamente razão social, nome fantasia, contato e endereço na Receita (via BrasilAPI).taxIdType: "generic"— Tax ID de outro país; só é normalizado, etaxIdCountry(ISO, ex.:"US") é obrigatório.
Obrigatórios de uma organização legal: legalName e legalAddress.street, .city, .postalCode, .country. O que a consulta não trouxer precisa ser informado pelo usuário.
// 1. Pré-preenche o formulário e descobre o que ainda falta pedir
const { lookupTaxId, createOrganization } = useOrg();
const result = await lookupTaxId("12.345.678/0001-95"); // type padrão: "br_cnpj"
if (!result.success) throw new Error(result.error);
const lookup = result.data;
// lookup.data → { legalName, tradeName, legalEmail, legalPhone, legalAddress }
// lookup.missingFields → ex.: ["legalAddress.postalCode"]
// lookup.alreadyRegistered → já existe org com esse CNPJ neste app
// 2. Cria — o servidor refaz a consulta e completa o que não foi enviado
const org = await createOrganization({
name: lookup.suggestedName ?? "Acme",
slug: "acme",
type: "legal",
taxId: lookup.taxId,
legalAddress: { postalCode: "01001000" }, // só o que faltou
});
// Se ainda faltar obrigatório: { success: false, code: "legal_fields_required", missingFields }.
// Tax ID genérico (outro país)
await createOrganization({
name: "Acme LLC", slug: "acme-llc", type: "legal",
taxId: "12-3456789", taxIdType: "generic", taxIdCountry: "US",
legalName: "Acme LLC",
legalAddress: { street: "1 Main St", city: "Austin", postalCode: "73301", country: "US" },
});Com @geekapps/auth-elements-nextjs (0.3+), o CreateOrganizationForm (também usado no OrgSwitcher e no ManageOrganizationModal) já entrega essa UI pronta: alterna Padrão/Empresa, busca o CNPJ e pede só os obrigatórios que faltarem.
Papel do usuário na organização
useOrg() traz a participação do usuário logado na organização ativa, carregada sozinha ao trocar de organização:
const { currentMembership, getMyMembership, refreshMembership } = useOrg();
currentMembership?.role?.key; // ex.: "admin"
currentMembership?.isOwner; // dono passa em qualquer checagem
currentMembership?.permissions.includes("org:update");
const other = await getMyMembership(orgId); // outra organizaçãoQualquer membro ativo pode listar os membros da organização com a role de cada um (listMembers). Editar, adicionar ou remover membros continua exigindo as permissões org:members:*.
Convites recebidos pelo usuário
Para mostrar os convites dentro do app, sem depender do link do e-mail:
const { listMyInvites, acceptInviteById, rejectInviteById } = useInvites();
const invites = await listMyInvites(); // pendentes para o e-mail da conta, só deste app
await acceptInviteById(invites[0].id); // cria a membership e atualiza as organizações
await rejectInviteById(invites[1].id);Só o próprio convidado (mesmo e-mail da conta) consegue aceitar ou recusar. Quem enviou o convite não recebe nenhum token.
Standalone client auth actions
@geekapps/auth-nextjs/client exporta funções standalone usadas pela auth-screens:
import {
loginWithPassword,
registerWithPassword,
requestPasswordlessLink,
startGoogleLogin,
} from "@geekapps/auth-nextjs/client";Depois de aplicar esse source, rode o build do package para gerar o dist:
bun --filter @geekapps/auth-nextjs buildTipos das App Connections
Em desenvolvimento (NODE_ENV=development), o SDK baixa o OpenAPI de cada app conectável e escreve os tipos em geekapps-connections/, ao subir e depois a cada minuto. O gerador vem embutido no próprio SDK; só é preciso ter o openapi-typescript como devDependency:
npm i -D openapi-typescriptSem ele, a geração simplesmente não roda e o app funciona normalmente. Para desligar, use GEEKAPPS_CONNECTIONS_TYPES=off. Para checar no CI se os tipos estão em dia:
import { generateConnectionTypes } from "@geekapps/auth-nextjs/typegen";
const { stale } = await generateConnectionTypes({
credentials: { issuer, clientId, clientSecret },
check: true,
});
if (stale.length) process.exit(1);