450 lines
22 KiB
HTML
450 lines
22 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Remove 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_detail' object.pk %}" class="me-3">
|
|
<i class="fas fa-arrow-left"></i>
|
|
</a>
|
|
Remove 2FA Device
|
|
</h4>
|
|
<h6>Confirm removal of two-factor authentication device</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row justify-content-center">
|
|
<div class="col-lg-8 col-md-10">
|
|
<!-- Warning Alert -->
|
|
<div class="alert alert-danger" role="alert">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-exclamation-triangle fa-2x me-3"></i>
|
|
<div>
|
|
<h5 class="alert-heading mb-1">Security Warning</h5>
|
|
<p class="mb-0">You are about to remove a two-factor authentication device. This will reduce your account security.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Device Information Card -->
|
|
<div class="card">
|
|
<div class="card-header bg-danger text-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-trash me-2"></i>
|
|
Device Removal Confirmation
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Device Details -->
|
|
<div class="device-summary mb-4">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-2 text-center">
|
|
<div class="device-icon-large">
|
|
{% if object.device_type == 'authenticator' %}
|
|
<i class="fas fa-mobile-alt fa-3x text-primary"></i>
|
|
{% elif object.device_type == 'hardware_key' %}
|
|
<i class="fas fa-key fa-3x text-success"></i>
|
|
{% elif object.device_type == 'sms' %}
|
|
<i class="fas fa-sms fa-3x text-warning"></i>
|
|
{% else %}
|
|
<i class="fas fa-shield-alt fa-3x text-info"></i>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-10">
|
|
<h5 class="mb-2">{{ object.name|default:"Google Authenticator" }}</h5>
|
|
<div class="device-details">
|
|
<div class="row">
|
|
<div class="col-sm-6">
|
|
<small class="text-muted">Device Type:</small>
|
|
<div class="mb-2">
|
|
{% if object.device_type == 'authenticator' %}
|
|
<span class="badge bg-primary">
|
|
<i class="fas fa-mobile-alt me-1"></i>Mobile App
|
|
</span>
|
|
{% elif object.device_type == 'hardware_key' %}
|
|
<span class="badge bg-success">
|
|
<i class="fas fa-key me-1"></i>Hardware Key
|
|
</span>
|
|
{% elif object.device_type == 'sms' %}
|
|
<span class="badge bg-warning">
|
|
<i class="fas fa-sms me-1"></i>SMS
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<small class="text-muted">Status:</small>
|
|
<div class="mb-2">
|
|
{% if object.is_active %}
|
|
<span class="badge bg-success">Active</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">Inactive</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<small class="text-muted">Added:</small>
|
|
<div>{{ object.created_at|default:"Jan 15, 2024"|date:"M d, Y" }}</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<small class="text-muted">Last Used:</small>
|
|
<div>{{ object.last_used_at|default:"Today 09:30 AM"|date:"M d, Y g:i A" }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Impact Assessment -->
|
|
<div class="impact-assessment mb-4">
|
|
<h6 class="text-danger mb-3">
|
|
<i class="fas fa-exclamation-circle me-2"></i>
|
|
Impact of Removing This Device
|
|
</h6>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="impact-item mb-3">
|
|
<div class="d-flex align-items-start">
|
|
<i class="fas fa-shield-alt text-danger me-3 mt-1"></i>
|
|
<div>
|
|
<strong>Reduced Security</strong>
|
|
<p class="text-muted small mb-0">Your account will have fewer authentication factors, making it more vulnerable.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="impact-item mb-3">
|
|
<div class="d-flex align-items-start">
|
|
<i class="fas fa-key text-warning me-3 mt-1"></i>
|
|
<div>
|
|
<strong>Backup Access</strong>
|
|
<p class="text-muted small mb-0">You'll lose this device as a backup authentication method.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="impact-item mb-3">
|
|
<div class="d-flex align-items-start">
|
|
<i class="fas fa-history text-info me-3 mt-1"></i>
|
|
<div>
|
|
<strong>Usage History</strong>
|
|
<p class="text-muted small mb-0">All usage history for this device will be preserved for audit purposes.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="impact-item mb-3">
|
|
<div class="d-flex align-items-start">
|
|
<i class="fas fa-undo text-secondary me-3 mt-1"></i>
|
|
<div>
|
|
<strong>Recovery</strong>
|
|
<p class="text-muted small mb-0">You can re-add this device type later if needed.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Checks -->
|
|
<div class="security-checks mb-4">
|
|
<h6 class="mb-3">
|
|
<i class="fas fa-check-circle me-2"></i>
|
|
Security Verification
|
|
</h6>
|
|
|
|
<!-- Check for remaining devices -->
|
|
<div class="alert alert-info">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-info-circle me-3"></i>
|
|
<div>
|
|
<strong>Remaining 2FA Devices:</strong>
|
|
<span class="ms-2">{{ remaining_devices_count|default:2 }} device(s)</span>
|
|
{% if remaining_devices_count == 0 %}
|
|
<div class="text-danger small mt-1">
|
|
<i class="fas fa-exclamation-triangle me-1"></i>
|
|
Warning: This is your last 2FA device. Removing it will disable 2FA on your account.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backup codes check -->
|
|
<div class="alert alert-warning">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-key me-3"></i>
|
|
<div>
|
|
<strong>Backup Codes:</strong>
|
|
{% if has_backup_codes %}
|
|
<span class="text-success ms-2">Available</span>
|
|
<div class="small mt-1">You have backup codes that can be used for account recovery.</div>
|
|
{% else %}
|
|
<span class="text-danger ms-2">Not Generated</span>
|
|
<div class="small mt-1">Consider generating backup codes before removing this device.</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Confirmation Form -->
|
|
<form method="post" id="deleteForm">
|
|
{% csrf_token %}
|
|
|
|
<!-- Confirmation Checkbox -->
|
|
<div class="confirmation-section mb-4">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="confirm_removal" name="confirm_removal" required>
|
|
<label class="form-check-label" for="confirm_removal">
|
|
<strong>I understand the security implications and want to remove this device</strong>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reason for Removal -->
|
|
<div class="form-group mb-4">
|
|
<label for="removal_reason" class="form-label">Reason for Removal (Optional)</label>
|
|
<select class="form-select" id="removal_reason" name="removal_reason">
|
|
<option value="">Select a reason...</option>
|
|
<option value="lost_device">Lost or stolen device</option>
|
|
<option value="replaced_device">Replaced with new device</option>
|
|
<option value="no_longer_needed">No longer needed</option>
|
|
<option value="technical_issues">Technical issues</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Additional Notes -->
|
|
<div class="form-group mb-4">
|
|
<label for="removal_notes" class="form-label">Additional Notes (Optional)</label>
|
|
<textarea class="form-control" id="removal_notes" name="removal_notes" rows="3"
|
|
placeholder="Any additional information about this removal..."></textarea>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{% url 'accounts:two_factor_device_detail' object.pk %}" class="btn btn-secondary">
|
|
<i class="fas fa-times me-1"></i>Cancel
|
|
</a>
|
|
<div>
|
|
{% if remaining_devices_count > 0 %}
|
|
<button type="button" class="btn btn-outline-primary me-2" onclick="generateBackupCodes()">
|
|
<i class="fas fa-key me-1"></i>Generate Backup Codes First
|
|
</button>
|
|
{% endif %}
|
|
<button type="submit" class="btn btn-danger" id="confirmDeleteBtn" disabled>
|
|
<i class="fas fa-trash me-1"></i>Remove Device
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Alternative Actions -->
|
|
<div class="card mt-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-lightbulb me-2"></i>
|
|
Alternative Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="alternative-action">
|
|
<h6>
|
|
<i class="fas fa-pause-circle text-warning me-2"></i>
|
|
Temporarily Disable
|
|
</h6>
|
|
<p class="text-muted small">Disable the device without removing it completely. You can reactivate it later.</p>
|
|
<button type="button" class="btn btn-outline-warning btn-sm" onclick="disableDevice()">
|
|
<i class="fas fa-pause me-1"></i>Disable Instead
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="alternative-action">
|
|
<h6>
|
|
<i class="fas fa-sync text-info me-2"></i>
|
|
Reset Configuration
|
|
</h6>
|
|
<p class="text-muted small">Reset the device configuration while keeping the device registered.</p>
|
|
<button type="button" class="btn btn-outline-info btn-sm" onclick="resetDevice()">
|
|
<i class="fas fa-sync me-1"></i>Reset Instead
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Enable/disable submit button based on confirmation checkbox
|
|
const confirmCheckbox = document.getElementById('confirm_removal');
|
|
const submitButton = document.getElementById('confirmDeleteBtn');
|
|
|
|
confirmCheckbox.addEventListener('change', function() {
|
|
submitButton.disabled = !this.checked;
|
|
});
|
|
|
|
// Form submission confirmation
|
|
const deleteForm = document.getElementById('deleteForm');
|
|
deleteForm.addEventListener('submit', function(e) {
|
|
if (!confirm('Are you absolutely sure you want to remove this 2FA device? This action cannot be undone.')) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
});
|
|
|
|
function generateBackupCodes() {
|
|
if (confirm('Generate backup codes before removing this device? This is recommended for account security.')) {
|
|
// Redirect to backup codes generation
|
|
window.location.href = '{% url "accounts:generate_backup_codes" %}';
|
|
}
|
|
}
|
|
|
|
function disableDevice() {
|
|
if (confirm('Disable this device instead of removing it? You can reactivate it later.')) {
|
|
// Submit disable request
|
|
fetch('{% url "accounts:two_factor_device_disable" object.pk %}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Device disabled successfully.');
|
|
window.location.href = '{% url "accounts:two_factor_device_list" %}';
|
|
} else {
|
|
alert('Error disabling device: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error disabling device. Please try again.');
|
|
});
|
|
}
|
|
}
|
|
|
|
function resetDevice() {
|
|
if (confirm('Reset this device configuration? You will need to set it up again.')) {
|
|
// Submit reset request
|
|
fetch('{% url "accounts:two_factor_device_reset" object.pk %}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Device reset successfully. Please reconfigure it.');
|
|
window.location.href = '{% url "accounts:two_factor_device_update" object.pk %}';
|
|
} else {
|
|
alert('Error resetting device: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error resetting device. Please try again.');
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.device-icon-large {
|
|
padding: 20px;
|
|
background: #f8f9fa;
|
|
border-radius: 10px;
|
|
display: inline-block;
|
|
}
|
|
|
|
.device-summary {
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.impact-item {
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.security-checks .alert {
|
|
border-left: 4px solid;
|
|
}
|
|
|
|
.security-checks .alert-info {
|
|
border-left-color: #17a2b8;
|
|
}
|
|
|
|
.security-checks .alert-warning {
|
|
border-left-color: #ffc107;
|
|
}
|
|
|
|
.confirmation-section {
|
|
border: 2px solid #dc3545;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
background-color: #fff5f5;
|
|
}
|
|
|
|
.alternative-action {
|
|
padding: 15px;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.alternative-action h6 {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.device-summary .row {
|
|
text-align: center;
|
|
}
|
|
|
|
.device-icon-large {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.impact-assessment .row {
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.d-flex.justify-content-between {
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
|
|
.d-flex.justify-content-between > div {
|
|
text-align: center;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|