Search code examples
ruby-on-rails-5rails-apireset-passworddevise-token-auth

Rails5 API + devise_token_auth edit token format


so i'm using devise_token_auth gem for basic user functionalities, and I made some simple changes to the reset password flow. Basically instead of having a link in the email for the user to click and go to the reset page, I show the token, so the users has to copy it and enter manually in a field.

It is working like that, but the token is to large and ugly, i'd like it to be something like 6 alphanumeric digits. It looks better and it makes things easier for the user. I don't quite know how to do it, I probably should overwrite some devise original Controller. How to do it? As devise_token_auth places a controller "on top" of every original devise controller and what I want is to overwrite the set_reset_password_token from devise's recoverable module.

EDIT: I've found the answer myself so i'll answer it below! But please, feel free to complain, ask and propose better/alternative solutions.


Solution

  • So, first of, if you want to overwrite a function from a devise module, you can just write it with the same name on your model file, probably in models/user.rb. For overwriting the set_reset_password from recoverable module, you can just do the following (as suggested by Sergio Tulentsev here):

    class User < ActiveRecord::Base
      # Include default devise modules. Others available are:
      # :timeoutable and :omniauthable
      devise :database_authenticatable, :registerable,
             :recoverable, :rememberable, :trackable, :validatable,
             :confirmable, :lockable
      ...
    
      protected
        def set_reset_password_token
          ...
        end
    end
    

    Now for changing to how you want your raw token to be (before it's encrypted), in my case I wanted it to have 6 digits, containing only A-Z, a-z, 0-9 digits, lets see what devise does (link to github file):

    def set_reset_password_token
      raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
    
      self.reset_password_token   = enc
      self.reset_password_sent_at = Time.now.utc
      save(validate: false)
      raw
    end
    

    What Devise.token_generator.generate (seen here) does is generate a raw token using SecureRandom.urlsafe_base64(rlength).tr('lIO0', 'sxyz') (of length 20, as seen here) and return the raw and encrypted token unless some user already has the same token assigned.

    Finally, what you need to do is rewrite this set_password_token using SecureRandom functions allied with tr to generate tokens any way you like, and don't forget to make sure the token is unique and not currently being used.