This is just some code I recently used in my development application in order to add token-based authentication for my api-only rails app. The api-client was to be consumed by a mobile application, so I needed an authentication solution that would keep the user logged in indefinetly and the only way to do this was either using refresh tokens or sliding sessions.
I also needed a way to both blacklist and whitelist tokens based on a unique identifier (jti)
Before trying it out DIY, I considered using:
- devise-jwt which unfortunately does not support refresh tokens
- devise_token_auth I ran into issues when it came to the changing headers on request on mobile, disabling this meant users would have to sign in periodically
- doorkeeper This was pretty close to what I needed, however, it was quite complicated and I considered it wasn't worth the extra effort of implmeneting OAuth2 (for now)
- api_guard This was great, almost everything I needed but it didn't play too nicely with GraphQL and I needed to implement token whitelisting also.
So, since I couldn't find any widely-used gem to meet my needs; I decided to just go DIY, and the end result works pretty well. And overview of how things works is so:
- You call on the Jwt::Issuermodule to create anaccess_tokenandrefresh_tokenpair.
- You call on the Jwt::Authenticatormodule to authenticate theaccess_tokenget thecurrent_userand thedecoeded_token
- You call on the Jwt::Revokermodule to revoke (blacklist/remove whitelist) a token
- You call on the Jwt::Refreshermodule to refresh anaccess_tokenbased on a refresh_token
There are more modules, but you can preview them for yourself.
There are some prequistes you need in order to use this code:
- 
You need to create a blacklisted tokens table like so: rails g model BlacklistedToken jti:string:uniq:index user:belongs_to exp:datetime
- 
If you want to use whitelisting to, create a tokens table like so: rails g model WhitelistedToken jti:string:uniq:index user:belongs_to exp:datetime
- 
Create a refresh tokens table like & model so: rails g model RefreshToken crypted_token:string:uniq user:belongs_to
class RefreshToken < ApplicationRecord
  belongs_to :user
  before_create :set_crypted_token
  attr_accessor :token
  def self.find_by_token(token)
    crypted_token = Digest::SHA256.hexdigest token
    RefreshToken.find_by(crypted_token: crypted_token)
  end
  private
  def set_crypted_token
    self.token = SecureRandom.hex
    self.crypted_token = Digest::SHA256.hexdigest(token)
  end
end- Update the user model to include the associations
  has_many :refresh_tokens, dependent: :delete_all
  has_many :whitelisted_tokens, dependent: :delete_all
  has_many :blacklisted_tokens, dependent: :delete_allThen you are pretty much ready to go!
In the future, I might make this into a gem or add redis support or similar.
I hope this gist helps someone!
This is such a hidden gem, thanks for this