Master Your Tokens
By: Ifat Ribon / May 5, 2017
A Little Too Much
If you build web apps with users, then you have some level of experience with common user management features, such as confirming emails, resetting passwords, and inviting users.
There are some wonderful libraries out there for Rails and Ruby apps that take care of these features, such as Devise and Sorcery. These gems do the heavy lifting of authentication and user management flows, and we use them in many of our apps.
Devise, for example, is a great out-of-the-box solution to quickly implement a comprehensive authentication system. However, it is tightly tied to Rails views, mailers, and routes, making it difficult to decouple certain components if they are not needed.
Over time, as we started building API-only apps with JavaScript frontends, we found ourselves not needing all of the functionality these libraries provided. We wanted more control and flexibility in managing our user management flows, and a solution that doesn’t care about how we might be extending our Rails application. Ultimately, we wanted to be able to do a little more, with a little less.
Designing the Solution
We set out to build our own libraries to accomplish that increased control, using as simple of logic as possible. We started with Confirmable, a small library that generated a token for a user, sent instructions to the user with the token, and confirmed the user when it returned the token, given it had not yet expired.
Then we moved to resettable. Immediately we noticed the code for resettable was about 95% the same. The only discernible difference was the reset flow should allow for the password of the user to actually be updated. That is, resettable needed to know a little bit more about the model (i.e., its password fields) than confirmable did.
We guessed at that point that these, and likely other user management flows, shared a lot in common. Essentially, we were granting a user temporary access, using a token, to a small part of the web application in order to complete an action required to unlock the rest of the application.
Instead of writing multiple similar libraries, we could look to abstract the commonalities and build one library to handle all of the actions. Once we abstracted, we reduced these “tokenable” actions down to having the same five steps:
- Create a unique token to allow temporary and limited access to your token
- Provide the user with a method to redeem the token (e.g., an email with a link)
- Redeem or reject the token based on validity or expiration
- Update the user instance with any new information (e.g., new passwords)
- Ultimately revoke the token (optional)
We merely wanted our library to handle just these steps (and catch some errors along the way).
Enter…. the Token Master!
Token Master
Token Master handles the logic of basic user management flows in granting temporary access to complete certain “tokenable” actions. Token Master is easy to implement with the token_master generator. You provide it with the tokenable Class (e.g., User), and the tokenable actions you’d like to manage (e.g., confirm, reset). You can call these actions whatever you want.
bundle exec rails generate token_master User confirm foo
This provides you with a migration file to add four columns per tokenable action:
- confirm_created_at
- confirm_token
- confirm_sent_at
- confirm_completed_at
as well as an initializer file, to specify configurations for each of the tokenable actions. You can modify the three configurations available:
- token_lifetime
- token_length
- required_params
config.add_tokenable_options :confirm, { token_lifetime: 14, required_params: [:email], token_length: 20 }
From there, Token Master is easy to use with only five public methods, four instance methods and one class method. Each method name is unique to the tokenable action, but provides the same logic.
Instance methods:
user.set_confirm_token! user.send_confirm_instructions! # forcibly complete a tokenable action user.force_confirm! # return the current status of the tokenable (e.g., confirm) action user.confirm_status # no token # created # sent # completed # expired
Class methods:
# complete the confirm action using the user's confirm_token User.confirm_by_token!
If you decide later that you want to add more tokenable actions to your model, simply run the generator again with the new actions:
bundle exec rails generate token_master User reset confirm
Token Master will create a new migration file for the new tokenable actions, and add config methods to your existing initializer, or create a new initializer if you removed it.
Finally, you use your new Token Master methods on your user instance or class, and let the gem handle all the logic for the five steps above!
# 1: Generate and save a unique token token = user.set_reset_token! # 2: Mark the token as sent user.send_reset_instructions do # you can pass a block with your email logic here end # 3, 4, 5: Complete the tokenable action, using the token and any required params User.reset_by_token!(token, password: ‘password’, password_confirmation: ‘password’)
We are now using Token Master in some of our projects, and are enjoying the flexibility of the library and the decoupling from our front-end structure and authentication processes we let the other libraries handle.
Token Master is available on Ruby gems, and you can read our docs or visit our Github repo to learn more.
Go forth, and master your tokens!
Ready to Build Something Great?
Partner with us to develop technology to grow your business.