| 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 %} |
| 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> |
| 37 |
{% if !connected.is_empty() %}<section class="card-box" id="connected"> |
| 38 |
<div class="card-head"><h2>Connected accounts</h2><span class="small mute more">sign in with these instead of a password</span></div> |
| 39 |
<ul class="rows token-rows">{% for (id, name, login) in connected %} |
| 40 |
<li> |
| 41 |
<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> |
| 42 |
<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> |
| 43 |
</li>{% endfor %} |
| 44 |
</ul> |
| 45 |
</section>{% endif %} |
| 46 |
<section class="card-box" id="sessions"> |
| 47 |
<div class="card-head"><h2>Signed in</h2><span class="count-chip">{{ sessions.len() }}</span> |
| 48 |
<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> |
| 49 |
{% if sessions.is_empty() %}<p class="empty small">No sessions.</p>{% else %} |
| 50 |
<ul class="rows token-rows">{% for x in sessions %} |
| 51 |
<li> |
| 52 |
<div class="token-main"> |
| 53 |
<div class="token-name"><strong>{{ x.browser }}</strong>{% if x.current %} <span class="tag">this browser</span>{% endif %}</div> |
| 54 |
<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> |
| 55 |
</div> |
| 56 |
<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> |
| 57 |
</li>{% endfor %} |
| 58 |
</ul>{% endif %} |
| 59 |
<p class="small mute pad sec-foot">A session ends by itself after 14 days unused, or 30 days after signing in.</p> |
| 60 |
</section> |
| 61 |
{% endblock %} |