{% extends 'base.html' %}

{# Two-factor auth can be in three states:

mfa_init: The user has not setup any two-factor methods or recovery codes
mfa_setup: The user has setup recovery codes but no two-factor methods. Two-factor authentication is still disabled.
mfa_enabled: The user has setup at least one two-factor method. Two-factor authentication is enabled.

#}

{% set mfa_enabled = request.user.mfa_enabled %}
{% set mfa_init = not request.user.mfa_recovery_codes and not mfa_enabled %}
{% set mfa_setup = request.user.mfa_recovery_codes and not mfa_enabled %}

{% block body %}
<p>
	{% if mfa_enabled %}
	{{ _("Two-factor authentication is currently <strong>enabled</strong>.")|safe }}
	{% else %}
	{{ _("Two-factor authentication is currently <strong>disabled</strong>.")|safe }}
	{% endif %}
{% if mfa_init %}
	{{_("You need to generate recovery codes and setup at least one authentication method to enable two-factor authentication.")}}
{% elif mfa_setup %}
	{{_("You need to setup at least one authentication method to enable two-factor authentication.")}}
{% endif %}
</p>
{% if mfa_setup or mfa_enabled %}
<div class="clearfix">
	{% if mfa_enabled %}
	<form class="form float-right" action="{{ url_for('mfa.disable') }}">
		<button type="submit" class="btn btn-danger mb-2">{{_("Disable two-factor authentication")}}</button>
	</form>
	{% else %}
	<form class="form float-right" action="{{ url_for('mfa.disable_confirm') }}" method="POST">
		<button type="submit" class="btn btn-light mb-2">{{_("Reset two-factor configuration")}}</button>
	</form>
	{% endif %}
</div>
{% endif %}

<hr>

<div class="row mt-3">
	<div class="col-12 col-md-5">
		<h4>{{_("Recovery Codes")}}</h4>
		<p>
			{{_("Recovery codes allow you to login and setup new two-factor methods when you lost your registered second factor.")}}
		</p>
		<p>
			{% if mfa_init %}<strong>{% endif %}
			{{_("You need to setup recovery codes before you can setup up authenticator apps or U2F/FIDO2 devices.")}}
			{% if mfa_init %}</strong>{% endif %}
			{{_("Each code can only be used once.")}}
		</p>
	</div>

	<div class="col-12 col-md-7">
		<form class="form" action="{{ url_for('mfa.setup_recovery') }}" method="POST">
			{% if mfa_init %}
			<button type="submit" class="btn btn-primary mb-2 col">
				{{_("Generate recovery codes to enable two-factor authentication")}}
			</button>
			{% else %}
			<button type="submit" class="btn btn-primary mb-2 col">
				{{_("Generate new recovery codes")}}
			</button>
			{% endif %}
		</form>

		{% if request.user.mfa_recovery_codes %}
		<p>{{ request.user.mfa_recovery_codes|length }} recovery codes remain</p>
		{% elif not request.user.mfa_recovery_codes and mfa_enabled %}
		<p>
			<strong>{{_("You have no remaining recovery codes.")}}</strong>
		</p>
		{% endif %}
	</div>
</div>

<hr>

<div class="row mt-3">
	<div class="col-12 col-md-5">
		<h4>{{_("Authenticator Apps (TOTP)")}}</h4>
		<p>
			{{_("Use an authenticator application on your mobile device as a second factor.")}}
		</p>
		<p>
			{{_("The authenticator app generates a 6-digit one-time code each time you login.
			Compatible apps are freely available for most phones.")}}
		</p>
	</div>

	<div class="col-12 col-md-7">
		<form class="form mb-2" action="{{ url_for('mfa.setup_totp') }}">
			<div class="row m-0">
				<label class="sr-only" for="totp-name">{{_("Name")}}</label>
				<input type="text" name="name" class="form-control mb-2 col-12 col-lg-auto mr-2" style="width: 15em;" id="totp-name" placeholder="{{_("Name")}}" required {{ 'disabled' if mfa_init }}>
				<button type="submit" id="totp-submit" class="btn btn-primary mb-2 col" {{ 'disabled' if mfa_init }}>{{_("Setup new app")}}</button>
			</div>
		</form>

		<table class="table">
			<thead>
				<tr>
					<th scope="col">{{_("Name")}}</th>
					<th scope="col">{{_("Registered On")}}</th>
					<th scope="col"></th>
				</tr>
			</thead>
			<tbody>
				{% for method in request.user.mfa_totp_methods %}
				<tr>
					<td>{{ method.name }}</td>
					<td>{{ method.created.strftime('%b %d, %Y') }}</td>
					<td><a class="btn btn-sm btn-danger float-right" href="{{ url_for('mfa.delete_totp', id=method.id) }}">{{_("Delete")}}</a></td>
				</tr>
				{% endfor %}
				{% if not request.user.mfa_totp_methods %}
				<tr class="table-secondary">
					<td colspan=3 class="text-center">{{_("No authenticator apps registered yet")}}</td>
				</tr>
				{% endif %}
			</tbody>
		</table>
	</div>
</div>

<hr>

<div class="row">
	<div class="col-12 col-md-5">
		<h4>{{_("U2F and FIDO2 Devices")}}</h4>
		<p>
			{{_("Use an U2F or FIDO2 compatible hardware security key as a second factor.")}}
		</p>
		<p>
			{{_("U2F and FIDO2 devices are not supported by all browsers and can be particularly difficult to use on mobile
			devices. <strong>It is strongly recommended to also setup an authenticator app</strong> to be able to login on all
			browsers.")}}
		</p>
	</div>

	<div class="col-12 col-md-7">
		{% if not webauthn_supported %}
		<div class="alert alert-warning" role="alert">{{_("U2F/FIDO2 support not enabled")}}</div>
		{% endif %}
		<noscript>
			<div class="alert alert-warning" role="alert">
				{{_("Enable javascript in your browser to use U2F and FIDO2 devices!")}}
			</div>
		</noscript>
		<div id="webauthn-alert" class="alert alert-warning d-none" role="alert"></div>
		<form id="webauthn-form" class="form mb-2">
			<div class="row m-0">
				<label class="sr-only" for="webauthn-name">{{_("Name")}}</label>
				<input type="text" class="form-control mb-2 col-12 col-lg-auto mr-2" style="width: 15em;" id="webauthn-name" placeholder="{{_("Name")}}" required disabled>
				<button type="submit" id="webauthn-btn" class="btn btn-primary mb-2 col" disabled>
					<span id="webauthn-spinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
					<span id="webauthn-btn-text">{{_("Setup new device")}}</span>
				</button>
			</div>
		</form>

		<table class="table">
			<thead>
				<tr>
					<th scope="col">{{_("Name")}}</th>
					<th scope="col">{{_("Registered On")}}</th>
					<th scope="col"></th>
				</tr>
			</thead>
			<tbody>
				{% for method in request.user.mfa_webauthn_methods %}
				<tr>
					<td>{{ method.name }}</td>
					<td>{{ method.created.strftime('%b %d, %Y') }}</td>
					<td><a class="btn btn-sm btn-danger float-right" href="{{ url_for('mfa.delete_webauthn', id=method.id) }}">{{_("Delete")}}</a></td>
				</tr>
				{% endfor %}
				{% if not request.user.mfa_webauthn_methods %}
				<tr class="table-secondary">
					<td colspan=3 class="text-center">{{_("No U2F/FIDO2 devices registered yet")}}</td>
				</tr>
				{% endif %}
			</tbody>
		</table>
	</div>
</div>

{% if webauthn_supported %}
<script src="{{ url_for('static', filename="cbor.js") }}"></script>
<script>

$('#webauthn-form').on('submit', function(e) {
	$('#webauthn-alert').addClass('d-none');
	$('#webauthn-spinner').removeClass('d-none');
	$('#webauthn-btn-text').text('Contacting server');
	$('#webauthn-btn').prop('disabled', true);
	fetch({{ url_for('mfa.setup_webauthn_begin')|tojson }}, {
		method: 'POST',
	}).then(function(response) {
		if (response.ok)
			return response.arrayBuffer();
		if (response.status == 403)
			throw new Error('You need to generate recovery codes first');
		throw new Error('Server error');
	}).then(CBOR.decode).then(function(options) {
		$('#webauthn-btn-text').text('Waiting for response from your device');
		return navigator.credentials.create(options);
	}).then(function(attestation) {
		return fetch({{ url_for('mfa.setup_webauthn_complete')|tojson }}, {
			method: 'POST',
			headers: {'Content-Type': 'application/cbor'},
			body: CBOR.encode({
				"attestationObject": new Uint8Array(attestation.response.attestationObject),
				"clientDataJSON": new Uint8Array(attestation.response.clientDataJSON),
				"name": $('#webauthn-name').val()
			})
		});
	}).then(function(response) {
		if (response.ok) {
			$('#webauthn-spinner').addClass('d-none');
			$('#webauthn-btn-text').text('Success');
			window.location = {{ url_for('mfa.setup')|tojson }};
		} else {
			throw new Error('Response from authenticator rejected');
		}
	}, function(err) {
		console.log(err);
		/* various webauthn errors */
		if (err.name == 'NotAllowedError')
			$('#webauthn-alert').text('Registration timed out, was aborted or not allowed');
		else if (err.name == 'InvalidStateError')
			$('#webauthn-alert').text('You attempted to register a device that is already registered');
		else if (err.name == 'AbortError')
			$('#webauthn-alert').text('Registration was aborted');
		else if (err.name == 'NotSupportedError')
			$('#webauthn-alert').text('U2F and FIDO2 devices are not supported by your browser');
		/* errors from fetch() */
		else if (err.name == 'TypeError')
			$('#webauthn-alert').text('Could not connect to server');
		/* our own errors */
		else if (err.name == 'Error')
			$('#webauthn-alert').text(err.message);
		/* fallback */
		else
			$('#webauthn-alert').text('Registration failed ('+err+')');
		$('#webauthn-alert').removeClass('d-none');
		$('#webauthn-spinner').addClass('d-none');
		$('#webauthn-btn-text').text('Retry registration');
		$('#webauthn-btn').prop('disabled', false);
	});
	return false;
});

if (typeof(PublicKeyCredential) != "undefined") {
	{% if not mfa_init %}
	$('#webauthn-btn').prop('disabled', false);
	$('#webauthn-name').prop('disabled', false);
	{% endif %}
} else {
	$('#webauthn-alert').text('U2F and FIDO2 devices are not supported by your browser');
	$('#webauthn-alert').removeClass('d-none');
}

</script>
{% endif %}

{% endblock %}
