{% extends "base_settings_page.html" %} {% block content %} <div class="alert alert-dismissible alert-warning"> <h4 class="alert-heading">Warning!</h4> <p class="mb-0">Once a new factor has been added to your account, you can not access your account anymore using only your password. If you remove all your second factors, 2 Factor Authentication is automatically disabled for your account.</p> </div> <p> <a href="/settings/two_factors/add_totp" type="button" class="btn btn-primary">Add Authenticator App</a> <a href="/settings/two_factors/add_webauthn" type="button" class="btn btn-primary">Add Security Key</a> </p> <table class="table table-hover" style="max-width: 800px;" aria-describedby="Factors list"> <thead> <tr> <th scope="col">Factor type</th> <th scope="col">Name</th> <th scope="col">Actions</th> </tr> </thead> <tbody> {% for f in user.two_factor %} <tr id="factor-{{ f.id.0 }}"> <td><img src="{{ f.type_image() }}" alt="Factor icon" style="height: 1.5em; margin-right: 0.5em;"/>{{ f.type_str() }} </td> <td>{{ f.name }}</td> <td><a href="javascript:delete_factor('{{ f.id.0 }}');">Delete</a></td> </tr> {% endfor %} </tbody> </table> {% if !user.last_successful_2fa.is_empty() %} <div id="2fa_history_container"> <h5 style="margin-top: 50px">Successful 2FA login history</h5> <p> <a type="button" class="btn btn-danger btn-sm" onclick="clear_login_history()">Clear history</a> </p> <table class="table table-hover" style="max-width: 800px;" aria-describedby="Factors list"> <thead> <tr> <th scope="col">IP address</th> <th scope="col">Location</th> <th scope="col">Date</th> <th scope="col">Bypass 2FA</th> </tr> </thead> <tbody> {% for e in user.get_formatted_2fa_successful_logins() %} <tr> <td>{{ e.ip }}</td> <td> <locateip ip="{{ e.ip }}"></locateip> </td> <td>{{ e.fmt_time() }}</td> <td>{% if e.can_bypass_2fa %}YES{% else %}NO{% endif %}</td> </tr> {% endfor %} </tbody> </table> {% endif %} </div> {% if let Some(last_2fa_auth) = last_2fa_auth %} <p>Last successful 2FA authentication on this browser: {{ last_2fa_auth }}</p> {% endif %} <script> async function delete_factor(id) { if (!confirm("Do you really want to remove this factor?")) return; try { const res = await fetch("/settings/api/two_factor/delete_factor", { method: "post", headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ id: id, }) }); let text = await res.text(); alert(text); if (res.status == 200) document.getElementById("factor-" + id).remove(); } catch (e) { console.error(e); alert("Failed to remove factor!"); } } async function clear_login_history() { if (!confirm("Do you really want to clear your 2FA login history?")) return; try { const res = await fetch("/settings/api/two_factor/clear_login_history", { method: "post" }); let text = await res.text(); alert(text); if (res.status == 200) document.getElementById("2fa_history_container").remove(); } catch (e) { console.error(e); alert("Failed to clear 2FA history!"); } } </script> {% endblock content %}