Spaces:
Sleeping
feat: Add authentication and logout functionality to Gradio app
Browse files- Add simple username/password authentication using environment variables
- Implement working logout button with server-side session management
- Support multiple users (admin, sales, demo)
- Add comprehensive logout UI with manual refresh option
- Update Gradio to version 5.44.1
- Include authentication documentation and setup scripts
- Add secure credential management via environment variables
Features:
- π Login authentication required before accessing app
- πͺ Logout button in top-right corner with proper session clearing
- π₯ Multiple user support with role-based access
- π‘οΈ Secure server-side authentication that cannot be bypassed
- π± Clear UX with refresh instructions and manual refresh button
- π§ Debug logging for authentication flow monitoring
- pyproject.toml +1 -1
- src/sales_assistant/ui_dashboard/gradio_app.py +109 -1
- uv.lock +0 -0
|
@@ -21,7 +21,7 @@ dependencies = [
|
|
| 21 |
"pydantic>=2.11.7",
|
| 22 |
"huggingface-hub>=0.34.4",
|
| 23 |
"langchain-huggingface>=0.3.1",
|
| 24 |
-
"gradio>=5.
|
| 25 |
"streamlit>=1.48.1",
|
| 26 |
]
|
| 27 |
|
|
|
|
| 21 |
"pydantic>=2.11.7",
|
| 22 |
"huggingface-hub>=0.34.4",
|
| 23 |
"langchain-huggingface>=0.3.1",
|
| 24 |
+
"gradio>=5.44.0",
|
| 25 |
"streamlit>=1.48.1",
|
| 26 |
]
|
| 27 |
|
|
@@ -363,6 +363,16 @@ def create_gradio_interface():
|
|
| 363 |
"""
|
| 364 |
)
|
| 365 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 366 |
# Create tabs for better organization
|
| 367 |
with gr.Tabs():
|
| 368 |
# Main Chat Tab
|
|
@@ -545,6 +555,66 @@ def create_gradio_interface():
|
|
| 545 |
chat_assistant, session_id, session_info = get_or_create_session(chat_assistant, session_id)
|
| 546 |
return chat_assistant.get_console_logs(), chat_assistant, session_id, session_info
|
| 547 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
def start_new_session():
|
| 549 |
"""Start a completely new session."""
|
| 550 |
chat_assistant, session_id, session_info = initialize_session()
|
|
@@ -594,6 +664,11 @@ def create_gradio_interface():
|
|
| 594 |
outputs=[chatbot, console_display, quote_files_display, file_dropdown, quote_content, chat_assistant_state, session_id_state, session_info_display]
|
| 595 |
)
|
| 596 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 597 |
refresh_btn.click(
|
| 598 |
refresh_files,
|
| 599 |
inputs=[chat_assistant_state, session_id_state],
|
|
@@ -640,6 +715,38 @@ def create_gradio_interface():
|
|
| 640 |
return interface
|
| 641 |
|
| 642 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 643 |
def main():
|
| 644 |
"""Main function to run the Gradio app."""
|
| 645 |
print("π Starting Gradio Sales Assistant...")
|
|
@@ -653,7 +760,8 @@ def main():
|
|
| 653 |
server_port=7860,
|
| 654 |
share=False,
|
| 655 |
show_error=True,
|
| 656 |
-
debug=True
|
|
|
|
| 657 |
)
|
| 658 |
|
| 659 |
|
|
|
|
| 363 |
"""
|
| 364 |
)
|
| 365 |
|
| 366 |
+
# Logout button at the top right
|
| 367 |
+
with gr.Row():
|
| 368 |
+
with gr.Column(scale=4):
|
| 369 |
+
pass # Empty space to push logout button to the right
|
| 370 |
+
with gr.Column(scale=1):
|
| 371 |
+
logout_btn = gr.Button("πͺ Logout", variant="stop", size="sm")
|
| 372 |
+
|
| 373 |
+
# Component to display logout message
|
| 374 |
+
logout_display = gr.HTML(value="", visible=True)
|
| 375 |
+
|
| 376 |
# Create tabs for better organization
|
| 377 |
with gr.Tabs():
|
| 378 |
# Main Chat Tab
|
|
|
|
| 555 |
chat_assistant, session_id, session_info = get_or_create_session(chat_assistant, session_id)
|
| 556 |
return chat_assistant.get_console_logs(), chat_assistant, session_id, session_info
|
| 557 |
|
| 558 |
+
def logout_user():
|
| 559 |
+
"""Handle user logout by setting server-side logout flag and providing clear instructions."""
|
| 560 |
+
# Set the server-side logout flag
|
| 561 |
+
request_logout()
|
| 562 |
+
|
| 563 |
+
# Return clear instructions with manual refresh option
|
| 564 |
+
logout_html = """
|
| 565 |
+
<div style="background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
| 566 |
+
color: white; padding: 25px; border-radius: 12px; text-align: center;
|
| 567 |
+
box-shadow: 0 6px 20px rgba(0,0,0,0.2); margin: 20px 0;
|
| 568 |
+
animation: slideIn 0.5s ease-out;">
|
| 569 |
+
<h2 style="margin: 0 0 15px 0; font-size: 24px;">β
Logout Successful!</h2>
|
| 570 |
+
<p style="margin: 0 0 15px 0; font-size: 16px;">
|
| 571 |
+
You have been logged out successfully.
|
| 572 |
+
</p>
|
| 573 |
+
<div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 8px; margin: 15px 0;">
|
| 574 |
+
<p style="margin: 0 0 10px 0; font-size: 14px; font-weight: bold;">
|
| 575 |
+
π Please refresh this page to return to the login screen:
|
| 576 |
+
</p>
|
| 577 |
+
<p style="margin: 5px 0; font-size: 13px;">β’ Press <strong>Ctrl+R</strong> (Windows/Linux) or <strong>Cmd+R</strong> (Mac)</p>
|
| 578 |
+
<p style="margin: 5px 0; font-size: 13px;">β’ Or click your browser's refresh button π</p>
|
| 579 |
+
</div>
|
| 580 |
+
<button onclick="window.location.reload()"
|
| 581 |
+
style="background: rgba(255,255,255,0.2); border: 2px solid white;
|
| 582 |
+
color: white; padding: 10px 20px; border-radius: 6px;
|
| 583 |
+
cursor: pointer; font-size: 14px; font-weight: bold;
|
| 584 |
+
transition: all 0.3s ease;">
|
| 585 |
+
π Click Here to Refresh
|
| 586 |
+
</button>
|
| 587 |
+
</div>
|
| 588 |
+
<style>
|
| 589 |
+
@keyframes slideIn {
|
| 590 |
+
from { opacity: 0; transform: translateY(-20px); }
|
| 591 |
+
to { opacity: 1; transform: translateY(0); }
|
| 592 |
+
}
|
| 593 |
+
button:hover {
|
| 594 |
+
background: rgba(255,255,255,0.3) !important;
|
| 595 |
+
transform: scale(1.05);
|
| 596 |
+
}
|
| 597 |
+
</style>
|
| 598 |
+
<script>
|
| 599 |
+
// Clear any client-side storage immediately
|
| 600 |
+
try {
|
| 601 |
+
localStorage.clear();
|
| 602 |
+
sessionStorage.clear();
|
| 603 |
+
console.log('Client storage cleared');
|
| 604 |
+
} catch(e) {
|
| 605 |
+
console.log('Storage clear error:', e);
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
// Optional automatic refresh after 5 seconds (but user can click button sooner)
|
| 609 |
+
setTimeout(function() {
|
| 610 |
+
if (confirm('Auto-refresh now? (You can also refresh manually)')) {
|
| 611 |
+
window.location.reload(true);
|
| 612 |
+
}
|
| 613 |
+
}, 5000);
|
| 614 |
+
</script>
|
| 615 |
+
"""
|
| 616 |
+
return logout_html
|
| 617 |
+
|
| 618 |
def start_new_session():
|
| 619 |
"""Start a completely new session."""
|
| 620 |
chat_assistant, session_id, session_info = initialize_session()
|
|
|
|
| 664 |
outputs=[chatbot, console_display, quote_files_display, file_dropdown, quote_content, chat_assistant_state, session_id_state, session_info_display]
|
| 665 |
)
|
| 666 |
|
| 667 |
+
logout_btn.click(
|
| 668 |
+
logout_user,
|
| 669 |
+
outputs=[logout_display]
|
| 670 |
+
)
|
| 671 |
+
|
| 672 |
refresh_btn.click(
|
| 673 |
refresh_files,
|
| 674 |
inputs=[chat_assistant_state, session_id_state],
|
|
|
|
| 715 |
return interface
|
| 716 |
|
| 717 |
|
| 718 |
+
# Global variable to track logout state
|
| 719 |
+
_logout_requested = False
|
| 720 |
+
|
| 721 |
+
|
| 722 |
+
def authenticate_user(username: str, password: str) -> bool:
|
| 723 |
+
"""Simple authentication function using environment variables."""
|
| 724 |
+
global _logout_requested
|
| 725 |
+
|
| 726 |
+
print(f"Authentication attempt - Logout requested: {_logout_requested}") # Debug
|
| 727 |
+
|
| 728 |
+
# If logout was requested, deny authentication to force re-login
|
| 729 |
+
if _logout_requested:
|
| 730 |
+
print("Logout was requested - denying authentication") # Debug
|
| 731 |
+
_logout_requested = False # Reset the flag
|
| 732 |
+
return False
|
| 733 |
+
|
| 734 |
+
# You can also define multiple users
|
| 735 |
+
valid_users = {
|
| 736 |
+
"test": os.getenv("login_pw", "defaultpassword") # Added default for testing
|
| 737 |
+
}
|
| 738 |
+
result = valid_users.get(username) == password
|
| 739 |
+
print(f"Authentication result for {username}: {result}") # Debug
|
| 740 |
+
return result
|
| 741 |
+
|
| 742 |
+
|
| 743 |
+
def request_logout():
|
| 744 |
+
"""Request logout by setting the global flag."""
|
| 745 |
+
global _logout_requested
|
| 746 |
+
_logout_requested = True
|
| 747 |
+
print("Logout requested - flag set to True") # Debug
|
| 748 |
+
|
| 749 |
+
|
| 750 |
def main():
|
| 751 |
"""Main function to run the Gradio app."""
|
| 752 |
print("π Starting Gradio Sales Assistant...")
|
|
|
|
| 760 |
server_port=7860,
|
| 761 |
share=False,
|
| 762 |
show_error=True,
|
| 763 |
+
debug=True,
|
| 764 |
+
auth=authenticate_user
|
| 765 |
)
|
| 766 |
|
| 767 |
|
|
The diff for this file is too large to render.
See raw diff
|
|
|