Ir al contenido

Autenticación API

Las integraciones usan un token ligado a una organización enviado como credencial Bearer: un token API personal para scripts y clientes de la API, o un token de aplicación de la organización para enviar resultados desde CI. La aplicación web sigue usando cookies de sesión y X-Organization-Id para cambiar de organización.

Un token API pertenece al usuario que lo creó, en una organización. Actúa con los permisos que ese usuario tenga en cada momento y se revoca cuando el usuario sale de la organización. Úsalo para scripts y clientes de la API; para enviar resultados de pruebas desde CI, es mejor un token de aplicación, que pertenece a la organización.

Crear y revocar un token exige el permiso Administrar API tokens (api-tokens.manage), que tienen los propietarios, los administradores y los miembros. Un viewer puede leer la tabla de tokens y nada más: crear o revocar devuelve 403 forbidden, y la pantalla no le ofrece ningún control de creación ni de revocación. Mientras la organización exija la autenticación en dos pasos, crear un token API también requiere que tu cuenta tenga una aplicación de autenticación confirmada; si no, la petición responde 403 organization_two_factor_required. Los tokens creados antes siguen funcionando.

  1. Inicia sesión en Probara y abre Tokens de API desde el menú del avatar (/user/api-tokens).
  2. Pulsa Nuevo token en la cabecera de la pantalla para abrir el diálogo de creación, indica un nombre y pulsa Crear token — el token se crea para la organización que tiene el foco en ese momento.
  3. Copia el secreto del panel de revelación única de inmediato — se muestra una sola vez y no se puede recuperar después.
  4. Revisa Tokens activos en la tabla (Creado, Último uso o Nunca) y revoca con el icono de papelera al rotar credenciales.

Formato del token: probara_ seguido de bytes aleatorios en base64url. Guarda los tokens en el almacén de secretos de tu CI, no en el repositorio.

