{% extends "base.html" %}
{% block title %}Set a new password{% endblock %}
{% block chrome %}{% endblock %}
{% block content %}
<form class="card" id="reset-form">
<h1>Set a new password</h1>
{# The token from the emailed link, carried through the form rather than
consumed by the page that rendered it. A mail client that prefetches links
would otherwise burn it before the person clicked. #}
<input type="hidden" name="token" value="{{ token }}">
{% if not token %}
<p class="error">This link is missing its token. Open the one from the email,
or <a href="/forgot">ask for a new link</a>.</p>
{% endif %}
<label>New password
<input type="password" name="password" autocomplete="new-password"
minlength="12" required autofocus>
<span class="hint">At least 12 characters.</span>
</label>
<p class="error" id="reset-error" hidden></p>
<button type="submit" id="reset-submit" {% if not token %}disabled{% endif %}>
Set password
</button>
</form>
<div class="card" id="reset-done" hidden>
<h1>Password set</h1>
<p>You can sign in with it now.</p>
{# No session is started here on purpose: signing in re-runs every gate,
including the second factor if one survived. See api_password_reset. #}
<p id="passkey-note" class="note" hidden>
Any passkeys on this account were removed, because a reset is how somebody
gets back in when the passkey itself is what they lost. Enrol a new one from
<strong>Security</strong> after you sign in.
</p>
<p><a class="button-secondary" href="/login">Sign in</a></p>
</div>
{% endblock %}
{% block scripts %}
<script>
const form = document.getElementById("reset-form");
const error = document.getElementById("reset-error");
form.addEventListener("submit", async (e) => {
e.preventDefault();
error.hidden = true;
const button = document.getElementById("reset-submit");
button.disabled = true;
const data = new FormData(form);
try {
await api.post("/api/password/reset", {
token: data.get("token"),
password: data.get("password"),
});
form.hidden = true;
// Shown unconditionally: the endpoint deliberately does not report whether
// the account had a passkey, because that is a fact about an account the
// caller has not signed into yet.
document.getElementById("passkey-note").hidden = false;
document.getElementById("reset-done").hidden = false;
} catch (err) {
error.textContent = err.message;
error.hidden = false;
button.disabled = false;
}
});
</script>
{% endblock %}