La primera vez que construyes una integración con GitHub, pasarás una hora intentando averiguar si quieres una GitHub App o una OAuth App. Los docs las tratan como opciones equivalentes. No lo son. Solo la historia de los webhooks suele bastar para decidir por ti.
El resumen muy corto
- GitHub App: instalada en cuentas o repositorios, tiene permisos de grano fino, recibe webhooks de los recursos donde está instalada, se autentica como ella misma con tokens de corta duración.
- OAuth App: los usuarios la autorizan, actúa en su nombre con sus permisos, recibe webhooks solo si los configura como cualquier herramienta de terceros, se autentica como el usuario con tokens de larga duración.
Si construyes algo que opera sin un usuario activo — un bot de CI, una automatización de revisión de código, cualquier cosa programada — quieres una GitHub App. Si construyes una herramienta que hace acciones en nombre de un usuario logueado (como una UI de búsqueda de código), una OAuth App es la forma correcta.
Cómo reciben webhooks
GitHub Apps
Configuras una URL de webhook al crear la app. Cuando un usuario instala la app en una cuenta o repositorio, GitHub empieza a enviar eventos de ese alcance a tu URL. Los eventos incluyen installation (el ciclo de vida de instalación/desinstalación), installation_repositories (repos añadidos o quitados) y los tipos de evento a los que tu app se suscribió (push, pull_request, etc.).
Cada evento incluye una cabecera X-GitHub-Hook-Installation-Target-Type («integration») y un installation ID que puedes usar para acuñar un token de esa instalación concreta. La firma es HMAC SHA-256 en X-Hub-Signature-256 — la misma forma que los webhooks de repositorio.
OAuth Apps
Las OAuth Apps no tienen una suscripción de webhook incorporada. Para recibir webhooks, los usuarios autorizados de la app tienen que crear webhooks de repositorio u organización que apunten a la URL de la app. Esto significa que el alcance de webhooks de la app está atado a dónde sus usuarios han configurado suscripciones, no a dónde la app está «instalada».
Algunos equipos construyen OAuth Apps que crean automáticamente webhooks vía la API de GitHub tras la autorización. Funciona, pero ahora gestionas el ciclo de vida del webhook por usuario junto con la lógica propia de la app.
La autenticación es la verdadera divergencia
Las GitHub Apps se autentican usando peticiones firmadas con JWT para acuñar tokens de instalación de corta duración (1 hora) por cada instalación. El JWT se firma con una clave privada que generas al crear la app. Tu código:
// 1. Firma un JWT con la clave privada de tu app (expira en 10 min)
const jwt = createAppJwt(APP_ID, PRIVATE_KEY);
// 2. Cambia el JWT por un token de instalación (válido 1 hora)
const token = await fetchInstallationToken(jwt, INSTALLATION_ID);
// 3. Haz llamadas a la API con ese token
const res = await fetch('https://api.github.com/repos/x/y/issues', {
headers: { Authorization: `token ${token}` },
});
Los tokens de instalación están acotados a la instalación y expiran automáticamente — así que una fuga tiene un radio de impacto limitado.
Las OAuth Apps se autentican con tokens de acceso de usuario obtenidos vía el flujo OAuth estándar. Esos tokens son de larga duración por defecto y actúan como el usuario — filtrar uno significa que un atacante puede hacer todo lo que ese usuario podría. Los docs de GitHub te empujan hacia las GitHub Apps para nuevas integraciones, en parte por esto.
Permisos: acotados vs amplios
Las GitHub Apps te permiten pedir permisos granulares: leer issues, escribir checks, leer pull requests, sin acceso a nada más. Cada permiso es independiente. El usuario ve la lista exacta y puede rechazar.
Las OAuth Apps usan el sistema más antiguo basado en scopes: repo, read:user, etc. Los scopes son más gruesos. El scope repo concede acceso de lectura y escritura a todos los repositorios que el usuario puede ver — no hay una versión de «solo lectura en repos específicos».
Para un bot de revisión de código que solo necesita leer código y escribir check runs, una GitHub App con dos permisos específicos es mucho menos invasiva que una OAuth App pidiendo acceso completo de repo.
Pruebas en local con ambas
La mecánica de la firma del webhook es idéntica — mira pruebas de webhooks de GitHub en local para la configuración. La diferencia es cómo registras la URL.
- GitHub App: pon la URL del webhook en la página de ajustes de tu app. Instala la app en un repositorio de prueba. Dispara eventos. Listo.
- OAuth App: la app en sí no tiene webhooks. Añade un webhook de repositorio (manualmente en los ajustes del repo, o programáticamente vía la API) que apunte a tu URL de túnel.
Para las OAuth Apps, también necesitas probar el propio flujo de callback OAuth — mira cómo probar callbacks OAuth en local.
Migrar entre ellas es doloroso
Si empiezas con una OAuth App y luego te das cuenta de que necesitas una GitHub App, no puedes migrar. Los usuarios tienen que reautorizar la nueva app, tú tienes que reemitir cualquier token almacenado, y cualquier webhook de repositorio que configuraste vía la OAuth App seguirá disparándose hasta que los borres. Dedica diez minutos al principio a elegir el tipo correcto.
Cuándo elegir cuál
Elige una GitHub App cuando:
- Tu integración corre en su propio horario o en respuesta a eventos, sin un usuario logueado.
- Quieres permisos acotados en repositorios u organizaciones específicos.
- Quieres webhooks atados al alcance de instalación, no configurados por repo.
- Construyes algo que acabará listado en el GitHub Marketplace.
Elige una OAuth App cuando:
- Tu herramienta es una UI en la que los usuarios inician sesión y hacen cosas en su propia cuenta de GitHub.
- Necesitas actuar exactamente como el usuario, incluidos sus patrones de acceso.
- No necesitas webhooks, o los configurarás manualmente por repo.
Para los detalles subyacentes de la verificación de firma, mira la guía de verificación de firmas. Únete a la lista de espera de PortPreview para un túnel que maneja el timing de webhooks de GitHub y la captura para reenvío.