File size: 3,496 Bytes
5383ef0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fec84f7
5383ef0
 
fec84f7
5383ef0
 
 
 
fec84f7
 
 
 
5383ef0
 
 
 
fec84f7
5383ef0
 
fec84f7
5383ef0
fec84f7
 
 
 
 
 
 
 
 
 
5383ef0
fec84f7
 
 
 
 
 
 
5383ef0
fec84f7
 
ad573c0
fec84f7
ad573c0
fec84f7
5383ef0
fec84f7
5383ef0
 
caef4d9
 
 
 
 
 
 
 
 
5383ef0
fec84f7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Signing in…</title>
  <style>
    body { background: #0d0e11; color: #e8eaf0; font-family: system-ui, sans-serif;
      display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; }
    .box { text-align: center; }
    .spinner { width: 32px; height: 32px; border: 3px solid rgba(229,200,70,0.2);
      border-top-color: #e5c846; border-radius: 50%; animation: spin 0.8s linear infinite;
      margin: 0 auto 16px; }
    @keyframes spin { to { transform: rotate(360deg); } }
  </style>
</head>
<body>
  <div class="box">
    <div class="spinner"></div>
    <p id="msg">Completing sign-in…</p>
  </div>
  <script>
    // ── Extract tokens from URL hash (Supabase implicit flow) or search params ──
    function getTokens() {
      const hash   = new URLSearchParams(location.hash.replace('#', ''));
      const search = new URLSearchParams(location.search);
      return {
        access_token:  hash.get('access_token')      || search.get('access_token'),
        refresh_token: hash.get('refresh_token')      || search.get('refresh_token'),
        error:         hash.get('error')              || search.get('error'),
        error_desc:    hash.get('error_description')  || search.get('error_description'),
      };
    }

    const tokens = getTokens();
    const msgEl  = document.getElementById('msg');

    if (tokens.error) {
      msgEl.textContent = 'Sign-in error: ' + (tokens.error_desc || tokens.error);
    } else if (tokens.access_token) {
      // ── Primary path: write to localStorage so the main tab picks it up
      // via a 'storage' event listener β€” works even when window.opener is null
      // (strict popup policies, mobile browsers, etc.).
      const payload = JSON.stringify({
        access_token:  tokens.access_token,
        refresh_token: tokens.refresh_token || '',
      });
      localStorage.setItem('ipai_oauth_pending', payload);

      // ── Also try postMessage to opener for fastest possible handoff ──────
      if (window.opener && !window.opener.closed) {
        try {
          window.opener.postMessage({
            type:          'oauth:callback',
            access_token:  tokens.access_token,
            refresh_token: tokens.refresh_token,
          }, location.origin);
        } catch (_) { /* cross-origin opener β€” storage event is enough */ }
      }

      msgEl.textContent = 'Sign-in complete! Closing…';
      // Give localStorage a moment to propagate, then try to close the popup.
      setTimeout(() => {
        try { window.close(); } catch (_) { /* ignore */ }
      }, 300);
    } else {
      msgEl.textContent = 'No token received. You can close this window.';
    }
  </script>
  <!-- Cloudflare Turnstile -->
  <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
  <div id="turnstile-overlay" style="position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.6);z-index:9999;">
    <div style="background:#0d0e11;padding:18px;border-radius:10px;text-align:center;max-width:420px;width:90%;">
      <div style="color:#d7d7d7;margin-bottom:12px;">Please verify you are human to continue.</div>
      <div class="cf-turnstile" data-sitekey="0x4AAAAAAC1ZXKIhZ9Kdz8j9" data-callback="onTurnstileSuccess"></div>
    </div>
  </div>
  <script type="module" src="/js/turnstile.js"></script>
</body>
</html>