RoyAalekh commited on
Commit
eee83a0
·
1 Parent(s): d7446d4

feat: Replace complex auto-login with simple Conference Demo button

Browse files

- Add Conference Demo option to login page with prominent styling
- Remove complex auto-login logic that caused redirect loops
- Simplify auth.py with on-demand session creation
- Add one-click conference access via dedicated login button
- Clean UX for conference participants
- Maintains all demo permissions and 12-hour sessions

Files changed (7) hide show
  1. app.py +14 -66
  2. auth.py +21 -19
  3. static/index.html +2 -2
  4. static/login.html +60 -0
  5. static/map.html +2 -2
  6. static/sw.js +1 -1
  7. version.json +1 -1
app.py CHANGED
@@ -390,48 +390,40 @@ async def login(login_data: LoginRequest, response: Response):
390
 
391
  @app.get("/api/auth/conference-login", response_model=ConferenceLoginResponse, tags=["Authentication"])
392
  async def conference_auto_login(response: Response):
393
- """Auto-login for conference demo mode"""
394
  if not settings.is_conference_mode():
395
  raise HTTPException(
396
  status_code=status.HTTP_404_NOT_FOUND,
397
  detail="Conference mode not enabled"
398
  )
399
 
400
- # Get the pre-created conference session token
401
- conference_token = auth_manager.get_conference_token()
402
- if not conference_token:
403
- raise HTTPException(
404
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
405
- detail="Conference session not initialized"
406
- )
407
-
408
- # Validate the session to get user data
409
- session_data = auth_manager.validate_session(conference_token)
410
- if not session_data:
411
  raise HTTPException(
412
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
413
- detail="Conference session invalid"
414
  )
415
 
416
  # Set authentication cookie for web page requests (12 hour expiry)
417
  response.set_cookie(
418
  key="auth_token",
419
- value=conference_token,
420
  max_age=12*60*60, # 12 hours for conference duration
421
  httponly=True,
422
  secure=True,
423
  samesite="lax"
424
  )
425
 
426
- logger.info("Conference participant auto-logged in")
427
 
428
  return ConferenceLoginResponse(
429
- token=conference_token,
430
  user={
431
- "username": session_data["username"],
432
- "role": session_data["role"],
433
- "full_name": session_data["full_name"],
434
- "permissions": session_data["permissions"]
435
  },
436
  is_demo_mode=True
437
  )
@@ -502,33 +494,11 @@ async def serve_login():
502
  raise HTTPException(status_code=404, detail="Login page not found")
503
 
504
  @app.get("/", response_class=HTMLResponse, tags=["Frontend"])
505
- async def read_root(request: Request, response: Response):
506
  """Serve the main application page with auth check"""
507
  # Check if user is authenticated
508
  user = get_current_user(request)
509
 
510
- # If no user and conference mode is enabled, auto-login
511
- if not user and settings.is_conference_mode():
512
- # Check if we already tried to set the cookie (prevent infinite loop)
513
- if not request.cookies.get('auth_token'):
514
- conference_token = auth_manager.get_conference_token()
515
- if conference_token:
516
- # Validate the session immediately to use it
517
- session = auth_manager.validate_session(conference_token)
518
- if session:
519
- # Set the conference session cookie
520
- response.set_cookie(
521
- key="auth_token",
522
- value=conference_token,
523
- max_age=12*60*60, # 12 hours
524
- httponly=True,
525
- secure=True,
526
- samesite="lax"
527
- )
528
- # Don't redirect - serve the page directly with the new session
529
- user = session
530
- logger.info("Conference participant auto-logged in")
531
-
532
  # Regular authentication check
533
  if not user:
534
  return RedirectResponse(url="/login")
@@ -543,33 +513,11 @@ async def read_root(request: Request, response: Response):
543
 
544
 
545
  @app.get("/map", response_class=HTMLResponse, tags=["Frontend"])
546
- async def serve_map(request: Request, response: Response):
547
  """Serve the map page with auth check"""
548
  # Check if user is authenticated
549
  user = get_current_user(request)
550
 
