Two-factor authentication (2FA) on Lemmy is a hot mess

2023-12-10 | Estimated reading time: 5 minutes

It’s been about half a year since Reddit effectively locked out third-party apps (RIP Apollo 🫡) and I have to admit that I kinda do miss Reddit a bit. Old Reddit makes it more bearable – there even is a browser extension that automagically redirects me to Old Reddit –, but most of my Reddit consumption was on my phone…

This is where I decided to give Lemmy and the Mlem for Lemmy app a chance. I chose programming.dev as my starting point. Setting up two-factor authentication (2FA) however… was quite a ride.

Disclaimer: As I am writing this, v0.18.5 is the most recent release, which is what programming.dev is running on, as well. A pull request reworking 2FA on Lemmy has been merged, but those changes will only be part of v0.19. As far as I can see, that PR only addresses users potentially locking themselves out, but not actual UX issues. Compatibility issues around using SHA-256 instead of SHA-1 are fixed, but the process of setting up 2FA itself still seems to be more complex than it has to be.

Currently, we can enable 2FA by just checking a checkbox on our settings page. Then, we are being told 2FA is now active and we can set up your 2FA app by using the the “2FA installation link” that shows up when we reload the settings page.

2FA installation link button on the settings page

What does the link look like? Something like this (line breaks added for readability):

otpauth://totp/programming.dev:[YOUR_USERNAME] \
  ?secret=[LONG_SECRET] \
  &algorithm=SHA256 \
  &issuer=programming.dev

What does clicking on that link do? It takes us to whichever application is set up to handle otpauth:// URIs. Which is System Settings.app (taking me to Passwords) on my MacBook Air. Which also happens to not be what I use for 2FA codes.

The important bits of this link are:

  1. It’s a time-based one-time password (TOTP).
  2. The [LONG_SECRET] string.
  3. The algorithm, SHA256.

The rest, programming.dev:[YOUR_USERNAME] and issuer=programming.dev, are just labels.

Armed with this information, we can have a look at how to set up different 2FA apps. I’ll skip Authy, because I don’t want to give them my phone number. Other apps I don’t mention here most likely have a similar setup process.

Google Authenticator

Note: Since Lemmy is using SHA-256 for releases before v0.19 and Google Authenticator currently does not support SHA-256, it is not possible to set up working 2FA with Google Authenticator for those releases.

The “Account” field is for whatever label we want to use to identify the right 2FA item in our list. [LONG_SECRET] goes into the “Key” field and “Time-based” should be pre-selected.

Using the 2FA installation link in Google Authenticator

OTP Auth

Adding an item using “Enter Credentials” gives us a screen with multiple input fields.

The “Name” and “Issuer” fields again are just label fields. The [LONG_SECRET] string goes into the “Secret” field and is Base32-encoded. (TIL about Base32, btw.) Again, “Time-based OTP” should be pre-selected.

Using the 2FA installation link in OTP Auth

Then, we need to tell the app to use SHA-256. For this, we have to edit the entry we just created and set the “Algorithm” field to “SHA256”.

Set the algorithm to SHA-256 in OTP Auth

1Password

Finally, something easy. We just create a “One-Time Password” field for our login

Add a "One-Time Password" field to a login in 1Password

and paste the entire 2FA installation link into that field.

Paste the 2FA installation link into the "One-Time Password" field in 1Password

How do we know everything is set up correctly?

Now that our 2FA app gives us 2FA codes, we should verify right away that everything is set up correctly. Otherwise, we risk locking ourselves out until the Lemmy server of our choice upgrades to v0.19 or newer.

Fortunately, verifying this is easy: We open a new private window (or a different browser entirely) and try to log into our Lemmy account. We should have to enter a 2FA code now and using the 2FA code our app generates should let us in. If it does not and setting up our 2FA app again following the steps above does not help, we should just disable 2FA for now and hope that we picked a password that’s strong enough.

I guess this is where not having to enter a working 2FA code to disable 2FA actually is a boon…

Why is this so bad…

Setting up 2FA has come with a lot of unwritten conventions and conveniences for years, namely:

  1. There is a QR code we can scan with a compatible app for an easier setup process.
  2. Having to enter a working 2FA code after setting up our app before 2FA is activated on our account to make sure we did not mess up somewhere.
  3. A list of backup codes to let us access our account for when we don’t have our 2FA app around, e. g. because we’ve lost our phone or we forgot to transfer it from our old phone when we got our new phone.

All these things exist for a thing: They give us confidence that even if something bad™ happens, we don’t lose access to our account. Which in turn allows us to be less hesitant when enabling 2FA.

As is, I would not recommend setting up 2FA on Lemmy, unless you know very well what you’re doing.

We can minimise the risk of being locked out by setting up multiple 2FA apps with different failure points – one might argue that that’s how it should be done in general anyway –, but the lack of backup codes that don’t rely on wherever our 2FA setup is on makes me feel really uncomfortable using 2FA on Lemmy.

Huy Dinh's profile picture

Huy Dinh

Senior Software Engineer
at Solaris SE