📝 Signup Form Best Practices
In this post, we look at a Svelte login form example and see some security and user experience (UX) best practices. Clearly, good accessibility and overall UX help improve conversion rates. It is important, though, to stress the economic benefits of helping users implement good security before moving on.
In 2022 a credential stuffing attack on DraftKings, a US sports betting site, led to USD 300,000 being stolen from user accounts. Reports suggest there was no breach of DraftKings systems. Instead, the attacker is understood to have taken a list of user credentials leaked from other sites and tried them on DraftKings, in the hope users had recycled passwords. This is credential stuffing: trying to again access to a service using leaked or stolen passwords from another service.
This kind of attack is difficult for services to prevent. We focus on implementing some best practices to promote users improving their security in post, using a Svelte code examples.
Multifactor authentication
User best practices involve creating strong and unique passwords for each new account and setting up Multifactor Authentication where offered. Here FIDO U2F keys are the gold standard, with OTP apps like Authy and Google Authenticator offering a secure alternative. Passkeys, using the new WebAuthn standard might be a common, secure choice going forward. Passkeys will work with mobile devices, and you can enforce that the user authenticates using device biometrics (like a fingerprint check) for extra security.
🧑🏽 User Experience Recommendations
To help keep forms accessible, test them with tools like Lighthouse and Axe. Also try opening up the Issues tab in Chrome Dev tool as new Autofill and general form debugging features are rolling out in Chrome from May 2023. These will help with more general UX issues, as well as accessibility (making sure label elements have a for
attribute matching an input, and so on).
For more general UX, remember to allow users to paste in passwords. Blocking pasting can be frustrating for password manager users. Finally, using autofill fields correctly can help users who store their passwords in-browser. Note, storing passwords in browser in not advised by some privacy advocates. See a longer checklist of sign-up best practices on web.dev.
🔐 OWASP recommendations
Ideally, your site will offer MFA and the Open Web Application Security Project (OWASP) has some recommendations, which also cover single factor authentication. These include:
- enforcing a minimum and maximum password lengths
- adding a password strength meter to encourage more complex passwords
- blocking common and previously breached passwords
The minimum password length should be eight or more characters, and the maximum, long enough for users creating passphrases using diceware and other techniques. Enforcing a maximum length helps protect against some DoS attacks. We see a library below which can helps in adding a strength meter and spotting common passwords.
🧱 Svelte Login From: What we're Building
I put together a full demo implementing some of the best practices just mentioned. The demo includes an SQLite database, so you can spin it up, and test and tweak the techniques we discuss. We just look at the most important front end snippets here, and you can find the link to view the full repo further down.
🖋️ Signup Form
Let’s look at some code, staring with form elements on the new user signup form.
Username Input Field
<label for="email">Email</label>
<input
id="email"
aria-invalid={emailError}
aria-describedby={emailError ? "email-error" : undefined}
type="email"
required
name="email"
autocomplete="username"
value={email}
placeholder="tillie@example.com"
/>
{#if errors?.email}
<small id="email-error" class="error-text">{errors.email}</small>
{/if}
Important points here for accessibility are:
- the
for
attribute on thelabel
element matches theinput
elementid
(both set toemail
) - the input uses
aria-invalid
andaria-describedby
to provide feedback for screen reader users - the label is displayed here, if you want to hide it visually, keep it in the DOM but add a screen reader only class
Some browsers will use the type
and required
attributes to offer hints or even intercept submission when inputs are invalid. Remember, these are front end validations, and additional back end validations are important as a naughty user can easily bypass the front end checks we have above!
Finally, we use the value
attribute here, to repopulate the field if, for example, the user submits an invalid password. A small UX tweak.
🤖 Strong Password Auto Generation
Some modern browsers will offer to generate and store a strong password for users. To enable this, be sure to set autocomplete="new-password"
on the password field. Also include this same attribute on any password confirmation field.
Here is a snippet in which we set autocomplete="new-password"
:
<label for="password">Password</label>
<input
bind:value={password}
on:keydown={() => {
passwordTouched = true;
}}
id="password"
aria-invalid={passwordError}
aria-describedby={passwordError ? "password-error" : undefined}
type="password"
required
name="password"
autocomplete="new-password"
placeholder="P@s$w0rd"
/>
{#if errors?.password}
<small id="password-error" class="error-text">{errors.password}</small>
{/if}
💪🏽 Password Strength Meter
We mentioned above that OWASP suggest adding a password strength meter. This can help users generate stronger passwords. zxcvbn
is a fantastic library for helping here. I guess the name comes from a sequence of keys typically used on English keyboards. As well as measuring password strength, zxcvbn
will offer an explanation on why it considers a password to be weak and hints on strengthening it. This is ideal for helping users generate strong passwords.
In this project code, we just look at using zxcvbn
in the frontend. For a production project, it is worth considering running it on the backend too for password verification. You might also consider running checks using the Have I Been Pwned API, which is a library of known leaked credentials.
Svelte Login Form: Strength Meter Code
First, add some zxcvbn
project dependencies:
pnpm add @zxcvbn-ts/core @zxcvbn-ts/language-common \\
@zxcvbn-ts/language-en
There are other language dictionaries worth considering. Anyway, this is the front end code:
<script lang="ts">
import { zxcvbn, zxcvbnOptions, type Score } from "@zxcvbn-ts/core";
import * as zxcvbnCommonPackage from "@zxcvbn-ts/language-common";
import * as zxcvbnEnPackage from "@zxcvbn-ts/language-en";
let password = "";
let passwordTouched = false;
const { translations } = zxcvbnEnPackage;
const { adjacencyGraphs: graphs, dictionary: commonDictionary } =
zxcvbnCommonPackage;
const { dictionary: englishDictionary } = zxcvbnEnPackage;
const options = {
translations,
graphs,
dictionary: { ...commonDictionary, ...englishDictionary },
};
zxcvbnOptions.setOptions(options);
$: ({
score,
feedback: { warning, suggestions },
} = zxcvbn(password));
let strengthDescription = "Low";
$: switch (score) {
case 3:
strengthDescription = "OK";
break;
case 4:
strengthDescription = "Good";
break;
case 0:
case 1:
case 2:
default:
strengthDescription = "Low";
}
</script>
The password
variable is bound to the password input, and in lines 21-24
we use zxcvbn
to recompute a score reactively, when password
changes. zxcvbn
includes a debounce function to make your code more efficient, though we keep things simple here.
The score takes values between 0
and 4
, where 3
and 4
indicate unguessable passwords. Notice, the zxcvbn
call also yields the warnings and suggestions mentioned above.
While you can use a Svelte each
loop to display the suggestions, you might consider an HTML meter
element to display the strength meter (thanks to Paweł Błaszczyk for this tip):
<label for="password-strength">Password strength: {strengthDescription}</label>
<meter id="password-strength" value={score} low="1.9" high="2.9" optimum="4" max="4" />
{#if warning}
<span class="warning"> {warning}</span>
<ul>
{#each suggestions as suggestion}
<li class="alert">{suggestion}</li>
{/each}
</ul>{/if}
💯 Svelte Login Form Example: Testing it Out
To see the code in action, clone the repo and install packages. It uses Prisma with an on-disk SQLite database. To get going:
git clone https://github.com/rodneylab/svelte-login-form.git
cd svelte-login-form
pnpm install
cp .env.example .env
pnpm dev
The final, additional step is to send feedback!
🙌🏽 Svelte Login Form Example: Wrapping Up
In this post, we had a look at a Svelte Login form example. More specifically, we saw:
- why login and signup form UX and security matter;
- some form UX and security best practices; and
- how you might add a password strength meter to a Svelte signup form.
Please see the full repo code on the Rodney Lab GitHub repo. For more HTML login form best practices, also see the recent Evil Martians post. I do hope you have found this post useful and can use the code in your own Svelte app. Let me know if you have any suggestions for improvements to the post. Also reach out with ideas on new posts in this area. Drop a comment below or reach out on other channels.
🙏🏽 Svelte Login Form Example: Feedback
If you have found this post useful, see links below for further related content on this site. I do hope you learned one new thing from the video. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on Twitter, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram. Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as Search Engine Optimization among other topics. Also, subscribe to the newsletter to keep up-to-date with our latest projects.