551
- # If no user and conference mode is enabled, auto-login
552
- if not user and settings.is_conference_mode():
553
- # Check if we already tried to set the cookie (prevent infinite loop)
554
- if not request.cookies.get('auth_token'):
555
- conference_token = auth_manager.get_conference_token()
556
- if conference_token:
557
- # Validate the session immediately to use it
558
- session = auth_manager.validate_session(conference_token)
559
- if session:
560
- # Set the conference session cookie
561
- response.set_cookie(
562
- key="auth_token",
563
- value=conference_token,
564
- max_age=12*60*60, # 12 hours
565
- httponly=True,
566
- secure=True,
567
- samesite="lax"
568
- )
569
- # Don't redirect - serve the page directly with the new session
570
- user = session
571
- logger.info("Conference participant auto-logged in for map")
572
-
573
  # Regular authentication check
574
  if not user:
575
  return RedirectResponse(url="/login")
 
390
 
391
  @app.get("/api/auth/conference-login", response_model=ConferenceLoginResponse, tags=["Authentication"])
392
  async def conference_auto_login(response: Response):
393
+ """Login for conference demo mode"""
394
  if not settings.is_conference_mode():
395
  raise HTTPException(
396
  status_code=status.HTTP_404_NOT_FOUND,
397
  detail="Conference mode not enabled"
398
  )
399
 
400
+ # Create a new conference session
401
+ result = auth_manager.create_conference_session()
402
+ if not result:
 
 
 
 
 
 
 
 
403
  raise HTTPException(
404
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
405
+ detail="Could not create conference session"
406
  )
407
 
408
  # Set authentication cookie for web page requests (12 hour expiry)
409
  response.set_cookie(
410
  key="auth_token",
411
+ value=result["token"],
412
  max_age=12*60*60, # 12 hours for conference duration
413
  httponly=True,
414
  secure=True,
415
  samesite="lax"
416
  )
417
 
418
+ logger.info("Conference participant logged in via demo button")
419
 
420
  return ConferenceLoginResponse(
421
+ token=result["token"],
422
  user={
423
+ "username": result["user"]["username"],
424
+ "role": result["user"]["role"],
425
+ "full_name": result["user"]["full_name"],
426
+ "permissions": result["user"]["permissions"]
427
  },
428
  is_demo_mode=True
429
  )
 
494
  raise HTTPException(status_code=404, detail="Login page not found")
495
 
496
  @app.get("/", response_class=HTMLResponse, tags=["Frontend"])
497
+ async def read_root(request: Request):
498
  """Serve the main application page with auth check"""
499
  # Check if user is authenticated
500
  user = get_current_user(request)
501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  # Regular authentication check
503
  if not user:
504
  return RedirectResponse(url="/login")
 
513
 
514
 
515
  @app.get("/map", response_class=HTMLResponse, tags=["Frontend"])
516
+ async def serve_map(request: Request):
517
  """Serve the map page with auth check"""
518
  # Check if user is authenticated
519
  user = get_current_user(request)
520
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  # Regular authentication check
522
  if not user:
523
  return RedirectResponse(url="/login")
auth.py CHANGED
@@ -78,23 +78,20 @@ class AuthManager:
78
  }
79
 
80
  logger.info(f"AuthManager initialized with {len(self.users)} user accounts")
81
-
82
- # Initialize conference session if needed
83
- self._init_conference_session()
84
 
85
- def _init_conference_session(self):
86
- """Initialize long-lasting conference session if in conference mode"""
87
- from config import get_settings
88
- settings = get_settings()
89
-
90
- if settings.is_conference_mode():
91
- # Create a long-lasting session for conference participants
92
- self.conference_session_token = secrets.token_urlsafe(AUTH_TOKEN_LENGTH)
 
93
 
94
- # Extended timeout for conference (12 hours to be safe)
95
  conference_timeout = timedelta(hours=12)
96
 
97
- conference_user = self.users["conference_participant"]
98
  session_data = {
99
  "username": "conference_participant",
100
  "role": conference_user["role"],
@@ -106,12 +103,17 @@ class AuthManager:
106
  "session_timeout": conference_timeout
107
  }
108
 
109
- self.sessions[self.conference_session_token] = session_data
110
- logger.info("Conference auto-login session initialized")
111
-
112
- def get_conference_token(self) -> Optional[str]:
113
- """Get the conference session token"""
114
- return self.conference_session_token
 
 
 
 
 