Envía el token en cada llamada protegida a /api/v1/* (excepto rutas de auth solo-sesión y gestión de tokens):

Authorization: Bearer probara_xxxxxxxx

Con un token API válido no necesitas X-Organization-Id. El Worker resuelve la organización y la membresía desde el token.

Ejemplo:

Ventana de terminal
curl -sS -H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://app.probara.net/api/v1/projects"

Un token de aplicación pertenece a la organización, no a una persona. Está ligado a una aplicación del catálogo de Integraciones — JUnit XML, para la CLI de Probara, Playwright, para el reporter de Probara para Playwright, Jest, para el reporter de Probara para Jest, Cypress, para el reporter de Probara para Cypress, Vitest, para el reporter de Probara para Vitest, o Cucumber, para el reporter de Probara para Cucumber y la sincronización Gherkin de la CLI — y es la credencial que conviene usar en CI:

Token APIToken de aplicación
Pertenece aUn usuario, en una organizaciónLa organización, ligado a una aplicación
Se gestiona desdeMenú del avatar → Tokens de APILa tarjeta de la aplicación en Workspace → Integraciones
Crear y revocarapi-tokens.manage (lo tienen propietarios, administradores y miembros)integrations.manage (lo tienen propietarios y administradores)
Secretoprobara_…probara_app_…
Puede llamar aTodo lo que puede su usuario, salvo iniciar una prueba del planSolo las seis llamadas que hace un reporter (abajo)
Cuando su creador se vaSe revocaSigue funcionando
En la actividad aparece comoTú, con «Desde un token de API»La aplicación y el nombre del token, por ejemplo JUnit XML · nightly-ci
PlanCualquieraSolo planes de pago (abajo)

Un token de aplicación no ocupa ningún puesto. Como sobrevive a quien lo creó, rotarlo depende de ti: revócalo y crea otro cuando pueda haberse filtrado, o cuando alguien que podía leer el secreto deje tu equipo.

  1. Como propietario o administrador, abre Workspace → Integraciones y selecciona la tarjeta de la aplicación que lo usará: JUnit XML, Playwright, Jest, Cypress, Vitest o Cucumber.
  2. Abre la pestaña Tokens de acceso y selecciona Crear un token de aplicación.
  3. Escribe un Nombre del token — ponle el nombre de lo que lo usa, como el repositorio o el pipeline — y selecciona Crear token.
  4. Copia el secreto desde Copia tu nuevo token. Empieza por probara_app_ y se muestra una sola vez: guárdalo enseguida como el secreto de CI PROBARA_API_TOKEN.

La pestaña Tokens de acceso lista los tokens activos con quién creó cada uno, cuándo y cuándo se usó por última vez. Revocar (tras una confirmación) lo detiene al momento: la siguiente petición con él responde 401 unauthorized. Los tokens revocados conservan su nombre y su aplicación en la actividad que dejaron.

La tarjeta usa estas rutas. Como el resto de /api/v1/orgs/{orgUlid}/integrations, solo aceptan cookie de sesión; una credencial Bearer se rechaza.

MétodoRutaComportamiento
GET/api/v1/orgs/{orgUlid}/integrations/apps/{app}/tokens{ items: [{ ulid, app, name, createdAt, lastUsedAt, createdBy }] } — los tokens activos, nunca el secreto. createdBy es { ulid, name }, o null cuando esa cuenta se eliminó
POST/api/v1/orgs/{orgUlid}/integrations/apps/{app}/tokensCuerpo { name } (1–100 caracteres). 201 { token, appToken }: token es el secreto, que solo se devuelve aquí
DELETE/api/v1/orgs/{orgUlid}/integrations/apps/{app}/tokens/{tokenUlid}Revoca el token. 204; 404 si ya está revocado o pertenece a otra organización o app

{app} es junit, playwright, jest, cypress, vitest o cucumber; cualquier otro valor responde 404 not_found. Cada aplicación lista, crea y revoca solo sus propios tokens. Las seis necesitan el permiso Administrar integraciones (integrations.manage, que tienen propietarios y administradores); si no, 403 forbidden. Crear necesita además un plan de pago y está limitado a 10 por usuario por hora.

Un token de aplicación puede hacer exactamente las llamadas que hacen la CLI y los reporters de frameworks:

MétodoRutaPara
POST/api/v1/projects/{projectId}/runsCrear una ejecución — solo con automated: true
POST/api/v1/projects/{projectId}/reportsEnviar un reporte
POST/api/v1/projects/{projectId}/gherkin-syncSincronizar los escenarios de archivos Gherkin .feature
POST/api/v1/runs/{runUlid}/results/{resultUlid}/attachments:stagePreparar adjuntos del resultado
PATCH/api/v1/runs/{runUlid}/results/{resultUlid}/attachmentsConfirmarlos — solo añadir
POST/api/v1/runs/{runUlid}/closeCerrar la ejecución
GET/api/v1/runs/{runUlid}/case-keysLeer las claves de casos de una ejecución, para ejecutar solo sus casos

Cualquier otro método o ruta — cualquier otra lectura incluida, y el endpoint MCP /mcp — responde 403 forbidden antes de ejecutar nada. Dentro de esas llamadas:

  • Crear una ejecución sin automated: true responde 403 forbidden: un token de aplicación solo crea ejecuciones automatizadas.
  • Una confirmación solo puede añadir adjuntos. Un cuerpo que deja fuera un adjunto que el resultado ya tiene responde 403 forbidden y no cambia nada, así que reenvía los archivos confirmados como { ulid, position } junto a los nuevos.
  • Actúa con los permisos de ejecutar ejecuciones, escribir casos de prueba y ejecutar resultados, y nada más. No está exento del bloqueo de resultados por asignado de un proyecto (403 run_case_assignee_locked) y, como un token API, no se evalúa contra las listas de acceso de los proyectos.
  • Sus peticiones cuentan para el límite de tasa de la API de la organización, cada resultado que registra cuenta para el límite mensual de resultados de la API, y Idempotency-Key funciona como con cualquier otra credencial.

El envío de resultados desde CI está incluido en los planes de pago. En una organización sin plan de pago:

  • toda petición con un token de aplicación responde 403 forbidden, y crear uno se rechaza igual;
  • POST /api/v1/projects/{projectId}/reports, y POST /api/v1/projects/{projectId}/runs con automated: true, responden 403 forbidden con cualquier credencial — tokens API y sesiones incluidos. Crear una ejecución manual sigue disponible;
  • listar y revocar tokens de aplicación sigue funcionando.

Pasar al plan Free no revoca los tokens de aplicación: Probara los conserva y los rechaza, y vuelven a funcionar en cuanto la organización vuelve a un plan de pago.

Lo que hace un token de aplicación se atribuye al token, y la aplicación web muestra el logotipo y el nombre de la aplicación junto al nombre del token. En las respuestas de la API, la forma api_token del author de una ejecución, de un plan de pruebas o de un caso de prueba, del updatedBy de un caso de prueba y del actor de un evento de auditoría, y el executedByApiToken de un resultado, llevan app — "junit", "playwright", "jest", "cypress", "vitest" o "cucumber" para un token de aplicación, null para un token API — y los elementos de adjuntos de la organización llevan uploaderApp del mismo modo.

El endpoint /mcp también acepta un token de acceso OAuth 2.1 como credencial Bearer — este es el flujo que usan los clientes MCP interactivos (Claude.ai, Claude Code, Cursor, ChatGPT) en lugar de copiar y pegar un token API. Consulta las guías de conexión MCP para los pasos de configuración por cliente.

Cualquier cliente puede descubrir el servidor de autorización y los requisitos de este recurso sin configuración previa:

Documento de metadatosRuta
Metadatos del servidor de autorización (RFC 8414)GET /.well-known/oauth-authorization-server
Metadatos del recurso protegido (RFC 9728)GET /.well-known/oauth-protected-resource

Ambos documentos anuncian solo PKCE S256 (sin plain) y exactamente los scopes de abajo. El documento de metadatos del servidor de autorización también indica authorization_response_iss_parameter_supported y client_id_metadata_document_supported como true.

Los clientes se registran a sí mismos antes de la primera petición de autorización — sin ningún paso manual de “crear una app OAuth”:

POST /oauth/register
Content-Type: application/json
{ "redirect_uris": ["https://client.example.com/callback"] }

Devuelve un client_id y los redirect_uris registrados. El registro tiene límite de tasa y un tope máximo. Un registro rechazado devuelve 429 con la cabecera Retry-After, en segundos. La expiración del cliente tiene dos niveles: un cliente que nunca completa una autorización expira 24 horas después del registro; un cliente que se autorizó al menos una vez expira 60 días después de su última actividad con un token (intercambio o renovación).

Un cliente también puede identificarse con una URL https que controla, en lugar de registrarse. Publica un documento JSON en esa URL y usa la URL como client_id:

{
"client_id": "https://app.example.com/oauth/client.json",
"client_name": "Example MCP Client",
"redirect_uris": ["http://127.0.0.1:3000/callback"],
"token_endpoint_auth_method": "none"
}

El servidor descarga el documento la primera vez que ve la URL, y de nuevo cuando caduca su caché. La caché dura lo que indique Cache-Control: max-age (o Expires), entre 5 minutos y 24 horas. Los requisitos:

  • El client_id del documento es exactamente su URL. La URL usa https y tiene una ruta. No tiene query string, fragmento, credenciales, puerto, dirección IP ni host localhost, y su nombre de host no termina en punto.
  • client_name y redirect_uris son obligatorios. Un redirect_uri de la petición de autorización debe aparecer en la lista.
  • token_endpoint_auth_method es none o está ausente. No se aceptan secretos de cliente.
  • El documento es application/json, de como máximo 5 KiB, y no puede redirigir.

Las peticiones rechazadas devuelven 429 con la cabecera Retry-After, en segundos. Un usuario con sesión puede añadir 10 documentos de cliente nuevos al día, todos los usuarios juntos pueden añadir 20 por dominio registrable al día, y un usuario puede descargar URL desconocidas como máximo 10 veces por host y 30 en total por hora. Un dominio registrable es example.com con sus subdominios; bajo un dominio de alojamiento compartido como github.io, cada sitio (alice.github.io) es un dominio registrable propio.

Cuando el host no responde mientras el servidor descarga o renueva un documento (un tiempo de espera agotado, un error del servidor o un fallo de DNS), un cliente que ya fue autorizado sigue funcionando con su última copia válida hasta 24 horas después de que caducara su caché. Un cliente que nunca fue autorizado, o cuya copia es más antigua que eso, recibe 503 con error igual a temporarily_unavailable y la cabecera Retry-After. Un documento claramente inválido, por ejemplo uno que redirige o indica otro client_id, se rechaza de inmediato con 400, y no se usa ninguna copia en su lugar.

La expiración sigue los dos niveles descritos arriba para los clientes registrados. Un cliente cuyo documento ha expirado se rechaza hasta que la limpieza horaria lo elimine. Después, la misma URL se registra como un cliente nuevo y sus usuarios deben volver a autorizarlo. La pantalla de consentimiento muestra el host que sirvió el documento, para que los usuarios vean de dónde viene la aplicación. El registro mediante /oauth/register sigue funcionando para los clientes que lo prefieran.

  1. El cliente redirige al usuario a GET /oauth/authorize con un code_challenge PKCE (S256), el redirect_uri exacto registrado, el resource con la URL del servidor MCP (https://app.probara.net/mcp, RFC 8707) y un scope opcional — consulta Scopes más abajo para lo que ocurre cuando se omite o no se reconoce. Si se omite resource, se toma esa URL. Cualquier otro recurso, o varios, se rechaza con invalid_target. Las variantes de escritura de esa misma URL (esquema o host en mayúsculas, barra final) se aceptan.
  2. Probara requiere una sesión activa; un visitante no autenticado se envía primero a iniciar sesión y luego vuelve al consentimiento — nunca se emite un código sin sesión.
  3. El usuario elige a qué organización conceder acceso y aprueba o deniega en la pantalla de consentimiento. La aprobación emite un código de autorización vinculado a esa organización, al scope negociado (posiblemente reducido por el rol) y al desafío PKCE. Mientras la organización exija la autenticación en dos pasos, la aprobación también requiere que la cuenta del usuario tenga una aplicación de autenticación confirmada; si no, el consentimiento responde 403 organization_two_factor_required y no se emite ningún código.
  4. El cliente intercambia el código por tokens:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=...&redirect_uri=...&code_verifier=...&client_id=...&resource=https%3A%2F%2Fapp.probara.net%2Fmcp

Envía el mismo resource en esta petición. Si nombra otro recurso, la respuesta es 400 con error: invalid_target, y el código sigue sin usar.

Una vez validados client_id y redirect_uri, cualquier error posterior de autorización (response_type no soportado, PKCE inválido o un resource que no es la URL MCP de este servidor) se entrega como una redirección 302 al redirect_uri registrado con error, error_description y el state reflejado en la query — según RFC 6749 §4.1.2.1 — y no como un error JSON en bruto. Toda redirección al redirect_uri registrado, de éxito o de error, incluye además iss, con el valor del issuer del documento de metadatos (RFC 9207). Verifícalo antes de usar el code. Un client_id desconocido o un redirect_uri no registrado siguen devolviendo un JSON 400 sin redirección (nunca se usa un destino de redirección controlado por un atacante).

ScopeConcede
mcp:readLlamadas de solo lectura (list_*, get_*, etc.)
mcp:writeLlamadas de lectura Y escritura (create_*, update_*, delete_*, etc.)

Los dos documentos de metadatos listan estos scopes en scopes_supported, así un cliente los descubre antes de pedir una concesión.

El scope en /oauth/authorize es opcional (RFC 6749 §3.3) y se negocia de forma flexible, ya que algunos clientes MCP lo omiten o envían valores que este servidor no reconoce:

  • Omitido o vacío → concede el conjunto completo soportado (mcp:read mcp:write).
  • Solo valores no reconocidos (p. ej. scope=claudeai) → también recae en el conjunto completo, igual que el caso omitido.
  • Mezcla de valores conocidos y desconocidos (p. ej. scope=mcp:read claudeai) → concede solo el subconjunto conocido (mcp:read).
  • Un subconjunto válido de los scopes soportados → se concede sin cambios.

El scope negociado es el que se muestra en la pantalla de consentimiento y el que refleja el campo scope de la respuesta del token. El scope efectivo en el momento de la llamada sigue siendo el mínimo entre el scope negociado de la concesión y el rol actual del usuario en esa organización — una degradación de rol después de emitida la concesión aplica de inmediato, sin esperar a que el usuario vuelva a dar su consentimiento.

Cuando una llamada a herramienta necesita un scope que la concesión no tiene, /mcp rechaza la llamada antes de ejecutar la herramienta, con 403 y WWW-Authenticate: Bearer error="insufficient_scope", scope="mcp:write" (o scope="mcp:read"), para que un cliente OAuth pueda volver a pedir ese scope. Cuando la concesión tiene el scope pero tu rol actual no lo permite, la llamada devuelve un error de herramienta forbidden que nombra el scope y tu rol, y no se envía ningún desafío insufficient_scope. Volver a autorizar no cambia esto: el rol limita lo que puede tener una concesión nueva, así que un propietario o administrador de la organización debe cambiar tu rol. Un token API no tiene scopes y nunca se rechaza así.

POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=...&client_id=...&resource=https%3A%2F%2Fapp.probara.net%2Fmcp

Un resource que nombra otro recurso se rechaza con 400 y error: invalid_target, y el refresh token sigue utilizable.

Cada renovación rota el par de tokens: el refresh token anterior deja de funcionar de inmediato. Reutilizar un refresh token ya rotado revoca toda la familia de tokens como precaución — si esto ocurre, el usuario debe volver a dar su consentimiento.

Volver a dar consentimiento no requiere volver a registrar el cliente: mientras el client_id siga dentro de su ventana de expiración de consentimiento (ver Registro dinámico de clientes (DCR) arriba), el MISMO client_id vuelve a GET /oauth/authorize para obtener una nueva concesión. Esa ventana está garantizada para durar más que el tiempo de vida del refresh token, así que un cliente cuyo refresh token acaba de expirar — o cuya familia acaba de ser revocada por detección de reutilización — siempre puede volver a autorizarse sin una nueva llamada de DCR.

Los usuarios gestionan sus propios clientes autorizados en Workspace → Authorized clients — revocar ahí invalida de inmediato todos los tokens de acceso y refresh emitidos a ese cliente para esa organización.

MecanismoQuién lo usaContexto de org
Cookie de sesión (tcms_session / __Host-tcms_session)SPA en navegadorCabecera X-Organization-Id en rutas de dominio
Token API BearerScripts, clientes APIDesde el token; sin cabecera de org
Token de aplicación BearerEnvío de resultados de CIDesde el token; sin cabecera de org

Rutas solo-sesión (Bearer rechazado): POST /api/v1/auth/logout, logout-all, GET /api/v1/auth/me, GET/PATCH /api/v1/profile, POST/DELETE /api/v1/profile/avatar, todo /api/v1/me/api-tokens, PATCH /api/v1/orgs/{orgUlid} (el cambio de nombre de la organización), y las rutas de tokens de aplicación bajo /api/v1/orgs/{orgUlid}/integrations.

MétodoRutaCuerpoRespuesta
GET/api/v1/profile—{ firstName, lastName, positionTitle, avatarKey, avatarVersion }
PATCH/api/v1/profile{ firstName, lastName, positionTitle? }Misma forma que GET; positionTitle vacío se guarda como null
POST/api/v1/profile/avatarmultipart/form-data con un file (PNG/JPEG/WebP, máx. 5 MiB){ avatarKey, avatarVersion }
DELETE/api/v1/profile/avatar—{ avatarKey: null, avatarVersion }

Las fotos se normalizan en el servidor a WebP 256×256 y se guardan en R2. La lectura pública es GET /__assets/{avatarKey}?v={avatarVersion} (sin sesión). avatarKey es null si no hay foto.

firstName y lastName son obligatorios y no vacíos en PATCH. No hace falta la cabecera X-Organization-Id.

POST /api/v1/auth/signup exige firstName y lastName (recortados, no vacíos) además de email y password. invitationToken opcional sin cambios. CREATE y REPAIR persisten los nombres en users; positionTitle solo se define después con PATCH /api/v1/profile.

Con un token válido puedes listar los eventos de auditoría de la organización (quién cambió qué) con el mismo Bearer:

GET /api/v1/orgs/{orgUlid}/audit-events — consulta API de eventos de auditoría para paginación por cursor y campos de respuesta. Requiere el permiso de lectura de auditoría (que tienen owner/admin).

EstadoCódigoSignificado
401unauthorizedCookie o Bearer inválidos
403forbiddenUn token de aplicación llamó a algo fuera de sus llamadas permitidas, o la organización no tiene un plan de pago para el envío desde CI
403email_not_verifiedEl usuario todavía no verificó su dirección de correo. Se devuelve al crear tokens de API, aprobar un cliente MCP, invitar miembros, aceptar una invitación por correo, conectar Google o activar la autenticación en dos pasos.
403organization_two_factor_requiredLa organización exige la autenticación en dos pasos y tu cuenta no tiene una aplicación de autenticación confirmada. Se devuelve al crear tokens de API o aprobar un cliente MCP para esa organización.
429too_many_requestsLímite de creación de tokens (10 por usuario por hora, igual para tokens API y tokens de aplicación)

Crear tokens API está limitado a 10 peticiones por usuario por hora, y crear tokens de aplicación tiene un presupuesto propio del mismo tamaño. POST /api/v1/auth/signup aplica dos dimensiones de rate-limit: 5 por hora por IP y 3 por hora por email. Las peticiones que excedan cualquiera de los dos límites devuelven 429 too_many_requests.

POST /api/v1/auth/login tiene un límite de 5 por minuto por IP y 10 por hora por email. Un navegador que ya inició sesión en esa dirección conserva su propio presupuesto de 10 por hora para ella, así que los intentos fallidos desde otros lugares no pueden bloquear ese navegador. Las peticiones que excedan el límite devuelven 429 too_many_requests.