# JWT Auth + Refresh Tokens in Rails 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](https://github.com/waiting-for-dev/devise-jwt) which unfortunately does not support refresh tokens - [devise_token_auth](https://github.com/lynndylanhurley/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](https://github.com/doorkeeper-gem/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](https://github.com/Gokul595/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::Issuer` module to create an `access_token` and `refresh_token` pair. - You call on the `Jwt::Authenticator` module to authenticate the `access_token` get the `current_user` and the `decoeded_token` - You call on the `Jwt::Revoker` module to revoke (blacklist/remove whitelist) a token - You call on the `Jwt::Refresher` module to refresh an `access_token` based 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: 1. You need to create a blacklisted tokens table like so: `rails g model BlacklistedToken jti:string:uniq:index user:belongs_to exp:datetime` 2. 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` 3. Create a refresh tokens table like & model so: `rails g model RefreshToken crypted_token:string:uniq user:belongs_to` ```ruby 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 ``` 4. Update the user model to include the associations ```ruby has_many :refresh_tokens, dependent: :delete_all has_many :whitelisted_tokens, dependent: :delete_all has_many :blacklisted_tokens, dependent: :delete_all ``` Then 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!