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

@elefantedev/auth-client

v0.2.0

Published

Server-side OIDC/BFF client for Elefante Auth

Readme

@elefantedev/auth-client

Cliente OIDC server-side para integrar aplicativos independentes ao Elefante Auth. Ele implementa o padrão BFF: tokens ficam cifrados em cookie HttpOnly host-only e nunca são entregues ao JavaScript do navegador.

O pacote é ESM, não possui dependências de runtime e funciona em Cloudflare Workers ou Node.js 20+ com Web Crypto, Fetch, Request e Response.

Este pacote é distribuído sob licença proprietária. Consulte LICENSE.

Instalação

Instale a versão publicada no npm:

pnpm add @elefantedev/auth-client

Também é possível instalar o tarball gerado localmente:

pnpm add ./elefantedev-auth-client-0.2.0.tgz

Não importe este pacote em uma SPA. clientSecret, tokens e chaves de cookie devem existir somente no Worker/BFF.

Configuração

import {
  ElefanteAuthClient,
  type FetcherLike,
} from "@elefantedev/auth-client";

interface Env {
  AUTH_ISSUER: string;
  AUTH_CLIENT_ID: string;
  AUTH_CLIENT_SECRET: string;
  AUTH_COOKIE_KEY: string;
  APP_BASE_URL: string;
  AUTH?: FetcherLike;
}

export function createAuth(env: Env) {
  return new ElefanteAuthClient({
    issuer: env.AUTH_ISSUER,
    clientId: env.AUTH_CLIENT_ID,
    clientSecret: env.AUTH_CLIENT_SECRET,
    redirectUri: `${env.APP_BASE_URL}/auth/callback`,
    postLogoutRedirectUri: `${env.APP_BASE_URL}/`,
    cookieKeys: [
      { version: "v1", secret: env.AUTH_COOKIE_KEY },
    ],
    sessionMaxAgeSeconds: 14 * 24 * 60 * 60,
    ...(env.AUTH ? { binding: env.AUTH } : {}),
  });
}

A chave de cookie deve ter no mínimo 32 bytes. Para rotacioná-la sem encerrar todas as sessões, coloque a chave nova primeiro e mantenha temporariamente as anteriores:

cookieKeys: [
  { version: "v2", secret: env.AUTH_COOKIE_KEY_V2 },
  { version: "v1", secret: env.AUTH_COOKIE_KEY_V1 },
]

sessionMaxAgeSeconds controla a duração absoluta da sessão BFF. O default e o limite máximo são 30 dias; sessões de navegador continuam sem Max-Age quando o login usa remember: false.

Rotas mínimas do BFF

const auth = createAuth(env);
const url = new URL(request.url);

if (request.method === "GET" && url.pathname === "/login") {
  return auth.beginLogin({ returnTo: "/conta" });
}

if (request.method === "GET" && url.pathname === "/auth/callback") {
  const result = await auth.handleCallback(request);
  const response = new Response(null, {
    status: 303,
    headers: {
      Location: new URL(result.redirectTo, request.url).toString(),
    },
  });
  for (const cookie of result.cookies) {
    response.headers.append("Set-Cookie", cookie);
  }
  return response;
}

if (request.method === "GET" && url.pathname === "/conta") {
  return auth.requireUser(
    request,
    ({ user }) => Response.json({ user }),
    () => Response.redirect(new URL("/login", request.url), 302),
  );
}

if (request.method === "POST" && url.pathname === "/logout") {
  return auth.logout(request, { global: true, redirectTo: "/" });
}

handleCallback(), authenticate() e requireUser() nunca retornam access, ID ou refresh tokens. O aplicativo continua responsável por validar origem/CSRF nos endpoints de mutação, aplicar cabeçalhos de segurança e nunca registrar resultados completos, cookies ou códigos.

Identidade externa e autorização local