115
 
116
  def _hash_password(self, password: str) -> str:
117
  """Hash password using bcrypt with automatic salt generation"""
 
78
  }
79
 
80
  logger.info(f"AuthManager initialized with {len(self.users)} user accounts")
 
 
 
81
 
82
+ def create_conference_session(self) -> Optional[Dict[str, Any]]:
83
+ """Create a new conference session when requested"""
84
+ try:
85
+ conference_user = self.users.get("conference_participant")
86
+ if not conference_user:
87
+ return None
88
+
89
+ # Create session token
90
+ session_token = secrets.token_urlsafe(AUTH_TOKEN_LENGTH)
91
 
92
+ # Extended timeout for conference (12 hours)
93
  conference_timeout = timedelta(hours=12)
94
 
 
95
  session_data = {
96
  "username": "conference_participant",
97
  "role": conference_user["role"],
 
103
  "session_timeout": conference_timeout
104
  }
105
 
106
+ self.sessions[session_token] = session_data
107
+ logger.info("Conference session created")
108
+
109
+ return {
110
+ "token": session_token,
111
+ "user": session_data
112
+ }
113
+
114
+ except Exception as e:
115
+ logger.error(f"Error creating conference session: {e}")
116
+ return None
117
 
118
  def _hash_password(self, password: str) -> str:
119
  """Hash password using bcrypt with automatic salt generation"""
static/index.html CHANGED
@@ -907,7 +907,7 @@
907
  // Force refresh if we detect cached version
908
  (function() {
909
  const currentVersion = '5.1.1';
910
- const timestamp = '1761483168'; // Cache-busting bump
911
  const lastVersion = sessionStorage.getItem('treetrack_version');
912
  const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
913
 
@@ -1152,7 +1152,7 @@
1152
  </div>
1153
  </div>
1154
 
1155
- <script type="module" src="/static/js/tree-track-app.js?v=5.1.1&t=1761483168"></script>
1156
 
1157
  <script>
1158
  // Idle-time prefetch of map assets to speed up first navigation
 
907
  // Force refresh if we detect cached version
908
  (function() {
909
  const currentVersion = '5.1.1';
910
+ const timestamp = '1761483327'; // Cache-busting bump
911
  const lastVersion = sessionStorage.getItem('treetrack_version');
912
  const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
913
 
 
1152
  </div>
1153
  </div>
1154
 
1155
+ <script type="module" src="/static/js/tree-track-app.js?v=5.1.1&t=1761483327"></script>
1156
 
1157
  <script>
1158
  // Idle-time prefetch of map assets to speed up first navigation
static/login.html CHANGED
@@ -294,6 +294,28 @@
294
  }
295
  }
296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  /* Focus improvements */
298
  .demo-toggle:focus,
299
  .account-item:focus {
@@ -336,6 +358,10 @@
336
  </button>
337
 
338
  <div class="accounts-dropdown" id="accountsDropdown">
 
 
 
 
339
  <button class="account-item" onclick="selectAccount('aalekh', event)">
340
  <div class="account-role">Aalekh (Admin)</div>
341
  <div class="account-username">Full system access</div>
@@ -374,6 +400,40 @@
374
  }
375
  }
376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  function selectAccount(username, event) {
378
  document.getElementById('username').value = username;
379
  document.getElementById('password').value = '';
 
294
  }
295
  }
296
 
297
+ /* Conference Demo Styling */
298
+ .account-item.conference-demo {
299
+ background: linear-gradient(135deg, var(--primary-50), var(--primary-100));
300
+ border: 2px solid var(--primary-200);
301
+ margin-bottom: var(--space-2);
302
+ }
303
+
304
+ .account-item.conference-demo:hover {
305
+ background: linear-gradient(135deg, var(--primary-100), var(--primary-150));
306
+ border-color: var(--primary-300);
307
+ transform: translateY(-1px);
308
+ }
309
+
310
+ .account-item.conference-demo .account-role {
311
+ color: var(--primary-700);
312
+ font-weight: var(--font-semibold);
313
+ }
314
+
315
+ .account-item.conference-demo .account-username {
316
+ color: var(--primary-600);
317
+ }
318
+
319
  /* Focus improvements */
