"""
Authentication Manager for ID Agents
=====================================
Handles user authentication, session management, and access control
for the ID Agents application running on HF Spaces.
"""
import gradio as gr
import time
import secrets
from typing import Dict, Optional, Tuple, Any
from auth_config import authenticate_user, authenticate_with_code, get_user_capabilities
class AuthManager:
def __init__(self):
self.active_sessions: Dict[str, Dict] = {}
self.session_timeout = 3600 # 1 hour in seconds
def create_session(self, user_info: Dict) -> str:
"""Create a new user session"""
session_id = secrets.token_urlsafe(32)
self.active_sessions[session_id] = {
"user_info": user_info,
"created_at": time.time(),
"last_activity": time.time(),
"capabilities": get_user_capabilities(user_info["access_level"])
}
return session_id
def validate_session(self, session_id: str) -> Tuple[bool, Optional[Dict]]:
"""Validate a session and return user info if valid"""
if not session_id or session_id not in self.active_sessions:
return False, None
session = self.active_sessions[session_id]
current_time = time.time()
# Check if session has expired
if current_time - session["last_activity"] > self.session_timeout:
del self.active_sessions[session_id]
return False, None
# Update last activity
session["last_activity"] = current_time
return True, session
def logout_session(self, session_id: str) -> bool:
"""Logout a user session"""
if session_id in self.active_sessions:
del self.active_sessions[session_id]
return True
return False
def get_session_info(self, session_id: str) -> Optional[Dict]:
"""Get session information"""
is_valid, session = self.validate_session(session_id)
return session if is_valid else None
# Global auth manager instance
auth_manager = AuthManager()
def create_auth_interface():
"""Create the authentication interface"""
with gr.Blocks(title="ID Agents - Beta Access") as auth_interface:
# Add custom CSS for the auth interface
auth_interface.css = """
:root {
--auth-bg: #1a1f2e;
--auth-panel: #23263a;
--auth-accent: #00ffe7;
--auth-accent2: #7f5cff;
--auth-text: #ffffff;
--auth-muted: #a0a5b8;
--auth-success: #00ff88;
--auth-error: #ff4757;
}
body, .gradio-container, .gr-block, .gr-app {
background: var(--auth-bg) !important;
color: var(--auth-text) !important;
}
.auth-container {
max-width: 500px;
margin: 2rem auto;
padding: 2rem;
background: linear-gradient(135deg, var(--auth-panel) 60%, #2a2f45 100%);
border: 2px solid var(--auth-accent2);
border-radius: 20px;
box-shadow: 0 8px 40px rgba(127, 92, 255, 0.3);
}
.auth-title {
text-align: center;
font-size: 2rem;
font-weight: 700;
margin-bottom: 0.5rem;
background: linear-gradient(90deg, var(--auth-accent), var(--auth-accent2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.auth-subtitle {
text-align: center;
color: var(--auth-muted);
margin-bottom: 2rem;
font-size: 1.1rem;
}
.auth-tabs {
background: transparent !important;
border: none !important;
}
.auth-form {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 1.5rem;
margin: 1rem 0;
}
.auth-input {
background: rgba(255, 255, 255, 0.1) !important;
border: 1px solid var(--auth-accent2) !important;
border-radius: 8px !important;
color: var(--auth-text) !important;
margin-bottom: 1rem;
}
.auth-button {
background: linear-gradient(90deg, var(--auth-accent2), var(--auth-accent)) !important;
color: var(--auth-bg) !important;
border: none !important;
border-radius: 8px !important;
font-weight: 600 !important;
padding: 12px 24px !important;
width: 100% !important;
margin-top: 1rem;
}
.auth-success {
color: var(--auth-success) !important;
background: rgba(0, 255, 136, 0.1);
border: 1px solid var(--auth-success);
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
.auth-error {
color: var(--auth-error) !important;
background: rgba(255, 71, 87, 0.1);
border: 1px solid var(--auth-error);
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
.auth-info {
background: rgba(0, 255, 231, 0.1);
border: 1px solid var(--auth-accent);
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
color: var(--auth-accent);
}
.user-info {
background: rgba(127, 92, 255, 0.1);
border: 1px solid var(--auth-accent2);
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
"""
# Session state
session_id = gr.State("")
with gr.Column(elem_classes="auth-container"):
gr.HTML('
🦠 ID Agents
')
gr.HTML('Beta Testing Access Portal
')
# Authentication status
auth_status = gr.Markdown("", visible=False)
user_info_display = gr.Markdown("", visible=False)
# Login tabs
with gr.Tabs(elem_classes="auth-tabs") as auth_tabs:
# Username/Password Login
with gr.Tab("👤 User Login", elem_classes="auth-form"):
gr.Markdown("### Sign in with your beta testing credentials")
username_input = gr.Textbox(
label="Username",
placeholder="Enter your username...",
elem_classes="auth-input"
)
password_input = gr.Textbox(
label="Password",
type="password",
placeholder="Enter your password...",
elem_classes="auth-input"
)
login_button = gr.Button("🔑 Sign In", elem_classes="auth-button")
gr.HTML("""
Beta Testing Accounts:
• dr_smith (password: idweek2025)
• id_fellow (password: hello)
• pharmacist (password: stewardship)
• ipc_nurse (password: infection)
• researcher (password: research)
• admin (password: idagents2025)
""")
# Invitation Code Login
with gr.Tab("🎫 Invitation Code", elem_classes="auth-form"):
gr.Markdown("### Enter your invitation code")
invitation_input = gr.Textbox(
label="Invitation Code",
placeholder="Enter your invitation code...",
elem_classes="auth-input"
)
code_login_button = gr.Button("🎟️ Access with Code", elem_classes="auth-button")
gr.HTML("""
Sample Invitation Codes:
• IDWEEK2025 (VIP Access)
• BETA-FELLOW (Fellowship Program)
• PHARM-STEW (Pharmacy Access)
• IPC-NURSE (Infection Prevention)
• RESEARCH-AI (Clinical Research)
""")
# Main app placeholder (hidden until authenticated)
app_container = gr.HTML("", visible=False)
# Logout button (hidden until authenticated)
logout_button = gr.Button("🚪 Logout", visible=False, elem_classes="auth-button")
# Authentication functions
def handle_login(username: str, password: str, current_session: str):
"""Handle username/password login"""
if not username or not password:
return (
gr.update(value="⚠️ Please enter both username and password.", visible=True, elem_classes="auth-error"),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
current_session
)
success, user_info = authenticate_user(username, password)
if success:
session_id = auth_manager.create_session(user_info)
user_display = f"""
✅ Welcome, {user_info['full_name']}!
Role: {user_info['role']}
Access Level: {user_info['access_level']}
Email: {user_info['email']}
"""
return (
gr.update(value="🎉 Authentication successful! Loading ID Agents...", visible=True, elem_classes="auth-success"),
gr.update(value=user_display, visible=True),
gr.update(visible=False), # Hide auth tabs
gr.update(visible=True), # Show app container
gr.update(visible=True), # Show logout button
session_id
)
else:
return (
gr.update(value="❌ Invalid username or password. Please try again.", visible=True, elem_classes="auth-error"),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
current_session
)
def handle_invitation_login(invitation_code: str, current_session: str):
"""Handle invitation code login"""
if not invitation_code:
return (
gr.update(value="⚠️ Please enter an invitation code.", visible=True, elem_classes="auth-error"),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
current_session
)
success, user_info = authenticate_with_code(invitation_code.upper())
if success:
session_id = auth_manager.create_session(user_info)
user_display = f"""
✅ Welcome, {user_info['full_name']}!
Role: {user_info['role']}
Access Level: {user_info['access_level']}
Email: {user_info['email']}
Authenticated via invitation code
"""
return (
gr.update(value="🎉 Authentication successful! Loading ID Agents...", visible=True, elem_classes="auth-success"),
gr.update(value=user_display, visible=True),
gr.update(visible=False), # Hide auth tabs
gr.update(visible=True), # Show app container
gr.update(visible=True), # Show logout button
session_id
)
else:
return (
gr.update(value="❌ Invalid or expired invitation code. Please check your code and try again.", visible=True, elem_classes="auth-error"),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
current_session
)
def handle_logout(current_session: str):
"""Handle user logout"""
if current_session:
auth_manager.logout_session(current_session)
return (
gr.update(value="", visible=False),
gr.update(visible=False),
gr.update(visible=True), # Show auth tabs
gr.update(visible=False), # Hide app container
gr.update(visible=False), # Hide logout button
"", # Clear session
"", # Clear username
"", # Clear password
"" # Clear invitation code
)
# Wire up the authentication handlers
login_button.click(
fn=handle_login,
inputs=[username_input, password_input, session_id],
outputs=[auth_status, user_info_display, auth_tabs, app_container, logout_button, session_id]
)
code_login_button.click(
fn=handle_invitation_login,
inputs=[invitation_input, session_id],
outputs=[auth_status, user_info_display, auth_tabs, app_container, logout_button, session_id]
)
logout_button.click(
fn=handle_logout,
inputs=[session_id],
outputs=[auth_status, user_info_display, auth_tabs, app_container, logout_button, session_id, username_input, password_input, invitation_input]
)
return auth_interface, session_id
def check_user_permission(session_id: str, required_capability: str) -> Tuple[bool, Optional[str]]:
"""
Check if user has permission for a specific capability
Returns:
(has_permission: bool, error_message: str or None)
"""
session_info = auth_manager.get_session_info(session_id)
if not session_info:
return False, "Please log in to access this feature."
capabilities = session_info["capabilities"]
if required_capability not in capabilities or not capabilities[required_capability]:
user_level = session_info["user_info"]["access_level"]
return False, f"Your access level ({user_level}) doesn't permit this action. Contact admin for upgrade."
return True, None
def get_user_limits(session_id: str) -> Dict[str, Any]:
"""Get user-specific limits based on their access level"""
session_info = auth_manager.get_session_info(session_id)
if not session_info:
return {
"max_agents": 0,
"max_file_size_mb": 0,
"can_upload_files": False
}
return {
"max_agents": session_info["capabilities"]["max_agents"],
"max_file_size_mb": session_info["capabilities"]["max_file_size_mb"],
"can_upload_files": session_info["capabilities"]["can_upload_files"],
"can_download_configs": session_info["capabilities"]["can_download_configs"],
"can_see_debug_info": session_info["capabilities"]["can_see_debug_info"]
}