I’ve updated my social networking demo app Scrawl to include a couple often requested features – Universal links, email verification, and password reset.
In addition, I’ve integrated Mailgun into the backend application in order to allow the server to send email messages to users (which is obviously a requirement for two of these features).
If you are interested in a slightly deep dive into these features, read on!
The first feature added to Scrawl is universal links. In a nutshell, this feature essentially allows our app to request that it be the first responder for any requests to a given domain.
For example, if your app is called MyApp and you own the myapp.com domain, you can set up your app to handle any links to that domain. Instead of the device opening Safari and loading up a webpage, the URL can be passed to the app, and the app can handle the request.
This is great because it allows you to create a mobile website that works perfectly fine for users who don’t have your app installed, but allows those users who do have your app installed to get the best, native app experience possible.
Scrawl actually now supports two URL handling schemes: it is the handler for links from the domain https://getscrawl.com as well as responding to it’s own custom URL scheme scrawl:// — the latter scheme is often useful for inter-app communication and data passing, whereas the former is convenient for users to pass around links to and into your app.
There’s another great advantage to using universal links: iOS devices will store your user’s credentials as if they were website passwords — which makes things much, much easier for your users when they reinstall your app and want to sign in again quickly!
In Scrawl, I’m using universal links to handle two kinds of links: email validation, and password reset requests.
Often, you will want to make sure users verify their identity in some way before they have access to some or all of the features in your app. The simplest and most common validation is email verification.
In order to handle email validation requests, a few things have to happen:
- The user object needs to be updated to include a column to denote if the email address was validated — BUT this column must only be writable by the server! Users should not be able to overwrite this column themselves (e.g. via a public REST api or any other method).
- When a user signs up, or when the user requests a new validation email, the server should create an object in the database, generate a token, store that token in the new object, and send an email to the email address with a link back to the app. The object created must only be creatable by the server, and read/writeable by the server.
- When the user clicks this link, the app should handle the request by validating that the request was issued by the server and, if so, mark that new column as true. This is done by simply checking the database to see if an object exists for the given email address matching the token. Since the server created the token and sent it to the email address, and since the object we stored it in is not readable, we can safely assume that anyone who has the token at least has access to the email address.
- The validation request should then be deleted from the server.
This is all pretty straightforward. The interesting thing about email validation is how to handle the case when the user hasn’t validated their email yet.
Do we pop up a window and refuse to close it until the validation is complete, essentially blocking the user out until validation is successful?
Or do we simply check that column before we allow the user to perform certain actions — both on the client side and the server side?
In Scrawl, I opted for the latter option — primarily because I don’t like putting walls in front of users and prefer to let users do as much as we can possibly allow without any interruption in use.
Specifically, I want to prevent the user from creating any content — a place, a comment, a photo, etc — until they have validated their email address. Other types of actions — like voting on places or comments, or simply reading comments, are perfectly fine to do even if you haven’t verified your email address.
This approach is consistent with my overall philosophy of not annoying the user! Let them have as much of the experience as possible before you ask them to sign up, pay, verify their identity, etc.
However, that’s not always appropriate, and sometimes “as much as we can allow” is… well, nothing!
To see an example of the former case, you can check out GlobeChat, which requires validation to finish signing up or logging in. In the case of GlobeChat, the validation email is sent immediately upon signup (and login, if the user has not verified their email yet!), and a modal dialogue essentially locks users out until the validation occurs.
The final piece of the pie is gatekeeping on the client and server side. I far too often see projects where there is only one check — either client or server — before allowing access to a feature that demands verification.
The server should always have the final say, and in the worst case, if you really, really can only do that check in one place, please make it the server!
That said, client side checks are super important to make sure that you don’t waste calls to the server for events you have the ability to know ahead of time will be rejected. And even more important, you should alert your users to the fact that what they are trying to do is going to fail, without making them wait for a network request to complete. Even better, make sure the UI reflects this fact so they won’t even be able to try!
When a user requests a password reset, the server needs to generate a token, and send an email to the user with a link back into the app.
When the user clicks the link, the client app should verify with the server that the request is valid, and then present the user with a password reset screen.
When the password is reset, the server should update the user, and delete the request.
This almost certainly seems really straightforward, but it is absolutely critical to get features like this right. If email validation or password reset requests are not secure and well built, users could potentially change other user’s passwords, create accounts with fake emails… or worse. Scrawl now reflects what I consider best practices in implementing these features.
Last modified: March 1, 2019