Files
adaptador-planilhas/src/server/auth/dev-session.ts
T
camilafagundesandClaude Opus 5 f5560cc4ac Autentica homologação com Supabase real e resolve workspace no servidor
Duas lacunas impediam qualquer uso de homologação. Primeira: sem SSO do Elo
Pro e sem o login local, não havia como entrar. Segunda, mais grave: nada no
repositório gravava o cookie `elo_pro_workspace_id`, que era lido como origem
do workspace ativo — mesmo com sessão válida a resposta era sempre "sem
workspace".

- `identity-provider.ts` define o contrato de identidade. Trocar o Supabase de
  homologação pelo SSO do Elo Pro passa a ser acrescentar uma implementação;
  nenhum outro arquivo conhece o mecanismo.
- `/api/auth/staging/session` autentica com `signInWithPassword` no projeto
  Supabase de homologação. Há senha, emitida e validada pelo Supabase, e o
  Adaptador continua sem tabela de usuário, senha ou sessão própria.
- O login local sem senha existe apenas em `ADAPTER_ENV=development`, e não
  por configuração: fora de development o provedor nem entra na lista.
- `workspace.ts` resolve o workspace por associação real lida sob RLS. O
  cookie passa a ser preferência, não prova: aceitá-lo como autorização
  transformava um valor editável pelo cliente em credencial.
- `/api/auth/workspace` grava a preferência só depois de confirmar a
  associação, e `/selecionar-workspace` existe para quem participa de mais de
  um workspace.
- Rate limiting por usuário e bucket nas rotas do módulo, com Retry-After.
  Upload e processamento custam muito mais que leitura.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-15 20:38:45 -03:00

47 lines
1.9 KiB
TypeScript

import { createHmac, timingSafeEqual } from "node:crypto";
import { adapterEnv } from "@/config/env";
export type DevSession = { userId: string; workspaceId: string; exp: number };
/**
* Sessão local sem senha. Existe apenas em `ADAPTER_ENV=development`.
* Homologação pública usa Supabase Auth real — ver `stagingAuthEnabled`.
*
* A autoridade é `ADAPTER_ENV`, não `NODE_ENV`: a imagem roda sempre com
* `NODE_ENV=production` (build otimizado), inclusive no compose local. E o
* padrão protege o caso perigoso: sem `ADAPTER_ENV` explícito, um processo com
* `NODE_ENV=production` resolve para `production` e esta função devolve false.
*/
export function devAuthEnabled(): boolean {
return (
adapterEnv() === "development" &&
process.env.ADAPTER_DEV_AUTH === "1" &&
Boolean(process.env.ADAPTER_DEV_AUTH_SECRET)
);
}
function secret(): string {
const value = process.env.ADAPTER_DEV_AUTH_SECRET;
if (!value) throw new Error("ADAPTER_DEV_AUTH_SECRET ausente.");
return value;
}
export function signDevSession(userId: string, workspaceId: string, ttlSeconds = 60 * 60 * 8): string {
const body = Buffer.from(JSON.stringify({ userId, workspaceId, exp: Date.now() + ttlSeconds * 1000 })).toString("base64url");
const sig = createHmac("sha256", secret()).update(body).digest("base64url");
return `${body}.${sig}`;
}
export function verifyDevSession(token: string): DevSession | null {
if (!devAuthEnabled()) return null;
const [body, sig] = token.split(".");
if (!body || !sig) return null;
const expected = createHmac("sha256", secret()).update(body).digest("base64url");
const left = Buffer.from(sig);
const right = Buffer.from(expected);
if (left.length !== right.length || !timingSafeEqual(left, right)) return null;
const parsed = JSON.parse(Buffer.from(body, "base64url").toString("utf8")) as DevSession;
if (!parsed.exp || parsed.exp < Date.now()) return null;
return parsed;
}