Skip to content

Instantly share code, notes, and snippets.

@goncalvesjoao
Last active November 13, 2020 01:52
Show Gist options
  • Save goncalvesjoao/5e5131954a12065ac4c601ecf1b827fe to your computer and use it in GitHub Desktop.
Save goncalvesjoao/5e5131954a12065ac4c601ecf1b827fe to your computer and use it in GitHub Desktop.
Changes you need to make in order to make Devise use JWT Header Authentication
# Add somewhere to your Devise configuration
# "config/initializers/devise.rb"
Devise.setup do |config|
# ...
config.warden do |manager|
# Registering your new Strategy
manager.strategies.add(:jwt, Devise::Strategies::JsonWebToken)
# Adding the new JWT Strategy to the top of Warden's list,
# Scoped by what Devise would scope (typically :user)
manager.default_strategies(scope: :user).unshift :jwt
end
# ...
end
# Add the "https://github.com/jwt/ruby-jwt" gem to your "Gemfile"
gem 'jwt'
# Add the following code to a view
# It will show you an example of how to make a
# curl HTTP request with the proper authentication headers.
# Be sure to actually use a working route and not "http://localhost:3000/api/v1/yada_yada"
<% if user_signed_in? %>
curl -X GET --header 'Authorization: Bearer <%= JWTWrapper.encode({ user_id: current_user.id }) %>' 'http://localhost:3000/api/v1/yada_yada'
<% end %>
# Your actual JWT Strategy
# "config/initializers/core_extensions/devise/strategies/json_web_token.rb"
module Devise
module Strategies
class JsonWebToken < Base
def valid?
bearer_header.present?
end
def authenticate!
return if no_claims_or_no_claimed_user_id
success! User.find_by_id claims['user_id']
end
protected
def bearer_header
request.headers['Authorization']&.to_s
end
def no_claims_or_no_claimed_user_id
!claims || !claims.has_key?('user_id')
end
private
def claims
strategy, token = bearer_header.split(' ')
return nil if (strategy || '').downcase != 'bearer'
JWTWrapper.decode(token) rescue nil
end
end
end
end
# Helper module for you to use on your app and in your Strategy
# Don't add "Helper" to its name and rails won't load it has a view helper module.
# "app/helpers/jwt_wrapper.rb"
module JWTWrapper
extend self
def encode(payload, expiration = nil)
expiration ||= Rails.application.secrets.jwt_expiration_hours
payload = payload.dup
payload['exp'] = expiration.to_i.hours.from_now.to_i
JWT.encode payload, Rails.application.secrets.jwt_secret
end
def decode(token)
begin
decoded_token = JWT.decode token, Rails.application.secrets.jwt_secret
decoded_token.first
rescue
nil
end
end
end
# Add somewhere to your secrets yml
# "config/secrets.yml"
development:
# ...
jwt_secret: 'super random key'
jwt_expiration_hours: 24
# ...
production:
# ...
jwt_secret: 'even more super random key'
jwt_expiration_hours: 6
# ...
@treznick
Copy link

treznick commented Dec 9, 2016

Hi, firstly thanks @goncalvesjoao for posting these and the corresponding Medium post. I'm playing around with JWT and devise, and I'm thrilled that someone has put together a strategy that uses warden instead of controller filters. Quick question regarding this though. There are a few bare rescues in the code base, which can sometimes be a risky thing to do. Is there anything that might be worth rescuing that doesn't subclass JWT::DecodeError? I also recognize that this is primarily a gist to back a medium post, so don't take this as critical of what is mostly just a barebones example.

Thanks!

@bhushangahire
Copy link

I have database login and jwt I want default to be database how I can use JWT without making it default strategy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment