forja

Login de usuarios de tu app

Forja guarda las cuentas de las personas que usan tu app (correo y contraseña), les da una sesión y hace que cada una vea solo sus propios registros. Vos armás las pantallas; Forja se ocupa de las contraseñas, las sesiones y los mails.

No confundir. Esto es el login de las personas que usan tu app (tus clientes). El login de quien administra el proyecto en el dashboard de Forja es otra cosa, y las llaves de API (Llaves y permisos) siguen siendo para tu servidor.

Cómo se conecta todo

  1. Tu app muestra un formulario de registro o de ingreso y llama a Forja con el correo y la contraseña.
  2. Forja devuelve una sesión: un accessToken corto (15 minutos) y un refreshToken largo (30 días).
  3. Tu app manda el accessToken en cada pedido a la API de tablas. Forja sabe quién es la persona y le muestra solo lo que las reglas de la tabla permiten.

El navegador de tu usuario habla directo con Forja: no hace falta una llave de API ni un servidor propio.

Antes de empezar: configurar el proyecto

  1. En el dashboard, abrí Usuarios de la app y encendé el login. Ahí también decidís si se pueden crear cuentas nuevas y si hay que confirmar el correo.
  2. Cargá los sitios autorizados: las direcciones desde donde se usa tu app (por ejemplo https://miapp.com), o la palabra localhost para probar en tu computadora. Un navegador desde otro sitio recibe un 403.
  3. Si pedís confirmar el correo, indicá a qué página de tu app lleva el enlace del mail (por ejemplohttps://miapp.com/confirmar), y lo mismo para restablecer la contraseña. Tienen que ser de un sitio autorizado.
  4. Para que las personas lean o guarden datos, activá la API automática de la tabla y definí sus reglas de acceso (más abajo). Sin reglas, la tabla es invisible para ellas.

Registro e ingreso

Crear una cuenta
POST https://api.getforja.site/v1/auth/<ID_DEL_PROYECTO>/signup
Content-Type: application/json

{ "email": "ana@ejemplo.com", "password": "una-clave-larga-1234", "metadata": { "nombre": "Ana" } }

Si el proyecto no pide confirmar el correo, la respuesta ya es una sesión:

Respuesta con sesión
{
  "accessToken": "eyJ…",
  "tokenType": "bearer",
  "expiresIn": 900,
  "expiresAt": "2026-10-31T12:15:00.000Z",
  "refreshToken": "q1w2e3…",
  "user": { "id": "…", "email": "ana@ejemplo.com", "emailVerified": false, "metadata": { "nombre": "Ana" }, "createdAt": "…" }
}

Si sí lo pide, la cuenta se crea sin sesión y se manda un mail con el enlace:

Respuesta cuando hay que confirmar
{ "verificationRequired": true }

El campo metadata es opcional: un objeto JSON con datos de la persona (nombre, preferencias…), de hasta 4096 bytes. El ingreso es igual, con POST …/login y los mismos dos campos.

Ejemplo en JavaScript
const API = "https://api.getforja.site";
const PROYECTO = "<ID_DEL_PROYECTO>";

async function auth(ruta, cuerpo, accessToken) {
  const res = await fetch(API + "/v1/auth/" + PROYECTO + "/" + ruta, {
    method: cuerpo === undefined && accessToken ? "GET" : "POST",
    headers: {
      ...(cuerpo !== undefined ? { "Content-Type": "application/json" } : {}),
      ...(accessToken ? { Authorization: "Bearer " + accessToken } : {}),
    },
    body: cuerpo !== undefined ? JSON.stringify(cuerpo) : undefined,
  });
  const datos = res.status === 204 ? null : await res.json();
  if (!res.ok) throw Object.assign(new Error(datos.error), { code: datos.code, status: res.status });
  return datos;
}

// Registro
const alta = await auth("signup", { email, password });
if (alta.verificationRequired) {
  // Mostrá "Revisá tu correo": todavía no hay sesión.
} else {
  guardarSesion(alta); // ya entró
}

// Ingreso
const sesion = await auth("login", { email, password });
guardarSesion(sesion);

// Datos de la persona que inició sesión
const yo = await auth("me", undefined, sesion.accessToken);

Confirmar el correo y recuperar la contraseña

Los mails los manda Forja desde no-responder@getforja.site, en español y con el nombre de tu proyecto. El botón lleva a la página de tu app que configuraste, con un ?token=… en la dirección. Esa página lee el código y se lo manda a Forja:

Las dos páginas que tenés que armar
// Página /confirmar de tu app (la que cargaste en "Enlace de verificación")
const token = new URLSearchParams(location.search).get("token");
await auth("verify-email", { token });
// Mostrá "Correo confirmado" y llevá a la persona a iniciar sesión.

// Página /nueva-clave de tu app (la que cargaste en "Enlace de restablecer contraseña")
const token = new URLSearchParams(location.search).get("token");
await auth("reset-password", { token, password: nuevaClave });
  • El enlace de confirmación vence a las 24 horas; el de contraseña, a la 1 hora. Los dos sirven una sola vez.
  • Confirmar el correo no inicia sesión: la persona entra después con su correo y contraseña. Es a propósito, para que nadie deje una sesión abierta en la cuenta de otro.
  • “Olvidé mi contraseña” responde lo mismo exista o no la cuenta, para no revelar quién está registrado. Cambiar la contraseña cierra todas las sesiones abiertas.
  • Si el enlace venció, POST …/resend-verification o POST …/forgot-password mandan uno nuevo.

Mantener la sesión

Renovar la sesión
// Cuando una llamada devuelve 401, o antes de que venza expiresAt:
const nueva = await auth("refresh", { refreshToken: sesion.refreshToken });
guardarSesion(nueva); // el refreshToken anterior ya no sirve: guardá el nuevo
  • Cada renovación entrega un refreshToken nuevo y deja sin efecto el anterior.
  • Si alguien usa un refreshToken que ya se usó (por ejemplo, porque lo copiaron), Forja cierra toda esa sesión y la persona tiene que volver a ingresar.
  • POST …/logout con el refreshToken cierra esa sesión; POST …/logout-all (con el accessToken) cierra todas y deja sin efecto los accessToken vigentes al instante.
  • Guardá el refreshToken con cuidado: quien lo tenga puede entrar como esa persona durante 30 días. Cualquier dato guardado en el navegador puede leerlo un script malicioso de tu página, así que no cargues código de terceros que no controles.

Que cada persona vea solo lo suyo

En la tabla creás una columna que guarda a quién pertenece cada registro (texto o UUID) y, en Acceso de los usuarios de tu app, elegís qué puede hacer la persona que inició sesión:

ReglaOpciones
Versolo sus registros, todos los de la tabla, o ninguno
Crearsí (queda a su nombre) o no
Editarsolo los suyos, o no
Borrarsolo los suyos, o no
Con el accessToken de la persona
GET https://api.getforja.site/v1/tables/<ID_DEL_PROYECTO>/notas
Authorization: Bearer <accessToken de la persona>

POST https://api.getforja.site/v1/tables/<ID_DEL_PROYECTO>/notas
Authorization: Bearer <accessToken de la persona>
Content-Type: application/json

{ "texto": "Comprar pan" }
  • El dueño se completa solo con el id de la persona al crear. No hace falta mandarlo, y si intenta mandar el de otra persona, Forja lo rechaza (403).
  • El dueño de un registro no se puede cambiar. Un registro ajeno responde 404, igual que uno que no existe.
  • El filtro por dueño se aplica dentro de la consulta, así que no hay manera de saltearlo con parámetros de la URL (filter[…] no puede pedir los registros de otra persona).
  • Una tabla sin reglas no existe para ellas: responde 404.

Cuidado con “todos los registros”. Si dejás que las personas vean todos los registros y además permitís que cualquiera se registre, la tabla queda pública para quien cree una cuenta. El dashboard te lo advierte; usalo solo para datos que de verdad pueda ver todo el mundo.

Referencia de rutas

Todas cuelgan de https://api.getforja.site/v1/auth/<ID_DEL_PROYECTO>.

RutaQué hace
POST /signupCrea la cuenta. Devuelve una sesión, o verificationRequired si hay que confirmar el correo.
POST /loginInicia sesión con correo y contraseña.
POST /refreshCambia un refreshToken por una sesión nueva.
POST /logoutCierra la sesión del refreshToken enviado (responde 204).
POST /logout-allCierra todas las sesiones de la persona (con su accessToken; responde 204).
GET /meLos datos de la persona que inició sesión.
POST /change-passwordCambia la contraseña con currentPassword y newPassword; devuelve una sesión nueva.
POST /verify-emailConfirma el correo con el token del mail.
POST /resend-verificationManda otro enlace de confirmación (responde 202 exista o no la cuenta).
POST /forgot-passwordManda el enlace para elegir una contraseña nueva (responde 202 exista o no la cuenta).
POST /reset-passwordElige una contraseña nueva con el token del mail.

Errores

Los errores traen un texto en español en error y, cuando tu app tiene que decidir algo, un code estable.

Código HTTPcodeCuándo
400WEAK_PASSWORD, INVALID_METADATA, INVALID_TOKENContraseña que no sirve, metadata inválida, o enlace de mail vencido o ya usado.
401INVALID_CREDENTIALS, UNAUTHENTICATEDCorreo o contraseña incorrectos (siempre el mismo mensaje), o falta iniciar sesión.
403EMAIL_NOT_VERIFIED, ACCOUNT_DISABLED, SIGNUPS_DISABLED, ORIGIN_NOT_ALLOWEDFalta confirmar el correo, cuenta deshabilitada, altas cerradas, o el sitio no está autorizado.
404AUTH_NOT_ENABLEDEl proyecto no existe o no tiene el login encendido.
409EMAIL_TAKENYa hay una cuenta con ese correo.
429RATE_LIMITED, ACCOUNT_LOCKED, EMAIL_LIMIT_REACHEDDemasiados intentos, cuenta bloqueada unos minutos, o se llegó al tope diario de mails.

Límites y protecciones

  • La contraseña tiene entre 8 y 128 caracteres, y no puede ser igual al correo ni un solo carácter repetido.
  • Después de 10 intentos fallidos la cuenta se bloquea 15 minutos.
  • Los pedidos que mandan un mail (alta, reenviar confirmación, olvidé mi contraseña) están limitados a 3 cada 10 minutos por correo.
  • Tu plan define cuántas cuentas puede tener cada proyecto y cuántos mails de login (confirmación y recuperación) se mandan por día: Free, hasta 500 cuentas y 20 mails por día; Starter, 10.000 cuentas y 300 mails; Growth, 50.000 cuentas y 1.500 mails; Enterprise, a medida. Los precios están en Precios. Los usuarios de tu app nunca ven nada de tu plan: solo reciben un mensaje genérico.
  • Las contraseñas se guardan con scrypt y nunca se pueden leer, ni siquiera vos.

Qué no incluye todavía

  • Ingreso con Google u otros proveedores para tus usuarios (hoy es solo correo y contraseña).
  • Verificación en dos pasos y enlaces mágicos.
  • Usar la sesión de una persona en las funciones, en pagos o en el servidor MCP: esas siguen usando llaves de API.
  • Datos de staging: las cuentas de las personas son solo de producción.
  • Roles de usuario (administrador, editor…), mails con tu propio remitente o diseño, e importar cuentas de otro sistema.
  • Exportar las cuentas: la exportación del proyecto trae tus tablas y funciones, pero no los correos ni las contraseñas de tus usuarios. Si algún día migrás, tendrán que volver a registrarse o crear una contraseña nueva.

Esta lista y otras están en Qué no hace todavía.

Información revisada el 2026-10-31.