O Elefante Auth comprova a identidade. Cada aplicativo decide localmente, usando user.sub, se essa identidade pode entrar e quais permissões possui. Não use e-mail como chave estável e não crie usuários, papéis ou permissões automaticamente dentro do SDK.

No callback, valide o acesso local antes de colocar o cookie de sessão no navegador. Se o usuário não puder acessar o app, reject() revoga os tokens recém-emitidos sem expô-los:

const callback = await auth.handleCallback(c.req.raw);
const localUser = await findActiveUserBySubject(
  c.env.DB,
  callback.user.sub,
);

if (!localUser) {
  const cookies = await callback.reject();
  const response = c.json(
    { error: "Acesso não autorizado para este aplicativo." },
    403,
  );
  for (const cookie of cookies) {
    response.headers.append("Set-Cookie", cookie);
  }
  return response;
}

const response = c.redirect(callback.redirectTo, 303);
for (const cookie of callback.cookies) {
  response.headers.append("Set-Cookie", cookie);
}
return response;

Um middleware Hono pode aplicar a autorização local nas requisições seguintes:

const identity = await auth.authenticate(c.req.raw);

if (!identity) {
  return c.json({ error: "Login necessário." }, 401);
}

const localUser = await findActiveUserBySubject(
  c.env.DB,
  identity.user.sub,
);

if (!localUser) {
  return c.json(
    { error: "Acesso não autorizado para este aplicativo." },
    403,
  );
}

c.set("user", withPermissions(localUser));
await next();

for (const cookie of identity.cookies) {
  c.header("Set-Cookie", cookie, { append: true });
}

identity.cookies normalmente está vazio e recebe valores apenas quando a biblioteca rotaciona tokens e precisa atualizar o cookie opaco.

Erros

AuthClientError.code usa a union exportada AuthClientErrorCode. Sessão ausente, inválida ou revogada faz authenticate() retornar null; indisponibilidade do IdP lança provider_unavailable com status 503. Callbacks inválidos, respostas incorretas do provedor, falhas de revogação e eventos inválidos permanecem erros tipados para o aplicativo decidir entre 400, 401, 502 e 503.

Eventos de conta

Webhooks do Elefante Auth chegam como JWS compacto. Verifique assinatura, issuer, audience, idade e tipo antes de processar:

const event = await auth.verifyEventJws(await request.text(), {
  allowedTypes: [
    "account.email_changed",
    "account.deletion_requested",
    "account.deletion_cancelled",
    "account.deleted",
  ],
});

O consumidor deve persistir event.jti e rejeitar replay antes de confirmar o recebimento.

Testes de aplicativos consumidores

O subpath @elefantedev/auth-client/testing fornece um fake determinístico e independente de Vitest, Jest ou outro test runner. Ele implementa ElefanteAuthClientLike, registra chamadas e permite cenários autenticados, anônimos e de erro sem iniciar um IdP:

import {
  createTestAuthClient,
  createTestUser,
} from "@elefantedev/auth-client/testing";

const auth = createTestAuthClient({
  user: createTestUser({
    sub: "user_123",
    email: "[email protected]",
  }),
  callbackRedirectTo: "/conta",
});

const app = createApp(env, auth);
const response = await app.fetch(
  new Request("https://app.elefante.dev/conta"),
);

expect(response.status).toBe(200);
expect(auth.calls.authenticate).toHaveLength(1);

Use user: null para uma sessão anônima. Respostas específicas e falhas tipadas podem ser configuradas por overrides; o fake continua registrando a chamada antes de executar o override. Os resultados públicos nunca incluem access, ID ou refresh tokens.

Desenvolvimento e pacote

Na raiz do repositório do Elefante Auth:

pnpm --filter @elefantedev/auth-client test
pnpm --filter @elefantedev/auth-client typecheck
pnpm --filter @elefantedev/auth-client build
pnpm auth-client:verify
pnpm auth-client:pack

test:package valida o tarball exato como dependência ESM e TypeScript de um projeto externo temporário. O diretório scripts/output é ignorado pelo Git.