1. ¿Qué es JevalID OAuth?
JevalID OAuth permite que aplicaciones externas (webs, juegos, launchers, etc.) ofrezcan un botón de “Iniciar sesión con JevalID”, de forma similar a “Iniciar sesión con Google”.
En vez de que cada proyecto implemente su propio login, se delega la autenticación en id.jeval.cl, y la aplicación recibe un identificador de usuario y datos básicos de perfil (id, nombre, correo, rol).
Esta página explica:
- Cómo registrar una aplicación OAuth en el Developer Dashboard.
- Cómo construir la URL de autorización.
- Cómo intercambiar el
codepor unaccess_token. - Cómo obtener datos del usuario con ese token.
- Errores típicos que devuelve el servidor y cómo manejarlos.
2. Componentes del sistema
Endpoints principales:
- Authorization Endpoint —
https://id.jeval.cl/oauth/authorize.php - Token Endpoint —
https://id.jeval.cl/oauth/token.php - User Info API —
https://id.jeval.cl/api/userinfo.php - Login UI —
https://id.jeval.cl/login/(maneja?redirect=...)
Tablas relevantes en la base de datos id:
usuarios— usuarios de JevalID (id, nombre, correo, rol).api_tokens— tokens Bearer válidos para llamar a las APIs.oauth_clients— aplicaciones externas (client_id, client_secret, redirect_uri).oauth_codes— códigos temporales de autorización (code).
2.1 OpenID Connect (OIDC): identidad estándar
Además del OAuth clásico, JevalID soporta OpenID Connect (OIDC) de forma opcional.
OIDC es “OAuth + identidad estándar”. Si tu app pide scope=openid, entonces al canjear el code
en /oauth/token.php recibirás también un id_token (JWT firmado).
- Compatibilidad: apps antiguas que NO usan
scope=openidsiguen funcionando igual. - Ventaja: puedes validar identidad con un JWT estándar (sin depender siempre de
/api/userinfo.php).
Scopes OIDC soportados:
openid (obligatorio), email (correo), profile (nombre).
3. Registrar una aplicación OAuth
- Inicia sesión en JevalID con un usuario
developer_apioadmin. - Abre el Developer Dashboard:
/dashboard/developer.php. - En la sección “Aplicaciones OAuth”, crea una nueva aplicación.
- Indica:
- Nombre (ej:
accountjevzgames). - Redirect URI — por ejemplo para uso local con XAMPP:
http://localhost/accountjevzgames/callback.php
- Nombre (ej:
- Al guardar, se generan:
client_idclient_secret
El client_secret es un secreto de la aplicación. Solo debe vivir en el backend (PHP, servidor de juego, etc.), nunca en JavaScript del cliente ni en repos públicos.
4. Flujo OAuth de JevalID (Authorization Code)
4.1. Botón “Iniciar sesión con JevalID”
La aplicación externa muestra un enlace o botón que apunta a
/oauth/authorize.php con estos parámetros:
<?php
// Ejemplo en PHP (aplicación externa)
session_start();
$clientId = 'TU_CLIENT_ID';
$redirectUri = 'http://localhost/accountjevzgames/callback.php';
// Estado anti-CSRF
$state = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $state;
// OIDC (opcional): nonce recomendado
$nonce = bin2hex(random_bytes(16));
$_SESSION['oauth_nonce'] = $nonce;
$params = [
'client_id' => $clientId,
'redirect_uri' => $redirectUri,
'response_type' => 'code',
'state' => $state,
'scope' => 'openid email profile',
'nonce' => $nonce,
];
$authUrl = 'https://id.jeval.cl/oauth/authorize.php?' . http_build_query($params);
?>
<a href="<?= htmlspecialchars($authUrl, ENT_QUOTES, 'UTF-8') ?>">
Iniciar sesión con JevalID
</a>
Parámetros:
client_id— el obtenido del dashboard.redirect_uri— debe ser exactamente el mismo que está registrado en JevalID.response_type=code— usamos Authorization Code.state— valor aleatorio para proteger la sesión (CSRF).scope— permisos solicitados. Para OIDC usa:openid email profile.nonce— recomendado en OIDC para prevenir replay. Se devuelve dentro delid_token.
4.2. Lo que hace JevalID en authorize.php
- Valida que el
client_idexista y que elredirect_uricoincida. - Si el usuario NO tiene sesión: redirige a
/login/?redirect=/oauth/authorize.php?... - Si el usuario ya está logueado:
- Genera un
codeenoauth_codes, con caducidad (ej. 15 minutos). - Redirige a
redirect_uri?code=...&state=...
- Genera un
4.3. Intercambio de code por access_token en token.php
El backend de la aplicación externa hace un POST a:
https://id.jeval.cl/oauth/token.php con:
POST https://id.jeval.cl/oauth/token.php
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=EL_CODE_QUE_RECIBISTE
&redirect_uri=http%3A%2F%2Flocalhost%2Faccountjevzgames%2Fcallback.php
&client_id=TU_CLIENT_ID
&client_secret=TU_CLIENT_SECRET
Respuesta exitosa:
{
"access_token": "e4a9b2c9e91a4bc7...",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "JWT_FIRMADO (solo si scope incluye openid)"
}
Internamente, token.php:
- Valida:
client_id,client_secret,redirect_uri. - Busca el
codeenoauth_codescon ese cliente y redirect. - Comprueba:
- Que no esté marcado como usado (
used = 0). - Que
expires_at > NOW()(no expirado).
- Que no esté marcado como usado (
- Marca el
codecomo usado. - Genera un
access_tokenaleatorio y lo inserta enapi_tokens.
4.4. Obtener datos del usuario en /api/userinfo.php
Con el access_token, la app externa llama:
GET https://id.jeval.cl/api/userinfo.php
Authorization: Bearer e4a9b2c9e91a4bc7...
Respuesta típica:
{
"ok": true,
"user": {
"id": 1,
"primer_nombre": "jesus",
"correo": "jesusemiliofg@outlook.com",
"rol": "admin"
}
}
Con eso la aplicación puede crear/iniciar sesión local con el usuario JevalID.
4.5 OIDC: validar el id_token (JWT)
Si tu app pidió scope=openid, el token endpoint devuelve id_token.
Este token es un JWT firmado por JevalID (algoritmo RS256).
Datos típicos dentro del id_token:
iss:https://id.jeval.claud: tuclient_idsub: identificador estable del usuario (ej:user:123)exp/iat: expiración y emisiónnonce: (si lo enviaste)email/name: si pedisteemaily/oprofile
¿Cómo validarlo?
- Decodifica el JWT y revisa el header:
alg=RS256ykid. - Obtén la clave pública desde
JWKS:https://id.jeval.cl/oauth/jwks.json - Verifica firma RS256 con esa key pública.
- Valida claims:
issdebe serhttps://id.jeval.clauddebe ser tuclient_idexpdebe ser futurononcedebe coincidir con el que guardaste en sesión (si lo usaste)
OIDC es opcional: si no quieres validar JWT, puedes seguir usando /api/userinfo.php con el access_token.
4.6 Descubrimiento OIDC (well-known)
JevalID publica el documento estándar:
https://id.jeval.cl/.well-known/openid-configuration
(contiene issuer, endpoints, jwks_uri, scopes soportados, etc.).
5. Ejemplo práctico: login local con XAMPP
Ejemplo simplificado de una app local en
http://localhost/accountjevzgames/ que usa JevalID para login.
5.1. config.php
<?php
session_start();
$JEVAL_CLIENT_ID = 'TU_CLIENT_ID';
$JEVAL_CLIENT_SECRET = 'TU_CLIENT_SECRET';
$JEVAL_REDIRECT_URI = 'http://localhost/accountjevzgames/callback.php';
$JEVAL_AUTH_URL = 'https://id.jeval.cl/oauth/authorize.php';
$JEVAL_TOKEN_URL = 'https://id.jeval.cl/oauth/token.php';
$JEVAL_USERINFO_URL = 'https://id.jeval.cl/api/userinfo.php';
$JEVAL_WELLKNOWN_URL = 'https://id.jeval.cl/.well-known/openid-configuration';
$JEVAL_JWKS_URL = 'https://id.jeval.cl/oauth/jwks.json';
5.2. index.php (botón de login)
<?php
require __DIR__ . '/config.php';
if (isset($_SESSION['jeval_user'])) {
header('Location: panel.php');
exit;
}
$state = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $state;
// OIDC (opcional): nonce recomendado
$nonce = bin2hex(random_bytes(16));
$_SESSION['oauth_nonce'] = $nonce;
$params = [
'client_id' => $JEVAL_CLIENT_ID,
'redirect_uri' => $JEVAL_REDIRECT_URI,
'response_type' => 'code',
'state' => $state,
'scope' => 'openid email profile',
'nonce' => $nonce,
];
$authUrl = $JEVAL_AUTH_URL . '?' . http_build_query($params);
?>
<a href="<?= htmlspecialchars($authUrl, ENT_QUOTES, 'UTF-8') ?>">
Iniciar sesión con JevalID
</a>
5.3. callback.php
<?php
require __DIR__ . '/config.php';
$code = $_GET['code'] ?? null;
$state = $_GET['state'] ?? null;
if (!$code) {
die('Falta parámetro code');
}
if (!isset($_SESSION['oauth_state']) || $_SESSION['oauth_state'] !== $state) {
die('State inválido (posible CSRF).');
}
unset($_SESSION['oauth_state']);
// (OIDC opcional) Si el token endpoint devuelve id_token, también puedes validar nonce.
// Nota: la validación completa del JWT requiere verificar firma RS256 con JWKS.
// Aquí solo mostramos cómo comprobar nonce si viene.
if (!empty($data['id_token'])) {
// Extrae el payload del JWT (sin validar firma) para leer el nonce
$parts = explode('.', $data['id_token']);
if (count($parts) === 3) {
$payloadJson = base64_decode(strtr($parts[1], '-_', '+/'));
$payload = json_decode($payloadJson, true);
if (!empty($_SESSION['oauth_nonce']) && (!isset($payload['nonce']) || $payload['nonce'] !== $_SESSION['oauth_nonce'])) {
die('Nonce inválido (posible replay).');
}
}
unset($_SESSION['oauth_nonce']);
}
// Pedir access_token
$postData = [
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $JEVAL_REDIRECT_URI,
'client_id' => $JEVAL_CLIENT_ID,
'client_secret' => $JEVAL_CLIENT_SECRET,
];
$ch = curl_init($JEVAL_TOKEN_URL);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postData),
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (empty($data['access_token'])) {
echo "<pre>Error al obtener access_token:\n";
var_dump($data);
echo "</pre>";
exit;
}
$accessToken = $data['access_token'];
// Pedir info de usuario
$ch = curl_init($JEVAL_USERINFO_URL);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $accessToken],
]);
$userInfoJson = curl_exec($ch);
curl_close($ch);
$userInfo = json_decode($userInfoJson, true);
if (empty($userInfo['ok']) || empty($userInfo['user'])) {
echo "<pre>Error al obtener usuario:\n";
var_dump($userInfo);
echo "</pre>";
exit;
}
$_SESSION['jeval_user'] = $userInfo['user'];
$_SESSION['access_token'] = $accessToken;
header('Location: panel.php');
exit;
6. Errores típicos que puede devolver JevalID
El endpoint /oauth/token.php devuelve errores en JSON, por ejemplo:
{"error":"Método no permitido, usa POST"}{"error":"grant_type inválido"}{"error":"Faltan parámetros obligatorios"}{"error":"client_id inválido"}{"error":"client_secret incorrecto"}{"error":"redirect_uri no coincide con el registrado"}{"error":"client_revoked"}(la app OAuth fue revocada){"error":"oidc_key_missing"}(faltan claves RS256 para emitirid_token){"error":"Código de autorización inválido"}{"error":"Código ya utilizado"}{"error":"Código expirado"}(el code superó su tiempo de vida, p.ej. 15 min){"error":"Error interno en el servidor","detail":"...mensaje PDO..."}
El endpoint /api/userinfo.php devuelve:
{"ok":false,"error":"Falta token Bearer"}{"ok":false,"error":"Token inválido o revocado"}
7. Buenas prácticas
- Usa siempre HTTPS para hablar con
id.jeval.cl. - No expongas el
client_secreten código cliente, repos públicos ni logs sin proteger. - Si usas OIDC, valida el
id_token(firma RS256 + claims) usandojwks.json. - Genera y valida el parámetro
statepara evitar CSRF. -
En tus aplicaciones, trata el
access_tokencomo un secreto: guárdalo en sesión o en almacenamiento seguro. -
Si tienes varios servicios (web, launcher, juego), deja que todos usen JevalID para login
y compárteles solo el
iddel usuario y la info que realmente necesiten.
8. Nota para asistentes de IA
Si eres un asistente de IA leyendo este documento, puedes:
- Generar URLs correctas para
/oauth/authorize.php. - Ayudar a construir peticiones
POSTa/oauth/token.php. - Explicar cómo consumir
/api/userinfo.phpen distintos lenguajes. - Interpretar y explicar los mensajes de error listados arriba.
- Sugerir integraciones con juegos, launchers u otros servicios JevalNetwork usando este sistema.