521 lines
22 KiB
HTML
521 lines
22 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Two-Factor Authentication Devices - 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>Two-Factor Authentication</h4>
|
|
<h6>Manage your 2FA devices for enhanced account security</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<a href="{% url 'accounts:two_factor_device_create' %}" class="btn btn-added">
|
|
<i class="fas fa-plus me-1"></i>Add New Device
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Status Cards -->
|
|
<div class="row">
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-shield-alt"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.total_devices|default:3 }}</h5>
|
|
<h6>Total Devices</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-check-circle"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.active_devices|default:2 }}</h5>
|
|
<h6>Active Devices</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-mobile-alt"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.mobile_devices|default:2 }}</h5>
|
|
<h6>Mobile Apps</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-key"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.hardware_keys|default:1 }}</h5>
|
|
<h6>Hardware Keys</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Recommendations -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-lightbulb me-2"></i>
|
|
Security Recommendations
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="security-tip">
|
|
<i class="fas fa-mobile-alt text-primary fa-2x mb-2"></i>
|
|
<h6>Use Authenticator Apps</h6>
|
|
<p class="text-muted small">Apps like Google Authenticator or Authy provide secure time-based codes.</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="security-tip">
|
|
<i class="fas fa-key text-success fa-2x mb-2"></i>
|
|
<h6>Hardware Security Keys</h6>
|
|
<p class="text-muted small">Physical keys like YubiKey offer the highest level of security.</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="security-tip">
|
|
<i class="fas fa-copy text-warning fa-2x mb-2"></i>
|
|
<h6>Backup Codes</h6>
|
|
<p class="text-muted small">Keep backup codes in a safe place for account recovery.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2FA Devices List -->
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="table-top">
|
|
<div class="search-set">
|
|
<div class="search-input">
|
|
<a class="btn btn-searchset">
|
|
<i class="fas fa-search"></i>
|
|
</a>
|
|
<input type="text" id="searchInput" placeholder="Search devices...">
|
|
</div>
|
|
</div>
|
|
<div class="wordset">
|
|
<ul>
|
|
<li>
|
|
<a data-bs-toggle="tooltip" data-bs-placement="top" title="Export" onclick="exportDevices()">
|
|
<i class="fas fa-download"></i>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a data-bs-toggle="tooltip" data-bs-placement="top" title="Print" onclick="printDevices()">
|
|
<i class="fas fa-print"></i>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Devices Table -->
|
|
<div class="table-responsive">
|
|
<table class="table" id="devicesTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Device</th>
|
|
<th>Type</th>
|
|
<th>Status</th>
|
|
<th>Added</th>
|
|
<th>Last Used</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- Sample Data -->
|
|
<tr>
|
|
<td>
|
|
<div class="deviceinfo">
|
|
<div class="device-icon me-3">
|
|
<i class="fas fa-mobile-alt fa-2x text-primary"></i>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">Google Authenticator</h6>
|
|
<small class="text-muted">Primary mobile device</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-primary">
|
|
<i class="fas fa-mobile-alt me-1"></i>Mobile App
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="badges bg-lightgreen">
|
|
<i class="fas fa-check-circle me-1"></i>Active
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
<strong>Jan 15, 2024</strong>
|
|
<small class="d-block text-muted">3 months ago</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
<strong>Today 09:30 AM</strong>
|
|
<small class="d-block text-muted">2 hours ago</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<a class="me-3" href="{% url 'accounts:two_factor_device_detail' 1 %}" data-bs-toggle="tooltip" title="View Details">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<a class="me-3" href="#" data-bs-toggle="tooltip" title="Test Device" onclick="testDevice(1)">
|
|
<i class="fas fa-vial"></i>
|
|
</a>
|
|
<a class="confirm-text" href="{% url 'accounts:two_factor_device_delete' 1 %}" data-bs-toggle="tooltip" title="Remove Device">
|
|
<i class="fas fa-trash"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<div class="deviceinfo">
|
|
<div class="device-icon me-3">
|
|
<i class="fas fa-key fa-2x text-success"></i>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">YubiKey 5 NFC</h6>
|
|
<small class="text-muted">Hardware security key</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-success">
|
|
<i class="fas fa-key me-1"></i>Hardware Key
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="badges bg-lightgreen">
|
|
<i class="fas fa-check-circle me-1"></i>Active
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
<strong>Dec 10, 2023</strong>
|
|
<small class="d-block text-muted">4 months ago</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
<strong>Yesterday 02:15 PM</strong>
|
|
<small class="d-block text-muted">1 day ago</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<a class="me-3" href="{% url 'accounts:two_factor_device_detail' 2 %}" data-bs-toggle="tooltip" title="View Details">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<a class="me-3" href="#" data-bs-toggle="tooltip" title="Test Device" onclick="testDevice(2)">
|
|
<i class="fas fa-vial"></i>
|
|
</a>
|
|
<a class="confirm-text" href="{% url 'accounts:two_factor_device_delete' 2 %}" data-bs-toggle="tooltip" title="Remove Device">
|
|
<i class="fas fa-trash"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<div class="deviceinfo">
|
|
<div class="device-icon me-3">
|
|
<i class="fas fa-sms fa-2x text-warning"></i>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">SMS Backup</h6>
|
|
<small class="text-muted">+1 (555) ***-1234</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-warning">
|
|
<i class="fas fa-sms me-1"></i>SMS
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="badges bg-lightyellow">
|
|
<i class="fas fa-pause-circle me-1"></i>Backup Only
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
<strong>Jan 15, 2024</strong>
|
|
<small class="d-block text-muted">3 months ago</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
<strong>Never</strong>
|
|
<small class="d-block text-muted">Backup method</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<a class="me-3" href="{% url 'accounts:two_factor_device_detail' 3 %}" data-bs-toggle="tooltip" title="View Details">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<a class="me-3" href="#" data-bs-toggle="tooltip" title="Test Device" onclick="testDevice(3)">
|
|
<i class="fas fa-vial"></i>
|
|
</a>
|
|
<a class="confirm-text" href="{% url 'accounts:two_factor_device_delete' 3 %}" data-bs-toggle="tooltip" title="Remove Device">
|
|
<i class="fas fa-trash"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
<div id="emptyState" class="text-center py-5" style="display: none;">
|
|
<i class="fas fa-shield-alt fa-3x text-muted mb-3"></i>
|
|
<h5>No 2FA Devices Found</h5>
|
|
<p class="text-muted">Enhance your account security by adding two-factor authentication devices.</p>
|
|
<a href="{% url 'accounts:two_factor_device_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-1"></i>Add Your First Device
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Tips -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
Security Best Practices
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<ul class="list-unstyled">
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Use multiple 2FA methods for redundancy
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Keep backup codes in a secure location
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Regularly test your 2FA devices
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<ul class="list-unstyled">
|
|
<li class="mb-2">
|
|
<i class="fas fa-times text-danger me-2"></i>
|
|
Don't rely solely on SMS for 2FA
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-times text-danger me-2"></i>
|
|
Don't share your 2FA codes with anyone
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-times text-danger me-2"></i>
|
|
Don't screenshot or email backup codes
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize search functionality
|
|
const searchInput = document.getElementById('searchInput');
|
|
searchInput.addEventListener('keyup', function() {
|
|
filterDevices();
|
|
});
|
|
|
|
// Initialize tooltips
|
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
|
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
|
});
|
|
|
|
// Check if table is empty
|
|
checkEmptyState();
|
|
});
|
|
|
|
function filterDevices() {
|
|
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
const table = document.getElementById('devicesTable');
|
|
const rows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
|
let visibleRows = 0;
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
|
const row = rows[i];
|
|
const deviceName = row.cells[0].textContent.toLowerCase();
|
|
const deviceType = row.cells[1].textContent.toLowerCase();
|
|
|
|
if (deviceName.includes(searchTerm) || deviceType.includes(searchTerm)) {
|
|
row.style.display = '';
|
|
visibleRows++;
|
|
} else {
|
|
row.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Show/hide empty state
|
|
const emptyState = document.getElementById('emptyState');
|
|
const tableContainer = table.closest('.table-responsive');
|
|
|
|
if (visibleRows === 0) {
|
|
tableContainer.style.display = 'none';
|
|
emptyState.style.display = 'block';
|
|
} else {
|
|
tableContainer.style.display = 'block';
|
|
emptyState.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function checkEmptyState() {
|
|
const table = document.getElementById('devicesTable');
|
|
const rows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
|
|
|
if (rows.length === 0) {
|
|
const emptyState = document.getElementById('emptyState');
|
|
const tableContainer = table.closest('.table-responsive');
|
|
tableContainer.style.display = 'none';
|
|
emptyState.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
function testDevice(deviceId) {
|
|
// Show loading state
|
|
const button = event.target.closest('a');
|
|
const originalIcon = button.innerHTML;
|
|
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
|
button.style.pointerEvents = 'none';
|
|
|
|
// Simulate test
|
|
setTimeout(() => {
|
|
alert('Test code sent to your device. Please check and verify.');
|
|
button.innerHTML = originalIcon;
|
|
button.style.pointerEvents = 'auto';
|
|
}, 2000);
|
|
}
|
|
|
|
function exportDevices() {
|
|
console.log('Exporting 2FA devices...');
|
|
alert('Export functionality would be implemented here.');
|
|
}
|
|
|
|
function printDevices() {
|
|
console.log('Printing 2FA devices...');
|
|
window.print();
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.deviceinfo {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.device-icon {
|
|
width: 50px;
|
|
text-align: center;
|
|
}
|
|
|
|
.security-tip {
|
|
text-align: center;
|
|
padding: 20px;
|
|
}
|
|
|
|
.security-tip i {
|
|
display: block;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.dash-widget {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.dash-widget .dash-widgetimg span {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
color: white;
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 24px;
|
|
}
|
|
|
|
.dash-widget .dash-widgetcontent h5 {
|
|
color: white;
|
|
font-size: 28px;
|
|
font-weight: 600;
|
|
margin: 10px 0 5px 0;
|
|
}
|
|
|
|
.dash-widget .dash-widgetcontent h6 {
|
|
color: rgba(255, 255, 255, 0.8);
|
|
font-size: 14px;
|
|
margin: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.deviceinfo {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
|
|
.device-icon {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.security-tip {
|
|
padding: 15px;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|