When we were building Notesnook, our first and foremost priority was to ensure the security & privacy of users' Notes. With that in mind, we added end-to-end encryption, app lock, notes vault & encrypted attachments. All these kept your notes safe but the security of your account was at constant risk.
Anyone with your password could easily login & read your notes — a troubling thought, I know. To remedy this, we have now added 2FA. This blog post is about how we went about adding 2-factor authentication & why we made certain decisions along the way.
Choosing 2FA providers
If you have setup 2FA in other apps, you know how varying the different 2FA methods can be. Some services offer 2FA via SMS, others via an authentication (TOTP) app, or both.
In Notesnook, we went with 3 main options:
The security experts will, no doubt, balk at the thought of 2FA via email
but let me explain. The more secure a method, the more cumbersome it becomes. For example, to setup 2FA via an authenticator app you have to go through multiple steps before you are done. There's no doubt about the level of security but it's also necessarily complex — not everyone's piece of cake
Obviously, the authenticator app is still the recommended method.
On the other hand, email is super easy. Almost everyone already has it and setting up 2FA takes less than a minute. This encourages even the laziest crowd to secure their accounts via 2FA.
Implementing 2FA
The most time consuming thing was fine tuning the UX behind 2FA. I researched the setup/auth flows of a lot of different services (Google, GitHub etc) and took the best thing from each.
I found GitHub's 2FA setup flow to be the most straight-forward with minimum amount of steps. Google's was most complex with a lot of back and forth. Discord's flow, while easy, was confusing.
In the end, I settled with a 3-step process:
- Selecting a method
- Verifying via the selected method
- Backing up the recovery codes
- Done
No navigation, no fancy animations, no back and forth — just a dialog that lets you enable 2FA in 3 steps.
The drama of SMS providers
The worst thing about being in Pakistan is that a lot of services simply refuse to offer their businesses here. I wanted to go with Twilio but they refused to accept Debit cards. After some more research, I found a couple of other providers:
- Vonage — failed with an error at sign up & login. Support was no help.
- Plivo — doesn't accept IPs from Pakistan
- Bandwidth — only offers SMS services to US & Canada
- MessageBird
- Telnyx
I wasted a couple of hours trying out the first 2. I really liked Plivo's API that allowed sending custom 2FA codes at no extra cost. Unfortunately, they don't allow Pakistan.
Telnyx was a good option but I found their documentation a little confusing. In the end, I went with MessageBird. Aside from very competitive pricing, they have an absolutely gorgeous UI (and very easy to use dashboard).
MessageBird
The server-side logic took me about 10 minutes before I found out that MessageBird doesn't allow sending custom 2FA codes. Oops. Contacting their support was no help (although they responded very quickly).
I had to change the implementation details to allow MessageBird to verify phone numbers with their own codes but that's about it.
So far it has been working really good. Perfect choice.
The E.164 standard format
MessageBird only accepts phone numbers in E.164 standard format. What's E.164 standard? I have no idea (I didn't bother to read the document) but I got the basics of it after checking a few libraries.
Phone numbers have various formats:
+1 (292)-2292-291
(292)-2292-291
.
.
And this gets really hard to parse so there needs to be a standard — the E.164 standard. This turns a complex combination of symbols & digits into:
+12922292291
I am sure there's more to it. Obviously, I didn't go about doing this myself; I found this amazing library by AfterShip to do all the heavy lifting. Be sure to star their repo.
OAuth2 + MFA = Hard work
OAuth2 is very unclear about 2FA leaving it up to the developers to figure out — a risky thing for sure. I checked the Auth0 documentation and essentially stole their implementation idea.
A normal authentication flow works like this (very simplified):
Client -> user credentials -> auth server -> access token generation -> client
With the addition of 2FA, the above becomes:
Step 1: Client -> user credentials -> auth server -> 2FA detected -> 2FA token generation -> client
Step 2: Client -> 2FA code + user credentials -> auth server -> 2FA verification -> access token generation -> client
The difference between a 2FA token & an access_token
is permissions. 2FA token only allows touching certain endpoints so nope, you won't be able to get users' notes with just the 2FA token.
The reason this is required in the first place is to prevent spam by making the 2FA endpoints public.
The End
All in all, it took us a week to finalize 2FA cross-platform and shipped in v1.8.3
. Download Notesnook on all your devices to try it out!