Spaces:
Sleeping
Sleeping
Commit ·
69171a1
1
Parent(s): e744d95
Real Google Auth Upgrade: Integrated Google Identity Services (GIS). Button now triggers real Google Popup if GOOGLE_CLIENT_ID is set in secrets.
Browse files- main.py +6 -1
- static/app.js +68 -28
- templates/index.html +1 -0
main.py
CHANGED
|
@@ -38,7 +38,12 @@ templates = Jinja2Templates(directory="templates")
|
|
| 38 |
# ================= HOME =================
|
| 39 |
@app.get("/")
|
| 40 |
async def home(request: Request):
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
|
| 44 |
# ================= API =================
|
|
|
|
| 38 |
# ================= HOME =================
|
| 39 |
@app.get("/")
|
| 40 |
async def home(request: Request):
|
| 41 |
+
import os
|
| 42 |
+
cid = os.getenv("GOOGLE_CLIENT_ID", "")
|
| 43 |
+
return templates.TemplateResponse("index.html", {
|
| 44 |
+
"request": request,
|
| 45 |
+
"google_client_id": cid
|
| 46 |
+
})
|
| 47 |
|
| 48 |
|
| 49 |
# ================= API =================
|
static/app.js
CHANGED
|
@@ -68,6 +68,64 @@ const els = {
|
|
| 68 |
function init() {
|
| 69 |
setupEventListeners();
|
| 70 |
initChart();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
}
|
| 72 |
|
| 73 |
// --- Event Listeners ---
|
|
@@ -233,34 +291,16 @@ async function handleAuth() {
|
|
| 233 |
}
|
| 234 |
|
| 235 |
async function handleGoogleAuth() {
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
method: 'POST',
|
| 247 |
-
headers: {'Content-Type': 'application/json'},
|
| 248 |
-
body: JSON.stringify({ email })
|
| 249 |
-
});
|
| 250 |
-
const data = await res.json();
|
| 251 |
-
|
| 252 |
-
if (data.status === 'success') {
|
| 253 |
-
onLoginSuccess(data.user);
|
| 254 |
-
} else {
|
| 255 |
-
els.authError.innerText = data.message;
|
| 256 |
-
els.authError.style.display = 'block';
|
| 257 |
-
}
|
| 258 |
-
} catch(err) {
|
| 259 |
-
els.authError.innerText = "Google Auth failed.";
|
| 260 |
-
els.authError.style.display = 'block';
|
| 261 |
-
} finally {
|
| 262 |
-
els.googleAuthBtn.disabled = false;
|
| 263 |
-
els.googleAuthBtn.innerHTML = '<i class="fa-brands fa-google" style="color: #4285F4; font-size: 20px;"></i> Sign in with Google';
|
| 264 |
}
|
| 265 |
}
|
| 266 |
|
|
|
|
| 68 |
function init() {
|
| 69 |
setupEventListeners();
|
| 70 |
initChart();
|
| 71 |
+
initRealGoogleAuth();
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
function initRealGoogleAuth() {
|
| 75 |
+
const cid = document.querySelector('meta[name="google-signin-client_id"]').content;
|
| 76 |
+
if (!cid || !window.google) return;
|
| 77 |
+
|
| 78 |
+
google.accounts.id.initialize({
|
| 79 |
+
client_id: cid,
|
| 80 |
+
callback: handleGoogleCredentialResponse,
|
| 81 |
+
cancel_on_tap_outside: false
|
| 82 |
+
});
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
function handleGoogleCredentialResponse(response) {
|
| 86 |
+
// This is the JWT token from Google
|
| 87 |
+
const token = response.credential;
|
| 88 |
+
const base64Url = token.split('.')[1];
|
| 89 |
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
| 90 |
+
const jsonPayload = decodeURIComponent(atob(base64).split('').map(c => {
|
| 91 |
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
| 92 |
+
}).join(''));
|
| 93 |
+
|
| 94 |
+
const payload = JSON.parse(jsonPayload);
|
| 95 |
+
const email = payload.email;
|
| 96 |
+
|
| 97 |
+
if (email) {
|
| 98 |
+
authenticateViaGoogleEmail(email);
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
async function authenticateViaGoogleEmail(email) {
|
| 103 |
+
els.authError.style.display = 'none';
|
| 104 |
+
const originalBtnText = els.googleAuthBtn.innerHTML;
|
| 105 |
+
els.googleAuthBtn.disabled = true;
|
| 106 |
+
els.googleAuthBtn.innerText = "Authenticating...";
|
| 107 |
+
|
| 108 |
+
try {
|
| 109 |
+
const res = await fetch('/api/google_login', {
|
| 110 |
+
method: 'POST',
|
| 111 |
+
headers: {'Content-Type': 'application/json'},
|
| 112 |
+
body: JSON.stringify({ email })
|
| 113 |
+
});
|
| 114 |
+
const data = await res.json();
|
| 115 |
+
|
| 116 |
+
if (data.status === 'success') {
|
| 117 |
+
onLoginSuccess(data.user);
|
| 118 |
+
} else {
|
| 119 |
+
els.authError.innerText = data.message;
|
| 120 |
+
els.authError.style.display = 'block';
|
| 121 |
+
}
|
| 122 |
+
} catch(err) {
|
| 123 |
+
els.authError.innerText = "Google Auth failed.";
|
| 124 |
+
els.authError.style.display = 'block';
|
| 125 |
+
} finally {
|
| 126 |
+
els.googleAuthBtn.disabled = false;
|
| 127 |
+
els.googleAuthBtn.innerHTML = originalBtnText;
|
| 128 |
+
}
|
| 129 |
}
|
| 130 |
|
| 131 |
// --- Event Listeners ---
|
|
|
|
| 291 |
}
|
| 292 |
|
| 293 |
async function handleGoogleAuth() {
|
| 294 |
+
const cid = document.querySelector('meta[name="google-signin-client_id"]').content;
|
| 295 |
+
if (cid && window.google) {
|
| 296 |
+
google.accounts.id.prompt(); // Shows the Google One-Tap/Login popup
|
| 297 |
+
} else {
|
| 298 |
+
// Fallback simulation if no Client ID is provided
|
| 299 |
+
els.authError.style.display = 'none';
|
| 300 |
+
const email = prompt("Google Sign In (Simulation):\n\nPlease enter your Google email address to authenticate:");
|
| 301 |
+
if (!email) return;
|
| 302 |
+
if (!email.includes("@")) return alert("Please enter a valid email.");
|
| 303 |
+
authenticateViaGoogleEmail(email);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
}
|
| 305 |
}
|
| 306 |
|
templates/index.html
CHANGED
|
@@ -8,6 +8,7 @@
|
|
| 8 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
| 9 |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
| 10 |
<script src="https://accounts.google.com/gsi/client" async defer></script>
|
|
|
|
| 11 |
</head>
|
| 12 |
<body class="dark-theme">
|
| 13 |
|
|
|
|
| 8 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
| 9 |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
| 10 |
<script src="https://accounts.google.com/gsi/client" async defer></script>
|
| 11 |
+
<meta name="google-signin-client_id" content="{{ google_client_id }}">
|
| 12 |
</head>
|
| 13 |
<body class="dark-theme">
|
| 14 |
|