320
  .demo-toggle:focus,
321
  .account-item:focus {
 
358
  </button>
359
 
360
  <div class="accounts-dropdown" id="accountsDropdown">
361
+ <button class="account-item conference-demo" onclick="selectConferenceDemo(event)">
362
+ <div class="account-role">Conference Demo</div>
363
+ <div class="account-username">Public demonstration access</div>
364
+ </button>
365
  <button class="account-item" onclick="selectAccount('aalekh', event)">
366
  <div class="account-role">Aalekh (Admin)</div>
367
  <div class="account-username">Full system access</div>
 
400
  }
401
  }
402
 
403
+ function selectConferenceDemo(event) {
404
+ // Directly log in to conference demo
405
+ event.preventDefault();
406
+
407
+ setLoading(true);
408
+
409
+ // Call conference auto-login API
410
+ fetch('/api/auth/conference-login', {
411
+ method: 'GET'
412
+ })
413
+ .then(response => response.json())
414
+ .then(result => {
415
+ if (result.token) {
416
+ // Store authentication token
417
+ localStorage.setItem('auth_token', result.token);
418
+ localStorage.setItem('user_info', JSON.stringify(result.user));
419
+
420
+ showMessage('Conference demo access granted! Redirecting...', 'success');
421
+
422
+ // Redirect to main app
423
+ setTimeout(() => {
424
+ window.location.href = '/';
425
+ }, 1500);
426
+ } else {
427
+ throw new Error('Conference mode not available');
428
+ }
429
+ })
430
+ .catch(error => {
431
+ console.error('Conference login error:', error);
432
+ showMessage('Conference demo is not currently available. Please try again later.');
433
+ setLoading(false);
434
+ });
435
+ }
436
+
437
  function selectAccount(username, event) {
438
  document.getElementById('username').value = username;
439
  document.getElementById('password').value = '';
static/map.html CHANGED
@@ -799,7 +799,7 @@
799
  // Force refresh if we detect cached version
800
  (function() {
801
  const currentVersion = '5.1.1';
802
- const timestamp = '1761483168'; // Current timestamp for cache busting
803
  const lastVersion = sessionStorage.getItem('treetrack_version');
804
  const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
805
 
@@ -925,7 +925,7 @@ const timestamp = '1761483168'; // Current timestamp for cache busting
925
 
926
  <!-- Leaflet JS -->
927
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
928
- <script src="/static/map.js?v=5.1.1&t=1761483168">
929
 
930
  "default-state": {
931
  gradients: [
 
799
  // Force refresh if we detect cached version
800
  (function() {
801
  const currentVersion = '5.1.1';
802
+ const timestamp = '1761483327'; // Current timestamp for cache busting
803
  const lastVersion = sessionStorage.getItem('treetrack_version');
804
  const lastTimestamp = sessionStorage.getItem('treetrack_timestamp');
805
 
 
925
 
926
  <!-- Leaflet JS -->
927
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
928
+ <script src="/static/map.js?v=5.1.1&t=1761483327">
929
 
930
  "default-state": {
931
  gradients: [
static/sw.js CHANGED
@@ -1,5 +1,5 @@
1
  // TreeTrack Service Worker - PWA and Offline Support
2
- const VERSION = 1761483168; // Cache busting bump - force clients to fetch new static assets and header image change
3
  const CACHE_NAME = `treetrack-v${VERSION}`;
4
  const STATIC_CACHE = `static-v${VERSION}`;
5
  const API_CACHE = `api-v${VERSION}`;
 
1
  // TreeTrack Service Worker - PWA and Offline Support
2
+ const VERSION = 1761483327; // Cache busting bump - force clients to fetch new static assets and header image change
3
  const CACHE_NAME = `treetrack-v${VERSION}`;
4
  const STATIC_CACHE = `static-v${VERSION}`;
5
  const API_CACHE = `api-v${VERSION}`;
version.json CHANGED
@@ -1,4 +1,4 @@
1
  {
2
  "version": "5.1.1",
3
- "timestamp": 1761483168
4
  }
 
1
  {
2
  "version": "5.1.1",
3
+ "timestamp": 1761483327
4
  }