npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-nextjs

Server

// 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ção
  • usePublicOrganizations() — descoberta de organizações públicas do app, separado de useOrg()
  • useJoinRequests() — solicitações de entrada em organizações públicas
  • useInvites() — 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 login
import { 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, e taxIdCountry (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ção

Qualquer 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 build

Tipos 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-typescript

Sem 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);