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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +69 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/devise_token_manifest.js +2 -0
  6. data/app/assets/javascripts/devise_token/application.js +13 -0
  7. data/app/assets/stylesheets/devise_token/application.css +15 -0
  8. data/app/controllers/devise_token/application_controller.rb +77 -0
  9. data/app/controllers/devise_token/authentications_controller.rb +87 -0
  10. data/app/controllers/devise_token/concerns/authenticate_token.rb +71 -0
  11. data/app/controllers/devise_token/confirmations_controller.rb +18 -0
  12. data/app/controllers/devise_token/registrations_controller.rb +189 -0
  13. data/app/controllers/devise_token/token_validations_controller.rb +29 -0
  14. data/app/helpers/devise_token/application_helper.rb +4 -0
  15. data/app/jobs/devise_token/application_job.rb +4 -0
  16. data/app/mailers/devise_token/application_mailer.rb +6 -0
  17. data/app/models/devise_token/application_record.rb +5 -0
  18. data/app/models/devise_token/concerns/user.rb +98 -0
  19. data/app/models/devise_token/json_web_token.rb +80 -0
  20. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  21. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  22. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  23. data/config/locales/en.yml +50 -0
  24. data/config/routes.rb +2 -0
  25. data/lib/devise_token.rb +8 -0
  26. data/lib/devise_token/authenticable.rb +23 -0
  27. data/lib/devise_token/controllers/helpers.rb +70 -0
  28. data/lib/devise_token/controllers/url_helpers.rb +8 -0
  29. data/lib/devise_token/engine.rb +44 -0
  30. data/lib/devise_token/rails/routes.rb +66 -0
  31. data/lib/devise_token/version.rb +3 -0
  32. data/lib/generators/devise_token/install_generator.rb +160 -0
  33. data/lib/generators/devise_token/install_views_generator.rb +16 -0
  34. data/lib/generators/devise_token/templates/devise_token.rb +16 -0
  35. data/lib/generators/devise_token/templates/devise_token_create_users.rb.erb +55 -0
  36. data/lib/tasks/devise_token_tasks.rake +4 -0
  37. 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,4 @@
1
+ module DeviseToken
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module DeviseToken
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module DeviseToken
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module DeviseToken
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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,5 @@
1
+ <p><%= t(:welcome).capitalize + ' ' + @email %>!</p>
2
+
3
+ <p><%= t '.confirm_link_msg' %> </p>
4
+
5
+ <p><%= link_to t('.confirm_account_link'), confirmation_url(@resource, {confirmation_token: @token, redirect_url: message['redirect-url']}).html_safe %></p>
@@ -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,7 @@
1
+ <p><%= t :hello %> <%= @resource.email %>!</p>
2
+
3
+ <p><%= t '.account_lock_msg' %></p>
4
+
5
+ <p><%= t '.unlock_link_msg' %></p>
6
+
7
+ <p><%= link_to t('.unlock_link'), unlock_url(@resource, unlock_token: @token, config: message['client-config'].to_s) %></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"
@@ -0,0 +1,2 @@
1
+ DeviseToken::Engine.routes.draw do
2
+ end
@@ -0,0 +1,8 @@
1
+ require "devise"
2
+ require "devise_token/engine"
3
+ require "devise_token/controllers/helpers"
4
+ require "devise_token/controllers/url_helpers"
5
+ require "devise_token/authenticable"
6
+ module DeviseToken
7
+ # Your code goes here...
8
+ end
@@ -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,8 @@
1
+ module DeviseToken
2
+ module Controllers
3
+ module UrlHelpers
4
+ def self.define_helpers(mapping)
5
+ end
6
+ end
7
+ end
8
+ 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