OXYGENHUB
# 8297a33b9a4c oxygenhub / templates / settings_security.html
6.1 KiB · HTML · last changed A phone as the second factor: Keystore keys approve sign-ins by the number the browser shows, and the sudo step asks a stale session again before sensitive changes (closes #356) 4 days ago source blame history raw
19bb31ff Andy McCall ↶ 1
{% extends "base.html" %}
2
{% import "settings_tabs.html" as t %}
3
{% block title %}Security{% endblock %}
4
{% block tabs %}{% call t::tabs("security") %}{% endblock %}
5
{% block content %}
6
<div class="head"><h1>Security <small>two-factor authentication and sessions</small></h1></div>
7
{% if let Some(n) = notice %}<p class="note">{{ n }}</p>{% endif %}
8
{% if let Some(e) = error %}<p class="error">{{ e }}</p>{% endif %}
9
{% if !recovery.is_empty() %}
10
<div class="panel"><h2>Recovery codes</h2><ul class="codes">{% for c in recovery %}<li>{{ c }}</li>{% endfor %}</ul></div>
11
{% endif %}
8597fb5c Andy McCall ↶ 12
<section class="card-box" id="two-factor">
13
  <div class="card-head"><h2>Two-factor authentication</h2>{% if enrol.is_some() %}<span class="small mute more">setting up</span>{% else if me.totp_enabled() %}<span class="tag ok more"><span class="sig sig-valid">✔</span> on</span>{% else %}<span class="tag more"><span class="sig sig-unsigned">○</span> off</span>{% endif %}</div>
14
  {% if let Some((secret, uri, qr)) = enrol %}
15
  <form class="pad sec-form" method="post" action="/settings/security/totp/enable">
16
    <input type="hidden" name="_csrf" value="{{ shell.csrf }}">
17
    <input type="hidden" name="secret" value="{{ secret }}">
18
    <p>Scan this with your authenticator app, then enter the code it shows.</p>
19
    <div class="qr">{{ qr|safe }}</div>
20
    <p class="small mute">Or enter the secret by hand: <code>{{ secret }}</code></p>
21
    <div class="sec-row"><label for="code">Code from the app</label><input type="text" id="code" name="code" inputmode="numeric" autocomplete="one-time-code" autofocus required><button type="submit">TURN ON</button></div>
22
  </form>
23
  {% else if me.totp_enabled() %}
24
  <form class="pad sec-form" method="post" action="/settings/security/totp/disable">
25
    <input type="hidden" name="_csrf" value="{{ shell.csrf }}">
26
    <p class="small mute">A code from your authenticator app is asked for at every sign in.</p>
27
    <div class="sec-row"><label for="password">Your password, to turn it off</label><input type="password" id="password" name="password" autocomplete="current-password" required><button type="submit" class="danger">TURN OFF</button></div>
28
  </form>
29
  {% else %}
30
  <form class="pad sec-form sec-split" method="post" action="/settings/security/totp/begin">
31
    <input type="hidden" name="_csrf" value="{{ shell.csrf }}">
32
    <p class="small mute">With it on, a time-based code from an authenticator app is asked for at every sign in.</p>
33
    <button type="submit">SET UP</button>
34
  </form>
35
  {% endif %}
36
</section>
a22e4aec Andy McCall ↶ 37
{% if me.totp_enabled() %}<section class="card-box" id="phones">
38
  <div class="card-head"><h2>Phones</h2><span class="count-chip">{{ phones.len() }}</span><span class="small mute more">approve with a fingerprint instead of typing a code</span></div>
39
  {% if phones.is_empty() %}<p class="empty small">No phone approves for you yet. In the OxygenHub app, Account › <strong>Use this phone as a second factor</strong>.</p>{% else %}
40
  <ul class="rows token-rows">{% for p in phones %}
41
    <li>
42
      <div class="token-main">
43
        <div class="token-name"><strong>{{ p.name }}</strong></div>
44
        <div class="small mute">added <time title="{{ p.created_ms|when_i }}">{{ p.created_ms|ago_i }}</time> · {% if p.last_used_ms > 0 %}last approved <time title="{{ p.last_used_ms|when_i }}">{{ p.last_used_ms|ago_i }}</time>{% else %}never used{% endif %}</div>
45
      </div>
46
      <div class="token-actions"><form class="inline" method="post" action="/settings/security/phones/{{ p.token_id }}/remove"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button class="small">Remove</button></form></div>
47
    </li>{% endfor %}
48
  </ul>{% endif %}
49
  <p class="small mute pad sec-foot">Signing in, a phone shows three numbers; pick the one this page shows and confirm with your fingerprint, face or PIN. Your authenticator app's codes still work when the phone is not to hand.</p>
50
</section>{% endif %}
8597fb5c Andy McCall ↶ 51
{% if !connected.is_empty() %}<section class="card-box" id="connected">
52
  <div class="card-head"><h2>Connected accounts</h2><span class="small mute more">sign in with these instead of a password</span></div>
53
  <ul class="rows token-rows">{% for (id, name, login) in connected %}
54
    <li>
55
      <div class="token-main"><div class="token-name"><strong>{{ name }}</strong>{% if let Some(l) = login %} <span class="mute">{{ l }}</span>{% else %} <span class="small mute">not connected</span>{% endif %}</div></div>
56
      <div class="token-actions">{% if login.is_some() %}<form class="inline" method="post" action="/settings/security/disconnect/{{ id }}"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button class="small">Disconnect</button></form>{% else %}<a class="go small" href="/settings/security/connect/{{ id }}">Connect</a>{% endif %}</div>
57
    </li>{% endfor %}
58
  </ul>
0024ccb1 Andy McCall ↶ 59
</section>{% endif %}
8597fb5c Andy McCall ↶ 60
<section class="card-box" id="sessions">
61
  <div class="card-head"><h2>Signed in</h2><span class="count-chip">{{ sessions.len() }}</span>
62
    <form class="inline more" method="post" action="/settings/security/sessions"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button class="small" title="Sign out every browser except this one">Sign out everywhere else</button></form></div>
63
  {% if sessions.is_empty() %}<p class="empty small">No sessions.</p>{% else %}
64
  <ul class="rows token-rows">{% for x in sessions %}
65
    <li>
66
      <div class="token-main">
67
        <div class="token-name"><strong>{{ x.browser }}</strong>{% if x.current %} <span class="tag">this browser</span>{% endif %}</div>
68
        <div class="small mute">{% if !x.address.is_empty() %}{{ x.address }} · {% endif %}used <time title="{{ x.seen_ms|when_i }}">{{ x.seen_ms|ago_i }}</time> · signed in <time title="{{ x.created_ms|when_i }}">{{ x.created_ms|ago_i }}</time></div>
69
      </div>
70
      <div class="token-actions"><form class="inline" method="post" action="/settings/security/sessions/{{ x.id }}/end"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button class="small">Sign out</button></form></div>
71
    </li>{% endfor %}
72
  </ul>{% endif %}
73
  <p class="small mute pad sec-foot">A session ends by itself after 14 days unused, or 30 days after signing in.</p>
74
</section>
19bb31ff Andy McCall ↶ 75
{% endblock %}