Introducción: ¿por qué se necesitan OAuth 2.0 y JWT en PHP?
Las aplicaciones web modernas rara vez funcionan de forma aislada. Los usuarios esperan un inicio de sesión único a través de Google, Facebook o GitHub, y los desarrolladores esperan un intercambio seguro de datos entre microservicios. Dos protocolos clave — OAuth 2.0 (delegación de acceso) y JWT (formato de token compacto) — se han convertido en el estándar de facto. En este artículo, analizaremos cómo implementar la autorización OAuth 2.0 y JWT en PHP desde cero, utilizando solo las capacidades integradas del lenguaje y bibliotecas populares. Obtendrá ejemplos de código listos para usar para la integración con proveedores externos y la creación de su propio sistema de autenticación.
1. Fundamentos de OAuth 2.0: flujos y roles
OAuth 2.0 es un protocolo de autorización que permite a aplicaciones de terceros obtener acceso limitado a los recursos del usuario sin transmitir la contraseña. En él participan cuatro roles:
- Resource Owner — el usuario propietario de los datos.
- Client — su aplicación PHP que solicita acceso.
- Authorization Server — el servidor que emite tokens (por ejemplo, Google).
- Resource Server — la API que proporciona datos mediante el token.
El flujo más popular para aplicaciones web es Authorization Code Grant. Es seguro porque el token se transmite a través de la parte del servidor, no a través del navegador. Veamos la implementación en PHP.
1.1. Registro de la aplicación y obtención de Client ID/Secret
Antes de escribir código, registre la aplicación en el proveedor (por ejemplo, en Google Cloud Console). Obtendrá client_id y client_secret, y configurará el redirect URI (por ejemplo, https://sursitio.es/callback.php).
1.2. Implementación del flujo Authorization Code en PHP
Crearemos dos archivos: login.php (redirección al servidor de autorización) y callback.php (procesamiento de la respuesta).
Archivo login.php
<?phpsession_start();
$client_id = 'SU_CLIENT_ID';$redirect_uri = 'https://sursitio.es/callback.php';$state = bin2hex(random_bytes(16)); // protección contra CSRF$_SESSION['oauth_state'] = $state;
$params = http_build_query([ 'client_id' => $client_id, 'redirect_uri' => $redirect_uri, 'response_type' => 'code', 'scope' => 'openid email profile', 'state' => $state,]);
header('Location: https://accounts.google.com/o/oauth2/v2/auth?' . $params);exit;?>Archivo callback.php
<?phpsession_start();
// Verificación de state (CSRF)if ($_GET['state'] !== $_SESSION['oauth_state']) { die('Error: state no válido');}
$client_id = 'SU_CLIENT_ID';$client_secret = 'SU_CLIENT_SECRET';$redirect_uri = 'https://sursitio.es/callback.php';
// Intercambio de código por token$ch = curl_init('https://oauth2.googleapis.com/token');curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 'code' => $_GET['code'], 'client_id' => $client_id, 'client_secret' => $client_secret, 'redirect_uri' => $redirect_uri, 'grant_type' => 'authorization_code',]));curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$response = curl_exec($ch);curl_close($ch);
$data = json_decode($response, true);$access_token = $data['access_token'] ?? null;
// Obtención del perfil del usuario$ch = curl_init('https://www.googleapis.com/oauth2/v2/userinfo');curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . $access_token]);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$userInfo = json_decode(curl_exec($ch), true);curl_close($ch);
echo 'Hola, ' . htmlspecialchars($userInfo['email']);?>Importante: Siempre verifique el parámetro state. Protege contra la falsificación de solicitudes (CSRF).