devise_token 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +69 -0
- data/Rakefile +36 -0
- data/app/assets/config/devise_token_manifest.js +2 -0
- data/app/assets/javascripts/devise_token/application.js +13 -0
- data/app/assets/stylesheets/devise_token/application.css +15 -0
- data/app/controllers/devise_token/application_controller.rb +77 -0
- data/app/controllers/devise_token/authentications_controller.rb +87 -0
- data/app/controllers/devise_token/concerns/authenticate_token.rb +71 -0
- data/app/controllers/devise_token/confirmations_controller.rb +18 -0
- data/app/controllers/devise_token/registrations_controller.rb +189 -0
- data/app/controllers/devise_token/token_validations_controller.rb +29 -0
- data/app/helpers/devise_token/application_helper.rb +4 -0
- data/app/jobs/devise_token/application_job.rb +4 -0
- data/app/mailers/devise_token/application_mailer.rb +6 -0
- data/app/models/devise_token/application_record.rb +5 -0
- data/app/models/devise_token/concerns/user.rb +98 -0
- data/app/models/devise_token/json_web_token.rb +80 -0
- data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/config/locales/en.yml +50 -0
- data/config/routes.rb +2 -0
- data/lib/devise_token.rb +8 -0
- data/lib/devise_token/authenticable.rb +23 -0
- data/lib/devise_token/controllers/helpers.rb +70 -0
- data/lib/devise_token/controllers/url_helpers.rb +8 -0
- data/lib/devise_token/engine.rb +44 -0
- data/lib/devise_token/rails/routes.rb +66 -0
- data/lib/devise_token/version.rb +3 -0
- data/lib/generators/devise_token/install_generator.rb +160 -0
- data/lib/generators/devise_token/install_views_generator.rb +16 -0
- data/lib/generators/devise_token/templates/devise_token.rb +16 -0
- data/lib/generators/devise_token/templates/devise_token_create_users.rb.erb +55 -0
- data/lib/tasks/devise_token_tasks.rake +4 -0
- metadata +175 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module DeviseToken
|
2
|
+
class TokenValidationsController < DeviseToken::ApplicationController
|
3
|
+
skip_before_action :assert_is_devise_resource!, :only => [:validate_token]
|
4
|
+
|
5
|
+
|
6
|
+
def validate_token
|
7
|
+
# @resource will have been set by set_user_token concern
|
8
|
+
if @resource
|
9
|
+
yield @resource if block_given?
|
10
|
+
render_validate_token_success
|
11
|
+
else
|
12
|
+
render_validate_token_error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def render_validate_token_success
|
19
|
+
render json: {
|
20
|
+
success: true,
|
21
|
+
data: resource_data(resource_json: @resource.token_validation_response)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def render_validate_token_error
|
26
|
+
render_error(401, I18n.t("devise_token.token_validations.invalid"))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
module DeviseToken::Concerns::User
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
# Hack to check if devise is already enabled
|
7
|
+
unless self.method_defined?(:devise_modules)
|
8
|
+
devise :database_authenticatable, :registerable,
|
9
|
+
:recoverable, :trackable, :validatable, :confirmable
|
10
|
+
end
|
11
|
+
|
12
|
+
# don't use default devise email validation
|
13
|
+
def email_required?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def email_changed?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def will_save_change_to_email?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def password_required?
|
26
|
+
return false unless provider == 'email'
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
# override devise method to include additional info as opts hash
|
31
|
+
def send_confirmation_instructions(opts=nil)
|
32
|
+
unless @raw_confirmation_token
|
33
|
+
generate_confirmation_token!
|
34
|
+
end
|
35
|
+
|
36
|
+
opts ||= {}
|
37
|
+
|
38
|
+
if pending_reconfirmation?
|
39
|
+
opts[:to] = unconfirmed_email
|
40
|
+
end
|
41
|
+
opts[:redirect_url] ||= DeviseToken.default_confirm_success_url
|
42
|
+
|
43
|
+
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
# override devise method to include additional info as opts hash
|
47
|
+
def send_reset_password_instructions(opts=nil)
|
48
|
+
token = set_reset_password_token
|
49
|
+
|
50
|
+
opts ||= {}
|
51
|
+
send_devise_notification(:reset_password_instructions, token, opts)
|
52
|
+
|
53
|
+
token
|
54
|
+
end
|
55
|
+
|
56
|
+
# override devise method to include additional info as opts hash
|
57
|
+
def send_unlock_instructions(opts=nil)
|
58
|
+
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
|
59
|
+
self.unlock_token = enc
|
60
|
+
save(validate: false)
|
61
|
+
|
62
|
+
opts ||= {}
|
63
|
+
|
64
|
+
send_devise_notification(:unlock_instructions, raw, opts)
|
65
|
+
raw
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
protected
|
71
|
+
|
72
|
+
def database_exists?
|
73
|
+
ActiveRecord::Base.connection_pool.with_connection { |con| con.active? } rescue false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
# this must be done from the controller so that additional params
|
80
|
+
# can be passed on from the client
|
81
|
+
def send_confirmation_notification?
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
def confirmed?
|
88
|
+
self.devise_modules.exclude?(:confirmable) || super
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def token_lifespan
|
93
|
+
DeviseToken.token_lifespan
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "jwt"
|
2
|
+
module DeviseToken
|
3
|
+
class JsonWebToken
|
4
|
+
|
5
|
+
attr_reader :token
|
6
|
+
attr_reader :payload
|
7
|
+
|
8
|
+
def initialize payload: {}, token: nil, verify_options: {}
|
9
|
+
if token.present?
|
10
|
+
@payload, _ = JWT.decode token.to_s, decode_key, true, options.merge(verify_options)
|
11
|
+
@token = token
|
12
|
+
else
|
13
|
+
@payload = claims.merge(payload)
|
14
|
+
@token = JWT.encode @payload,
|
15
|
+
secret_key,
|
16
|
+
DeviseToken.token_signature_algorithm
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_resource resource_class
|
21
|
+
if resource_class.respond_to? :auth_payload
|
22
|
+
resource_class.auth_payload @payload
|
23
|
+
else
|
24
|
+
resource_class.find @payload['sub']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_json options = {}
|
29
|
+
{jwt: @token}.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
private
|
34
|
+
def secret_key
|
35
|
+
DeviseToken.token_secret_signature_key.call
|
36
|
+
end
|
37
|
+
|
38
|
+
def decode_key
|
39
|
+
DeviseToken.token_public_key || secret_key
|
40
|
+
end
|
41
|
+
|
42
|
+
def options
|
43
|
+
verify_claims.merge({
|
44
|
+
algorithm: DeviseToken.token_signature_algorithm
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
48
|
+
def claims
|
49
|
+
_claims = {}
|
50
|
+
_claims[:exp] = token_lifetime if verify_lifetime?
|
51
|
+
_claims[:aud] = token_audience if verify_audience?
|
52
|
+
_claims
|
53
|
+
end
|
54
|
+
|
55
|
+
def token_lifetime
|
56
|
+
DeviseToken.token_lifespan.from_now.to_i if verify_lifetime?
|
57
|
+
end
|
58
|
+
|
59
|
+
def verify_lifetime?
|
60
|
+
!DeviseToken.token_lifespan.nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
def verify_claims
|
64
|
+
{
|
65
|
+
aud: token_audience,
|
66
|
+
verify_aud: verify_audience?,
|
67
|
+
verify_expiration: verify_lifetime?
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def token_audience
|
72
|
+
verify_audience? && DeviseToken.token_audience.call
|
73
|
+
end
|
74
|
+
|
75
|
+
def verify_audience?
|
76
|
+
DeviseToken.token_audience.present?
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<p><%= t(:hello).capitalize %> <%= @resource.email %>!</p>
|
2
|
+
|
3
|
+
<p><%= t '.request_reset_link_msg' %></p>
|
4
|
+
|
5
|
+
<p><%= link_to t('.password_change_link'), edit_password_url(@resource, reset_password_token: @token, config: message['client-config'].to_s, redirect_url: message['redirect-url'].to_s).html_safe %></p>
|
6
|
+
|
7
|
+
<p><%= t '.ignore_mail_msg' %></p>
|
8
|
+
<p><%= t '.no_changes_msg' %></p>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
en:
|
2
|
+
devise_token:
|
3
|
+
authentication:
|
4
|
+
not_confirmed: "A confirmation email was sent to your account at '%{email}'. You must follow the instructions in the email before your account can be activated"
|
5
|
+
bad_credentials: "Invalid login credentials. Please try again."
|
6
|
+
not_supported: "Use POST /sign_in to sign in. GET is not supported."
|
7
|
+
user_not_found: "User was not found or was not logged in."
|
8
|
+
token_validations:
|
9
|
+
invalid: "Invalid login credentials"
|
10
|
+
registrations:
|
11
|
+
missing_confirm_success_url: "Missing 'confirm_success_url' parameter."
|
12
|
+
redirect_url_not_allowed: "Redirect to '%{redirect_url}' not allowed."
|
13
|
+
email_already_exists: "An account already exists for '%{email}'"
|
14
|
+
account_with_uid_destroyed: "Account with UID '%{uid}' has been destroyed."
|
15
|
+
account_to_destroy_not_found: "Unable to locate account for destruction."
|
16
|
+
user_not_found: "User not found."
|
17
|
+
passwords:
|
18
|
+
missing_email: "You must provide an email address."
|
19
|
+
missing_redirect_url: "Missing redirect URL."
|
20
|
+
not_allowed_redirect_url: "Redirect to '%{redirect_url}' not allowed."
|
21
|
+
sended: "An email has been sent to '%{email}' containing instructions for resetting your password."
|
22
|
+
user_not_found: "Unable to find user with email '%{email}'."
|
23
|
+
password_not_required: "This account does not require a password. Sign in using your '%{provider}' account instead."
|
24
|
+
missing_passwords: "You must fill out the fields labeled 'Password' and 'Password confirmation'."
|
25
|
+
successfully_updated: "Your password has been successfully updated."
|
26
|
+
unlocks:
|
27
|
+
missing_email: "You must provide an email address."
|
28
|
+
sended: "An email has been sent to '%{email}' containing instructions for unlocking your account."
|
29
|
+
user_not_found: "Unable to find user with email '%{email}'."
|
30
|
+
errors:
|
31
|
+
messages:
|
32
|
+
validate_sign_up_params: "Please submit proper sign up data in request body."
|
33
|
+
validate_account_update_params: "Please submit proper account update data in request body."
|
34
|
+
not_email: "is not an email"
|
35
|
+
devise:
|
36
|
+
mailer:
|
37
|
+
confirmation_instructions:
|
38
|
+
confirm_link_msg: "You can confirm your account email through the link below:"
|
39
|
+
confirm_account_link: "Confirm my account"
|
40
|
+
reset_password_instructions:
|
41
|
+
request_reset_link_msg: "Someone has requested a link to change your password. You can do this through the link below."
|
42
|
+
password_change_link: "Change my password"
|
43
|
+
ignore_mail_msg: "If you didn't request this, please ignore this email."
|
44
|
+
no_changes_msg: "Your password won't change until you access the link above and create a new one."
|
45
|
+
unlock_instructions:
|
46
|
+
account_lock_msg: "Your account has been locked due to an excessive number of unsuccessful sign in attempts."
|
47
|
+
unlock_link_msg: "Click the link below to unlock your account:"
|
48
|
+
unlock_link: "Unlock my account"
|
49
|
+
hello: "hello"
|
50
|
+
welcome: "welcome"
|
data/config/routes.rb
ADDED
data/lib/devise_token.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module DeviseToken::Authenticable
|
2
|
+
|
3
|
+
def authenticate_auth resource_class
|
4
|
+
if token
|
5
|
+
DeviseToken::JsonWebToken.new(token: token).current_resource(resource_class)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def token
|
10
|
+
params[:auth_token] || auth_token_request
|
11
|
+
end
|
12
|
+
|
13
|
+
def auth_token_request
|
14
|
+
unless request.headers['Authorization'].nil?
|
15
|
+
request.headers.fetch("Authorization", "").split(" ").last
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def auth_token_response
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module DeviseToken
|
2
|
+
module Controllers
|
3
|
+
module Helpers
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
def log_process_action(payload)
|
9
|
+
payload[:status] ||= 401 unless payload[:exception]
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Define authentication filters and accessor helpers based on mappings.
|
15
|
+
# These filters should be used inside the controllers as before_actions,
|
16
|
+
# so you can control the scope of the user who should be signed in to
|
17
|
+
# access that specific controller/action.
|
18
|
+
# Example:
|
19
|
+
#
|
20
|
+
# Roles:
|
21
|
+
# User
|
22
|
+
# Admin
|
23
|
+
#
|
24
|
+
# Generated methods:
|
25
|
+
# authenticate_user! # Signs user in or 401
|
26
|
+
# authenticate_admin! # Signs admin in or 401
|
27
|
+
# user_signed_in? # Checks whether there is a user signed in or not
|
28
|
+
# admin_signed_in? # Checks whether there is an admin signed in or not
|
29
|
+
# current_user # Current signed in user
|
30
|
+
# current_admin # Current signed in admin
|
31
|
+
# render_authenticate_error # Render error unless user or admin is signed in
|
32
|
+
#
|
33
|
+
# Use:
|
34
|
+
# before_action :authenticate_user! # Tell devise to use :user map
|
35
|
+
# before_action :authenticate_admin! # Tell devise to use :admin map
|
36
|
+
#
|
37
|
+
def self.define_helpers(mapping) #:nodoc:
|
38
|
+
mapping = mapping.name
|
39
|
+
|
40
|
+
class_eval <<-METHODS, __FILE__, __LINE__ + 1
|
41
|
+
def authenticate_#{mapping}!(opts={})
|
42
|
+
unless current_#{mapping}
|
43
|
+
render_authenticate_error
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def #{mapping}_signed_in?
|
48
|
+
!!current_#{mapping}
|
49
|
+
end
|
50
|
+
|
51
|
+
def current_#{mapping}
|
52
|
+
@current_#{mapping} ||= authenticate_token(:#{mapping})
|
53
|
+
end
|
54
|
+
|
55
|
+
def render_authenticate_error
|
56
|
+
return render json: {
|
57
|
+
errors: [I18n.t('devise.failure.unauthenticated')]
|
58
|
+
}, status: 401
|
59
|
+
end
|
60
|
+
METHODS
|
61
|
+
|
62
|
+
ActiveSupport.on_load(:action_controller) do
|
63
|
+
if respond_to?(:helper_method)
|
64
|
+
helper_method "current_#{mapping}", "#{mapping}_signed_in?", "render_authenticate_error"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "devise_token/rails/routes"
|
2
|
+
|
3
|
+
module DeviseToken
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace DeviseToken
|
6
|
+
|
7
|
+
initializer "devise_token.url_helpers" do
|
8
|
+
Devise.helpers << DeviseToken::Controllers::Helpers
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
mattr_accessor :token_audience,
|
13
|
+
:change_headers_on_each_request,
|
14
|
+
:token_lifespan,
|
15
|
+
:default_confirm_success_url,
|
16
|
+
:default_password_reset_url,
|
17
|
+
:redirect_whitelist,
|
18
|
+
:check_current_password_before_update,
|
19
|
+
:enable_standard_devise_support,
|
20
|
+
:remove_tokens_after_password_reset,
|
21
|
+
:default_callbacks,
|
22
|
+
:token_secret_signature_key,
|
23
|
+
:token_signature_algorithm,
|
24
|
+
:token_public_key
|
25
|
+
|
26
|
+
self.token_audience = nil
|
27
|
+
self.change_headers_on_each_request = true
|
28
|
+
self.token_lifespan = 2.weeks
|
29
|
+
self.default_confirm_success_url = nil
|
30
|
+
self.default_password_reset_url = nil
|
31
|
+
self.redirect_whitelist = nil
|
32
|
+
self.check_current_password_before_update = false
|
33
|
+
self.enable_standard_devise_support = false
|
34
|
+
self.remove_tokens_after_password_reset = false
|
35
|
+
self.default_callbacks = true
|
36
|
+
self.token_secret_signature_key = -> { Rails.application.secrets.secret_key_base }
|
37
|
+
self.token_signature_algorithm = 'HS256'
|
38
|
+
self.token_public_key = nil
|
39
|
+
|
40
|
+
|
41
|
+
def self.setup(&block)
|
42
|
+
yield self
|
43
|
+
end
|
44
|
+
end
|