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:

  1. Create a unique token to allow temporary and limited access to your token
  2. Provide the user with a method to redeem the token (e.g., an email with a link)
  3. Redeem or reject the token based on validity or expiration
  4. Update the user instance with any new information (e.g., new passwords)
  5. 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:

as well as an initializer file, to specify configurations for each of the tokenable actions. You can modify the three configurations available:

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!

Ifat Ribon

Developer

With a penchant for finding the best word in each situation, Ifat gravitated to coding as the perfect outlet for fitting the right words together. Ifat brings her consulting background, with a propensity for asking questions, to software development, and loves seeing the diversity of needs web applications can address. Away from her computer, Ifat is an avid lacrosse player. She runs a club team in Chicago and is always prepared to strike up a conversation about lacrosse or her team with anyone who will listen.

Ready to Build Something Great?

Partner with us to develop technology to grow your business.