{% extends "base.html" %} {% from "components/ui.html" import ago, card, copy_button, csrf_field, dialog_button, dialog_buttons, empty, form_dialog, icon, related, row_form, tick, tick_all, toggle with context %} {% block title %}Second factor ยท {{ APP_NAME }}{% endblock %} {% block heading %}Second factor{% endblock %} {% block subheading %}
A second step after your password, when you sign in to this UI
{% endblock %} {% block content %} {% set devices = factor.totp %} {% if codes %}

Recovery codes

shown this once

Each signs you in once in place of a code of a device, e.g. when a phone is lost. Keep them apart from your devices, in a password manager or on paper.

    {% for code in codes %}
  • {{ code }}
  • {% endfor %}
{{ copy_button("recovery-codes", "Copy all codes") }}
{% endif %} {% if qr %} {% call card("Scan the code for " ~ setup.name, meta="With an authenticator app, within " ~ minutes ~ " minutes.") %} QR code of the device {{ setup.name }}

Where the camera cannot reach, type in this key:

{{ secret }}{{ copy_button("totp-key", "Copy the key") }}
{{ csrf_field() }}
{{ csrf_field() }}
{% endcall %} {% endif %} {% set can_add = not qr and devices | length < max_devices %} {% call related("Authenticator app (TOTP)", count=devices | length, meta=("devices: a code of any of them signs in" if devices else "off now: your password alone signs you in"), add=("Add a device" if can_add else None), add_form="add-device", add_open=not devices) %} {% if devices | length > 1 %}
Ticked devices {{ dialog_button("remove-ticked", "Remove", alone=False) }}
{% endif %}
{% if devices | length > 1 %}{% endif %} {% set columns = 5 if devices | length > 1 else 4 %} {% if can_add %} {% call row_form("add-device", columns, open=not devices) %} {{ csrf_field() }}
{% if devices %}
{% endif %}
The next step shows its QR code.{% if not devices %} Then ten recovery codes, once.{% endif %} Adding a device signs out your other sessions.
{% if devices %}{% endif %}
{% endcall %} {% endif %} {% for device in devices %} {% if devices | length > 1 %}{% endif %} {% call row_form("rename-" ~ device.id, columns) %} {{ csrf_field() }}
{% endcall %} {% endfor %}
{{ tick_all("devices-batch", "device") }}NameAddedLast usedActions
{{ tick("devices-batch", "device", device.id, "Tick " ~ device.name) }}{{ device.name }} {{ ago(device.created_at) }} {{ ago(device.last_used_at) }} {{ toggle("rename-" ~ device.id, "Rename", "pencil", name="Rename " ~ device.name) }} {{ dialog_button("remove-" ~ device.id, "Remove", name="Remove " ~ device.name) }}
{% if not devices %}{{ empty("No device yet. Any authenticator app works: add a phone or a tablet, scan its QR code, and keep the ten recovery codes the first one brings.") }}{% endif %}
{% endcall %} {% macro confirm_fields(id) %}
{% endmacro %} {% for device in devices %} {% call form_dialog("remove-" ~ device.id, "Remove " ~ device.name ~ "?", "Its codes work no more." ~ (" It is the last one: the second factor is off then, and the recovery codes go with it." if devices | length == 1 else "")) %}
{{ csrf_field() }} {{ confirm_fields("remove-" ~ device.id) }} {{ dialog_buttons("Remove device") }}
{% endcall %} {% endfor %} {% if devices | length > 1 %} {% call form_dialog("remove-ticked", "Remove the ticked devices?", "Their codes work no more. With every device ticked, the second factor is off, and the recovery codes go with it.") %}
{{ csrf_field() }} {{ confirm_fields("remove-ticked") }} {{ dialog_buttons("Remove devices") }}
{% endcall %} {% endif %} {% if factor.totp %} {% call card("Recovery codes", meta=factor.recovery_codes_left ~ " left. New ones replace them all.") %} {% if factor.recovery_codes_left == 0 %}
No recovery codes are left. Make new ones.
{% elif factor.recovery_codes_left <= 2 %}
Few recovery codes are left. Make new ones.
{% endif %}
{{ csrf_field() }}
{% endcall %} {% endif %} {% endblock %}