hospital-management/templates/account/two_factor_device_form.html
2025-08-12 13:33:25 +03:00

712 lines
32 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}{% if object %}Edit{% else %}Add{% endif %} 2FA Device - Account Security{% endblock %}
{% block content %}
<div class="content">
<div class="container-fluid">
<!-- Page Header -->
<div class="row">
<div class="col-12">
<div class="page-header">
<div class="page-title">
<h4>
<a href="{% url 'accounts:two_factor_device_list' %}" class="me-3">
<i class="fas fa-arrow-left"></i>
</a>
{% if object %}Edit{% else %}Add{% endif %} 2FA Device
</h4>
<h6>{% if object %}Update{% else %}Configure{% endif %} two-factor authentication device</h6>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Device Setup Form -->
<div class="col-lg-8 col-md-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-shield-alt me-2"></i>
Device Configuration
</h5>
</div>
<div class="card-body">
<form method="post" id="deviceForm">
{% csrf_token %}
<!-- Device Type Selection -->
<div class="row mb-4">
<div class="col-12">
<label class="form-label">Device Type <span class="text-danger">*</span></label>
<div class="device-type-selection">
<div class="row">
<div class="col-md-4">
<div class="device-type-card" data-type="authenticator">
<input type="radio" name="device_type" value="authenticator" id="type_authenticator"
{% if not object or object.device_type == 'authenticator' %}checked{% endif %}>
<label for="type_authenticator" class="device-type-label">
<i class="fas fa-mobile-alt fa-3x text-primary mb-3"></i>
<h6>Authenticator App</h6>
<p class="text-muted small">Google Authenticator, Authy, etc.</p>
<span class="badge bg-primary">Recommended</span>
</label>
</div>
</div>
<div class="col-md-4">
<div class="device-type-card" data-type="hardware_key">
<input type="radio" name="device_type" value="hardware_key" id="type_hardware_key"
{% if object.device_type == 'hardware_key' %}checked{% endif %}>
<label for="type_hardware_key" class="device-type-label">
<i class="fas fa-key fa-3x text-success mb-3"></i>
<h6>Hardware Key</h6>
<p class="text-muted small">YubiKey, Titan Key, etc.</p>
<span class="badge bg-success">Most Secure</span>
</label>
</div>
</div>
<div class="col-md-4">
<div class="device-type-card" data-type="sms">
<input type="radio" name="device_type" value="sms" id="type_sms"
{% if object.device_type == 'sms' %}checked{% endif %}>
<label for="type_sms" class="device-type-label">
<i class="fas fa-sms fa-3x text-warning mb-3"></i>
<h6>SMS</h6>
<p class="text-muted small">Text message codes</p>
<span class="badge bg-warning">Backup Only</span>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Basic Information -->
<div class="row mb-4">
<div class="col-md-6">
<div class="form-group">
<label for="device_name" class="form-label">Device Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="device_name" name="name"
value="{{ object.name|default:'' }}"
placeholder="e.g., My iPhone, Work YubiKey" required>
<small class="form-text text-muted">Choose a name to easily identify this device</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="device_description" class="form-label">Description</label>
<input type="text" class="form-control" id="device_description" name="description"
value="{{ object.description|default:'' }}"
placeholder="Optional description">
</div>
</div>
</div>
<!-- Authenticator App Setup -->
<div id="authenticator_setup" class="setup-section" style="display: none;">
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Setup Instructions:</strong> Scan the QR code below with your authenticator app, then enter the verification code.
</div>
<div class="row">
<div class="col-md-6 text-center">
<div class="qr-code-container mb-3">
<div class="qr-code-placeholder">
<i class="fas fa-qrcode fa-5x text-muted mb-3"></i>
<p class="text-muted">QR Code will appear here</p>
<button type="button" class="btn btn-primary btn-sm" onclick="generateQRCode()">
<i class="fas fa-sync me-1"></i>Generate QR Code
</button>
</div>
</div>
<div class="manual-entry">
<small class="text-muted">Can't scan? Enter manually:</small>
<div class="input-group input-group-sm mt-2">
<input type="text" class="form-control" id="manual_key" readonly
value="JBSWY3DPEHPK3PXP" placeholder="Secret key">
<button class="btn btn-outline-secondary" type="button" onclick="copySecret()">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="verification_code" class="form-label">Verification Code <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="verification_code" name="verification_code"
placeholder="Enter 6-digit code" maxlength="6" pattern="[0-9]{6}">
<small class="form-text text-muted">Enter the 6-digit code from your authenticator app</small>
</div>
<div class="verification-status mt-3" id="verification_status" style="display: none;">
<!-- Verification status will be shown here -->
</div>
</div>
</div>
</div>
<!-- Hardware Key Setup -->
<div id="hardware_key_setup" class="setup-section" style="display: none;">
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Setup Instructions:</strong> Insert your hardware key and click the button below to register it.
</div>
<div class="text-center">
<div class="hardware-key-status mb-4">
<i class="fas fa-key fa-4x text-muted mb-3"></i>
<p class="text-muted">Insert your hardware security key</p>
</div>
<button type="button" class="btn btn-primary" onclick="registerHardwareKey()">
<i class="fas fa-usb me-2"></i>Register Hardware Key
</button>
<div class="registration-status mt-3" id="hardware_status" style="display: none;">
<!-- Registration status will be shown here -->
</div>
</div>
</div>
<!-- SMS Setup -->
<div id="sms_setup" class="setup-section" style="display: none;">
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle me-2"></i>
<strong>Note:</strong> SMS is less secure than other methods and should only be used as a backup option.
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="phone_number" class="form-label">Phone Number <span class="text-danger">*</span></label>
<input type="tel" class="form-control" id="phone_number" name="phone_number"
value="{{ object.phone_number|default:'' }}"
placeholder="+1 (555) 123-4567">
<small class="form-text text-muted">Include country code</small>
</div>
<button type="button" class="btn btn-outline-primary" onclick="sendTestSMS()">
<i class="fas fa-paper-plane me-1"></i>Send Test Code
</button>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="sms_verification_code" class="form-label">Verification Code</label>
<input type="text" class="form-control" id="sms_verification_code" name="sms_verification_code"
placeholder="Enter code from SMS" maxlength="6" pattern="[0-9]{6}">
<small class="form-text text-muted">Enter the code sent to your phone</small>
</div>
<div class="sms-status mt-3" id="sms_status" style="display: none;">
<!-- SMS verification status will be shown here -->
</div>
</div>
</div>
</div>
<!-- Additional Options -->
<div class="row mt-4">
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_primary" name="is_primary"
{% if object.is_primary %}checked{% endif %}>
<label class="form-check-label" for="is_primary">
Set as primary 2FA device
</label>
<small class="form-text text-muted d-block">Primary device will be used first for authentication</small>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="generate_backup_codes" name="generate_backup_codes"
{% if not object %}checked{% endif %}>
<label class="form-check-label" for="generate_backup_codes">
Generate backup codes
</label>
<small class="form-text text-muted d-block">Backup codes can be used if your device is unavailable</small>
</div>
</div>
</div>
<!-- Form Actions -->
<div class="row mt-5">
<div class="col-12">
<div class="d-flex justify-content-between">
<a href="{% url 'accounts:two_factor_device_list' %}" class="btn btn-secondary">
<i class="fas fa-times me-1"></i>Cancel
</a>
<div>
<button type="button" class="btn btn-outline-primary me-2" onclick="testConfiguration()">
<i class="fas fa-vial me-1"></i>Test Configuration
</button>
<button type="submit" class="btn btn-primary" id="submitBtn">
<i class="fas fa-save me-1"></i>
{% if object %}Update{% else %}Add{% endif %} Device
</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Setup Guide Sidebar -->
<div class="col-lg-4 col-md-12">
<!-- Setup Progress -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-list-check me-2"></i>
Setup Progress
</h5>
</div>
<div class="card-body">
<div class="setup-steps">
<div class="step-item active" id="step1">
<div class="step-number">1</div>
<div class="step-content">
<h6>Choose Device Type</h6>
<small class="text-muted">Select your preferred 2FA method</small>
</div>
</div>
<div class="step-item" id="step2">
<div class="step-number">2</div>
<div class="step-content">
<h6>Configure Device</h6>
<small class="text-muted">Set up your authentication device</small>
</div>
</div>
<div class="step-item" id="step3">
<div class="step-number">3</div>
<div class="step-content">
<h6>Verify Setup</h6>
<small class="text-muted">Test your device configuration</small>
</div>
</div>
<div class="step-item" id="step4">
<div class="step-number">4</div>
<div class="step-content">
<h6>Complete</h6>
<small class="text-muted">Save your 2FA device</small>
</div>
</div>
</div>
</div>
</div>
<!-- Security Information -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-shield-alt me-2"></i>
Security Levels
</h5>
</div>
<div class="card-body">
<div class="security-level mb-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-key text-success me-2"></i>
<strong>Hardware Key</strong>
</div>
<span class="badge bg-success">High</span>
</div>
<small class="text-muted d-block mt-1">Phishing-resistant, most secure option</small>
</div>
<div class="security-level mb-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-mobile-alt text-primary me-2"></i>
<strong>Authenticator App</strong>
</div>
<span class="badge bg-primary">Medium</span>
</div>
<small class="text-muted d-block mt-1">Time-based codes, good security</small>
</div>
<div class="security-level">
<div class="d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-sms text-warning me-2"></i>
<strong>SMS</strong>
</div>
<span class="badge bg-warning">Basic</span>
</div>
<small class="text-muted d-block mt-1">Vulnerable to SIM swapping attacks</small>
</div>
</div>
</div>
<!-- Popular Apps -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-mobile-alt me-2"></i>
Recommended Apps
</h5>
</div>
<div class="card-body">
<div class="app-recommendation mb-3">
<div class="d-flex align-items-center">
<i class="fab fa-google text-primary fa-2x me-3"></i>
<div>
<h6 class="mb-0">Google Authenticator</h6>
<small class="text-muted">Free, simple, reliable</small>
</div>
</div>
</div>
<div class="app-recommendation mb-3">
<div class="d-flex align-items-center">
<i class="fas fa-shield-alt text-success fa-2x me-3"></i>
<div>
<h6 class="mb-0">Authy</h6>
<small class="text-muted">Cloud backup, multi-device</small>
</div>
</div>
</div>
<div class="app-recommendation">
<div class="d-flex align-items-center">
<i class="fas fa-lock text-info fa-2x me-3"></i>
<div>
<h6 class="mb-0">1Password</h6>
<small class="text-muted">Integrated with password manager</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize device type selection
const deviceTypeRadios = document.querySelectorAll('input[name="device_type"]');
deviceTypeRadios.forEach(radio => {
radio.addEventListener('change', function() {
showSetupSection(this.value);
updateStepProgress(2);
});
});
// Show initial setup section
const checkedRadio = document.querySelector('input[name="device_type"]:checked');
if (checkedRadio) {
showSetupSection(checkedRadio.value);
}
// Form validation
const form = document.getElementById('deviceForm');
form.addEventListener('submit', function(e) {
if (!validateForm()) {
e.preventDefault();
}
});
});
function showSetupSection(deviceType) {
// Hide all setup sections
const sections = document.querySelectorAll('.setup-section');
sections.forEach(section => section.style.display = 'none');
// Show selected section
const targetSection = document.getElementById(deviceType + '_setup');
if (targetSection) {
targetSection.style.display = 'block';
}
// Update device type cards
const cards = document.querySelectorAll('.device-type-card');
cards.forEach(card => card.classList.remove('selected'));
const selectedCard = document.querySelector(`[data-type="${deviceType}"]`);
if (selectedCard) {
selectedCard.classList.add('selected');
}
}
function updateStepProgress(step) {
const steps = document.querySelectorAll('.step-item');
steps.forEach((stepItem, index) => {
if (index < step) {
stepItem.classList.add('completed');
stepItem.classList.remove('active');
} else if (index === step - 1) {
stepItem.classList.add('active');
stepItem.classList.remove('completed');
} else {
stepItem.classList.remove('active', 'completed');
}
});
}
function generateQRCode() {
const button = event.target;
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Generating...';
button.disabled = true;
setTimeout(() => {
// Simulate QR code generation
const container = document.querySelector('.qr-code-placeholder');
container.innerHTML = `
<div class="qr-code-display">
<div style="width: 200px; height: 200px; background: #000; margin: 0 auto; display: flex; align-items: center; justify-content: center; color: white;">
QR CODE
</div>
<p class="text-success mt-2"><i class="fas fa-check me-1"></i>QR Code Generated</p>
</div>
`;
updateStepProgress(3);
}, 2000);
}
function copySecret() {
const secretInput = document.getElementById('manual_key');
secretInput.select();
document.execCommand('copy');
const button = event.target.closest('button');
const originalIcon = button.innerHTML;
button.innerHTML = '<i class="fas fa-check"></i>';
setTimeout(() => {
button.innerHTML = originalIcon;
}, 2000);
}
function registerHardwareKey() {
const button = event.target;
const originalText = button.innerHTML;
const statusDiv = document.getElementById('hardware_status');
button.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Registering...';
button.disabled = true;
statusDiv.style.display = 'block';
statusDiv.innerHTML = '<div class="alert alert-info"><i class="fas fa-info-circle me-2"></i>Touch your security key now...</div>';
setTimeout(() => {
statusDiv.innerHTML = '<div class="alert alert-success"><i class="fas fa-check-circle me-2"></i>Hardware key registered successfully!</div>';
button.innerHTML = '<i class="fas fa-check me-2"></i>Registered';
updateStepProgress(3);
}, 3000);
}
function sendTestSMS() {
const phoneInput = document.getElementById('phone_number');
const button = event.target;
const originalText = button.innerHTML;
if (!phoneInput.value) {
alert('Please enter a phone number first.');
return;
}
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Sending...';
button.disabled = true;
setTimeout(() => {
const statusDiv = document.getElementById('sms_status');
statusDiv.style.display = 'block';
statusDiv.innerHTML = '<div class="alert alert-success"><i class="fas fa-check-circle me-2"></i>Test code sent to your phone!</div>';
button.innerHTML = originalText;
button.disabled = false;
updateStepProgress(3);
}, 2000);
}
function testConfiguration() {
const deviceType = document.querySelector('input[name="device_type"]:checked').value;
alert(`Testing ${deviceType} configuration...`);
updateStepProgress(4);
}
function validateForm() {
const deviceType = document.querySelector('input[name="device_type"]:checked');
const deviceName = document.getElementById('device_name').value;
if (!deviceType) {
alert('Please select a device type.');
return false;
}
if (!deviceName.trim()) {
alert('Please enter a device name.');
return false;
}
// Additional validation based on device type
if (deviceType.value === 'authenticator') {
const verificationCode = document.getElementById('verification_code').value;
if (!verificationCode || verificationCode.length !== 6) {
alert('Please enter a valid 6-digit verification code.');
return false;
}
} else if (deviceType.value === 'sms') {
const phoneNumber = document.getElementById('phone_number').value;
if (!phoneNumber.trim()) {
alert('Please enter a phone number.');
return false;
}
}
return true;
}
</script>
<style>
.device-type-selection {
margin-bottom: 20px;
}
.device-type-card {
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
height: 100%;
}
.device-type-card:hover {
border-color: #007bff;
box-shadow: 0 4px 8px rgba(0,123,255,0.1);
}
.device-type-card.selected {
border-color: #007bff;
background-color: #f8f9ff;
}
.device-type-card input[type="radio"] {
display: none;
}
.device-type-label {
cursor: pointer;
margin: 0;
width: 100%;
height: 100%;
display: block;
}
.setup-section {
border: 1px solid #e9ecef;
border-radius: 10px;
padding: 20px;
margin-top: 20px;
background-color: #f8f9fa;
}
.qr-code-container {
border: 2px dashed #dee2e6;
border-radius: 10px;
padding: 30px;
background-color: white;
}
.qr-code-placeholder {
text-align: center;
}
.setup-steps {
position: relative;
}
.step-item {
display: flex;
align-items: center;
margin-bottom: 20px;
position: relative;
}
.step-item:not(:last-child)::after {
content: '';
position: absolute;
left: 15px;
top: 40px;
width: 2px;
height: 30px;
background-color: #dee2e6;
}
.step-item.completed::after {
background-color: #28a745;
}
.step-item.active::after {
background-color: #007bff;
}
.step-number {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #dee2e6;
color: #6c757d;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
margin-right: 15px;
flex-shrink: 0;
}
.step-item.active .step-number {
background-color: #007bff;
color: white;
}
.step-item.completed .step-number {
background-color: #28a745;
color: white;
}
.step-item.completed .step-number::before {
content: '✓';
font-size: 14px;
}
.step-item.completed .step-number {
font-size: 0;
}
.security-level,
.app-recommendation {
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.security-level:last-child,
.app-recommendation:last-child {
border-bottom: none;
}
@media (max-width: 768px) {
.device-type-card {
margin-bottom: 15px;
}
.setup-section {
margin-top: 15px;
padding: 15px;
}
.qr-code-container {
padding: 20px;
}
}
</style>
{% endblock %}