180 lines
6.7 KiB
HTML
180 lines
6.7 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Facebook OAuth2 Callback</title>
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
background: #18191a;
|
||
color: #e4e6eb;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100vh;
|
||
text-align: center;
|
||
}
|
||
.card {
|
||
background: #242526;
|
||
border-radius: 12px;
|
||
padding: 36px 44px;
|
||
max-width: 440px;
|
||
width: 90%;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||
}
|
||
.icon { font-size: 3.2rem; margin-bottom: 16px; }
|
||
h2 { font-size: 1.25rem; margin-bottom: 10px; font-weight: 600; }
|
||
p { font-size: 0.9rem; color: #aaa; line-height: 1.5; }
|
||
.error { color: #f44336; }
|
||
.success { color: #4caf50; }
|
||
.warn { color: #ff9800; }
|
||
.spinner {
|
||
width: 36px; height: 36px;
|
||
border: 3px solid rgba(45,136,255,0.2);
|
||
border-top-color: #2D88FF;
|
||
border-radius: 50%;
|
||
animation: spin 0.8s linear infinite;
|
||
margin: 0 auto 16px;
|
||
}
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
.debug {
|
||
margin-top: 16px;
|
||
background: #1a1a1a;
|
||
border-radius: 6px;
|
||
padding: 10px 14px;
|
||
font-size: 0.75rem;
|
||
color: #666;
|
||
text-align: left;
|
||
word-break: break-all;
|
||
white-space: pre-wrap;
|
||
display: none;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="card">
|
||
<div class="spinner" id="spinner"></div>
|
||
<div class="icon" id="icon" style="display:none"></div>
|
||
<h2 id="title">Procesando autorización…</h2>
|
||
<p id="msg">Por favor espera</p>
|
||
<div class="debug" id="debug"></div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
|
||
var TARGET_ORIGIN = '*';
|
||
|
||
function show(icon, title, msg, cls) {
|
||
document.getElementById('spinner').style.display = 'none';
|
||
var iconEl = document.getElementById('icon');
|
||
iconEl.style.display = 'block';
|
||
iconEl.textContent = icon;
|
||
document.getElementById('title').textContent = title;
|
||
var msgEl = document.getElementById('msg');
|
||
msgEl.textContent = msg;
|
||
msgEl.className = cls || '';
|
||
}
|
||
|
||
function showDebug(text) {
|
||
var el = document.getElementById('debug');
|
||
el.style.display = 'block';
|
||
el.textContent = text;
|
||
}
|
||
|
||
function parseParams(str) {
|
||
var params = {};
|
||
if (!str) return params;
|
||
str.replace(/^[?#]/, '').split('&').forEach(function (part) {
|
||
var idx = part.indexOf('=');
|
||
if (idx > -1) {
|
||
params[decodeURIComponent(part.slice(0, idx))] =
|
||
decodeURIComponent(part.slice(idx + 1).replace(/\+/g, ' '));
|
||
}
|
||
});
|
||
return params;
|
||
}
|
||
|
||
var hashParams = parseParams(window.location.hash);
|
||
var queryParams = parseParams(window.location.search);
|
||
|
||
// ── ERROR de Facebook ─────────────────────────────────────────────────
|
||
var errorCode = hashParams.error || queryParams.error;
|
||
var errorDesc = hashParams.error_description || queryParams.error_description
|
||
|| hashParams.error_reason || queryParams.error_reason
|
||
|| 'Autorización cancelada o denegada';
|
||
|
||
if (errorCode) {
|
||
show('❌', 'Autorización fallida', errorCode + ': ' + errorDesc, 'error');
|
||
showDebug('error: ' + errorCode + '\ndescription: ' + errorDesc);
|
||
if (window.opener) {
|
||
window.opener.postMessage({ type: 'fb_oauth_result', error: errorCode + ': ' + errorDesc }, TARGET_ORIGIN);
|
||
}
|
||
setTimeout(function () { window.close(); }, 4000);
|
||
return;
|
||
}
|
||
|
||
// ── AUTH CODE (response_type=code) — flujo preferido con backend ──────
|
||
// El backend (server/index.js) intercambia: code → short-lived → long-lived (60 días)
|
||
var code = queryParams.code;
|
||
var state = queryParams.state || '';
|
||
|
||
if (code) {
|
||
show('🔄', 'Intercambiando código…',
|
||
'Obteniendo token de larga duración (60 días)…', 'success');
|
||
|
||
// Enviar el code a la ventana principal; ella llamará al backend
|
||
if (window.opener) {
|
||
window.opener.postMessage(
|
||
{ type: 'fb_oauth_result', flow: 'code', code: code, state: state,
|
||
redirect_uri: window.location.origin + '/oauth/facebook/callback.html' },
|
||
TARGET_ORIGIN
|
||
);
|
||
show('✅', '¡Código enviado!', 'Procesando token de larga duración…', 'success');
|
||
setTimeout(function () { window.close(); }, 2500);
|
||
} else {
|
||
show('⚠️', 'Ventana sin opener',
|
||
'Esta página debe abrirse como popup desde Restreamer.', 'warn');
|
||
showDebug('code recibido pero window.opener es null.\ncode: ' + code.slice(0, 20) + '…');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// ── TOKEN IMPLÍCITO (response_type=token) — fallback 2h ──────────────
|
||
// Se usa cuando el App Secret no está configurado en el backend.
|
||
var accessToken = hashParams.access_token || queryParams.access_token;
|
||
var expiresIn = parseInt(hashParams.expires_in || queryParams.expires_in || '0', 10);
|
||
|
||
if (accessToken) {
|
||
show('✅', '¡Token recibido!', 'Enviando a Restreamer para upgrade…', 'success');
|
||
if (window.opener) {
|
||
window.opener.postMessage(
|
||
{ type: 'fb_oauth_result', flow: 'token',
|
||
access_token: accessToken, expires_in: expiresIn },
|
||
TARGET_ORIGIN
|
||
);
|
||
setTimeout(function () { window.close(); }, 1800);
|
||
} else {
|
||
show('⚠️', 'Ventana sin opener',
|
||
'Copia el token manualmente.', 'warn');
|
||
showDebug('access_token: ' + accessToken.slice(0, 30) + '…\nexpires_in: ' + expiresIn);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// ── Sin datos útiles ──────────────────────────────────────────────────
|
||
show('⚠️', 'Sin datos de autorización',
|
||
'No se recibió code ni token. Revisa la configuración de tu App de Facebook.', 'warn');
|
||
showDebug('hash: ' + window.location.hash + '\nsearch: ' + window.location.search);
|
||
if (window.opener) {
|
||
window.opener.postMessage({ type: 'fb_oauth_result', error: 'no_data_received' }, TARGET_ORIGIN);
|
||
}
|
||
setTimeout(function () { window.close(); }, 6000);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|