Skip to content

Instantly share code, notes, and snippets.

@jwaiswa7
Last active April 14, 2024 23:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jwaiswa7/2b58535c33fe15bed3e025708ca1e56c to your computer and use it in GitHub Desktop.
Save jwaiswa7/2b58535c33fe15bed3e025708ca1e56c to your computer and use it in GitHub Desktop.
How to use refresh tokens with Devise JWT

REFRESH TOKENS WITH DEVISE

I have been using the devise gem with rails to implement user authentication for may applications. When implementing authentication though API requests, then I turn to the devise-jwt gem. I faced an issue when implementing refresh tokens with devise, as devise does not support access tokens. I still needed the superior authentication that devise provides, but needed to manually work with fresh tokens. After looking around the web, here is how I was able to modify the code to implement refresh tokens with devise-jwt.

Take for the example a user model to sore the user email and password.

I created a blacklist model that will hold information of refresh tokens that have been decoded.

I generated refresh tokens with the generate_refresh_token method in the user module example below. I also added a decode frefresh token method as a class method on the user class, this will decode a refresh token passed and return the user object.

I created a refresh_tokens controller that will return an access token and refresh token as header values once accesed.

In the refresh token controller, I included the include Devise::Controllers::Helpers helper in the refresh token controller.

This allows me to access the sing_in method provided by devise.

# rails g model BlacklistedToken jti:string:unique user:references
class CreateBlacklistedTokens < ActiveRecord::Migration[7.0]
def change
create_table :blacklisted_tokens do |t|
t.string :jti
t.references :user, null: false, foreign_key: true
t.timestamps
end
add_index :blacklisted_tokens, :jti, unique: true
end
end
# app/controller/api/v1/users/refresh_tokens_controller.rb
# frozen_string_literal: true
module Api
module V1
module Users
class RefreshTokensController < ApplicationController
include Devise::Controllers::Helpers
skip_before_action :authenticate_user!
def create
user = decode_refresh_token(refresh_token_params[:refresh_token])
generate_refresh_token(user)
sign_in(:user, User.first)
end
private
def generate_refresh_token(user)
token = user.generate_refresh_token
response.headers['X-Refresh-Token'] = token
end
def decode_refresh_token(token)
User.decode_refresh_token(token)
end
def refresh_token_params
params.require(:user).permit(:refresh_token)
end
end
end
end
end
# app/models/user.rb
class User < ApplicationRecord
def self.decode_refresh_token(token)
blacklisted_token = BlacklistedToken.find_by(jti: token)
raise JWT::DecodeError, 'Token has been blacklisted' if blacklisted_token.present?
secret_key = Rails.application.credentials.devise_jwt_secret_key
payload = JWT.decode(token, secret_key, true, algorithm: 'HS256').first
user = User.find(payload['sub'])
BlacklistedToken.create(jti: token, user: user) if user.present?
user
end
def generate_refresh_token
secret_key = Rails.application.credentials.devise_jwt_secret_key
jti = SecureRandom.uuid
payload = { sub: id, jti: jti }
JWT.encode(payload, secret_key, 'HS256')
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment