Mark-Lasfar
commited on
Commit
·
df032da
1
Parent(s):
be59b4c
Update authorize for google & github
Browse files- api/auth.py +18 -0
- templates/login.html +80 -99
api/auth.py
CHANGED
|
@@ -6,10 +6,17 @@ from httpx_oauth.clients.github import GitHubOAuth2
|
|
| 6 |
from api.database import SessionLocal
|
| 7 |
from api.models import User, OAuthAccount
|
| 8 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
cookie_transport = CookieTransport(cookie_max_age=3600)
|
| 11 |
|
| 12 |
SECRET = os.getenv("JWT_SECRET")
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
def get_jwt_strategy() -> JWTStrategy:
|
| 15 |
return JWTStrategy(secret=SECRET, lifetime_seconds=3600)
|
|
@@ -20,11 +27,22 @@ auth_backend = AuthenticationBackend(
|
|
| 20 |
get_strategy=get_jwt_strategy,
|
| 21 |
)
|
| 22 |
|
|
|
|
| 23 |
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
|
| 24 |
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
|
| 25 |
GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID")
|
| 26 |
GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET")
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
google_oauth_client = GoogleOAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
|
| 29 |
github_oauth_client = GitHubOAuth2(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET)
|
| 30 |
|
|
|
|
| 6 |
from api.database import SessionLocal
|
| 7 |
from api.models import User, OAuthAccount
|
| 8 |
import os
|
| 9 |
+
import logging
|
| 10 |
+
|
| 11 |
+
# Setup logging
|
| 12 |
+
logger = logging.getLogger(__name__)
|
| 13 |
|
| 14 |
cookie_transport = CookieTransport(cookie_max_age=3600)
|
| 15 |
|
| 16 |
SECRET = os.getenv("JWT_SECRET")
|
| 17 |
+
if not SECRET or len(SECRET) < 32:
|
| 18 |
+
logger.error("JWT_SECRET is not set or too short.")
|
| 19 |
+
raise ValueError("JWT_SECRET is required (at least 32 characters).")
|
| 20 |
|
| 21 |
def get_jwt_strategy() -> JWTStrategy:
|
| 22 |
return JWTStrategy(secret=SECRET, lifetime_seconds=3600)
|
|
|
|
| 27 |
get_strategy=get_jwt_strategy,
|
| 28 |
)
|
| 29 |
|
| 30 |
+
# OAuth credentials
|
| 31 |
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
|
| 32 |
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
|
| 33 |
GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID")
|
| 34 |
GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET")
|
| 35 |
|
| 36 |
+
# Log OAuth credentials status
|
| 37 |
+
logger.info("GOOGLE_CLIENT_ID is set: %s", bool(GOOGLE_CLIENT_ID))
|
| 38 |
+
logger.info("GOOGLE_CLIENT_SECRET is set: %s", bool(GOOGLE_CLIENT_SECRET))
|
| 39 |
+
logger.info("GITHUB_CLIENT_ID is set: %s", bool(GITHUB_CLIENT_ID))
|
| 40 |
+
logger.info("GITHUB_CLIENT_SECRET is set: %s", bool(GITHUB_CLIENT_SECRET))
|
| 41 |
+
|
| 42 |
+
if not all([GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET]):
|
| 43 |
+
logger.error("One or more OAuth environment variables are missing.")
|
| 44 |
+
raise ValueError("All OAuth credentials (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET) are required.")
|
| 45 |
+
|
| 46 |
google_oauth_client = GoogleOAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
|
| 47 |
github_oauth_client = GitHubOAuth2(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET)
|
| 48 |
|
templates/login.html
CHANGED
|
@@ -104,12 +104,12 @@
|
|
| 104 |
</button>
|
| 105 |
</form>
|
| 106 |
<div class="flex justify-center gap-4 mt-4 flex-wrap">
|
| 107 |
-
<
|
| 108 |
Login with Google <i class="bx bxl-google ml-2"></i>
|
| 109 |
-
</
|
| 110 |
-
<
|
| 111 |
Login with GitHub <i class="bx bxl-github ml-2"></i>
|
| 112 |
-
</
|
| 113 |
</div>
|
| 114 |
<p class="mt-4">Don't have an account? <a href="/register" class="text-emerald-300 hover:underline">Register</a></p>
|
| 115 |
<p id="errorMsg" class="text-red-500 mt-4 hidden"></p>
|
|
@@ -189,107 +189,88 @@
|
|
| 189 |
📲 Install MG Chat
|
| 190 |
</button>
|
| 191 |
</footer>
|
|
|
|
| 192 |
<script>
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
} catch (error) {
|
| 216 |
-
spinner.classList.add('hidden');
|
| 217 |
-
errorMsg.textContent = 'An error occurred. Please try again.';
|
| 218 |
errorMsg.classList.remove('hidden');
|
| 219 |
}
|
| 220 |
-
})
|
| 221 |
-
|
| 222 |
-
|
|
|
|
|
|
|
| 223 |
}
|
| 224 |
-
|
| 225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
}
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
});
|
| 263 |
-
// تسجيل Service Worker لتفعيل الـ PWA
|
| 264 |
-
if ('serviceWorker' in navigator) {
|
| 265 |
-
navigator.serviceWorker.register('/static/js/sw.js')
|
| 266 |
-
.then(function(reg) {
|
| 267 |
-
console.log('✅ Service Worker Registered', reg);
|
| 268 |
-
}).catch(function(err) {
|
| 269 |
-
console.error('❌ Service Worker registration failed', err);
|
| 270 |
});
|
|
|
|
| 271 |
}
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
e.preventDefault();
|
| 276 |
-
deferredPrompt = e;
|
| 277 |
-
const installBtn = document.getElementById('installAppBtn');
|
| 278 |
-
if (installBtn) {
|
| 279 |
-
installBtn.style.display = 'block';
|
| 280 |
-
installBtn.addEventListener('click', () => {
|
| 281 |
-
deferredPrompt.prompt();
|
| 282 |
-
deferredPrompt.userChoice.then(choice => {
|
| 283 |
-
if (choice.outcome === 'accepted') {
|
| 284 |
-
console.log('✅ User accepted the install prompt');
|
| 285 |
-
} else {
|
| 286 |
-
console.log('❌ User dismissed the install prompt');
|
| 287 |
-
}
|
| 288 |
-
deferredPrompt = null;
|
| 289 |
-
});
|
| 290 |
-
});
|
| 291 |
-
}
|
| 292 |
-
});
|
| 293 |
-
</script>
|
| 294 |
</body>
|
| 295 |
</html>
|
|
|
|
| 104 |
</button>
|
| 105 |
</form>
|
| 106 |
<div class="flex justify-center gap-4 mt-4 flex-wrap">
|
| 107 |
+
<a id="googleLoginBtn" href="/auth/google" class="inline-flex items-center bg-gradient-to-r from-white to-gray-200 text-gray-800 px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
|
| 108 |
Login with Google <i class="bx bxl-google ml-2"></i>
|
| 109 |
+
</a>
|
| 110 |
+
<a id="githubLoginBtn" href="/auth/github" class="inline-flex items-center bg-gradient-to-r from-gray-800 to-black text-white px-6 py-3 rounded-full font-semibold hover:scale-105 transition-transform">
|
| 111 |
Login with GitHub <i class="bx bxl-github ml-2"></i>
|
| 112 |
+
</a>
|
| 113 |
</div>
|
| 114 |
<p class="mt-4">Don't have an account? <a href="/register" class="text-emerald-300 hover:underline">Register</a></p>
|
| 115 |
<p id="errorMsg" class="text-red-500 mt-4 hidden"></p>
|
|
|
|
| 189 |
📲 Install MG Chat
|
| 190 |
</button>
|
| 191 |
</footer>
|
| 192 |
+
|
| 193 |
<script>
|
| 194 |
+
const loginForm = document.getElementById('loginForm');
|
| 195 |
+
const loginBtn = document.getElementById('loginBtn');
|
| 196 |
+
const spinner = document.getElementById('spinner');
|
| 197 |
+
const errorMsg = document.getElementById('errorMsg');
|
| 198 |
+
|
| 199 |
+
// Handle email/password login
|
| 200 |
+
loginForm.addEventListener('submit', async (e) => {
|
| 201 |
+
e.preventDefault();
|
| 202 |
+
spinner.classList.remove('hidden');
|
| 203 |
+
errorMsg.classList.add('hidden');
|
| 204 |
+
const formData = new FormData(loginForm);
|
| 205 |
+
try {
|
| 206 |
+
const response = await fetch('/auth/jwt/login', {
|
| 207 |
+
method: 'POST',
|
| 208 |
+
body: formData
|
| 209 |
+
});
|
| 210 |
+
spinner.classList.add('hidden');
|
| 211 |
+
if (response.ok) {
|
| 212 |
+
window.location.href = '/chat';
|
| 213 |
+
} else {
|
| 214 |
+
const error = await response.json();
|
| 215 |
+
errorMsg.textContent = error.detail || 'Login failed. Please try again.';
|
|
|
|
|
|
|
|
|
|
| 216 |
errorMsg.classList.remove('hidden');
|
| 217 |
}
|
| 218 |
+
} catch (error) {
|
| 219 |
+
spinner.classList.add('hidden');
|
| 220 |
+
errorMsg.textContent = 'An error occurred. Please try again.';
|
| 221 |
+
errorMsg.classList.remove('hidden');
|
| 222 |
+
console.error('Error during login:', error);
|
| 223 |
}
|
| 224 |
+
});
|
| 225 |
+
|
| 226 |
+
// Check for error query parameters on page load (for OAuth errors)
|
| 227 |
+
window.addEventListener('load', () => {
|
| 228 |
+
const urlParams = new URLSearchParams(window.location.search);
|
| 229 |
+
const error = urlParams.get('error');
|
| 230 |
+
if (error) {
|
| 231 |
+
errorMsg.textContent = decodeURIComponent(error);
|
| 232 |
+
errorMsg.classList.remove('hidden');
|
| 233 |
}
|
| 234 |
+
});
|
| 235 |
+
|
| 236 |
+
// Handle card details toggle
|
| 237 |
+
function showCardDetails(cardId) {
|
| 238 |
+
document.getElementById(`${cardId}-details`).classList.remove('hidden');
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
function closeCardDetails(cardId) {
|
| 242 |
+
document.getElementById(`${cardId}-details`).classList.add('hidden');
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
// Service Worker for PWA
|
| 246 |
+
if ('serviceWorker' in navigator) {
|
| 247 |
+
navigator.serviceWorker.register('/static/js/sw.js')
|
| 248 |
+
.then(reg => console.log('✅ Service Worker Registered', reg))
|
| 249 |
+
.catch(err => console.error('❌ Service Worker registration failed', err));
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
// Handle PWA install prompt
|
| 253 |
+
let deferredPrompt;
|
| 254 |
+
window.addEventListener('beforeinstallprompt', (e) => {
|
| 255 |
+
e.preventDefault();
|
| 256 |
+
deferredPrompt = e;
|
| 257 |
+
const installBtn = document.getElementById('installAppBtn');
|
| 258 |
+
if (installBtn) {
|
| 259 |
+
installBtn.style.display = 'block';
|
| 260 |
+
installBtn.addEventListener('click', () => {
|
| 261 |
+
deferredPrompt.prompt();
|
| 262 |
+
deferredPrompt.userChoice.then(choice => {
|
| 263 |
+
if (choice.outcome === 'accepted') {
|
| 264 |
+
console.log('✅ User accepted the install prompt');
|
| 265 |
+
} else {
|
| 266 |
+
console.log('❌ User dismissed the install prompt');
|
| 267 |
+
}
|
| 268 |
+
deferredPrompt = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
});
|
| 270 |
+
});
|
| 271 |
}
|
| 272 |
+
});
|
| 273 |
+
</script>
|
| 274 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
</body>
|
| 276 |
</html>
|