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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9ad1e97c17af8e19492051f2a5c76db00e591f06
4
+ data.tar.gz: 1ee7490041ba11f1b6537f9a7ac54e1585fb1cf0
5
+ SHA512:
6
+ metadata.gz: eab33efbe9beea3f593908abd137dd3e6a769ff3844262809b33ba7baa0534fac73cb69ddf1f65da1bbe67765021525d951c762ab753601ef752bf63b2b46415
7
+ data.tar.gz: a340fdae974d327ba22725b0acf12ab95e0ec463ab7e7625e6908595ad716cb25fb5e7e442c1929a286d8bff7adcfca10ec4907276fd9cfbc9b4668e4f4bdaca
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Samuel Akrah
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,69 @@
1
+ # DeviseToken
2
+ A lightweight authentication gem based on devise and jwt gems.
3
+
4
+ ## Description
5
+ DeviseToken is a stripped down version of devise_token_auth, which removes the oauth and token implementation in devise_token_auth and replaces them with jwt for authentication.
6
+
7
+ ### What are JSON Web Tokens?
8
+
9
+ [![JWT](http://jwt.io/assets/badge.svg)](http://jwt.io/)
10
+
11
+
12
+ ## Usage
13
+
14
+ Include the `DeviseToken::Concerns::AuthenticateToken` module in your base controller, ie `ApplicationController` if it is not included by default.
15
+
16
+ ```ruby
17
+ class ApplicationController < ActionController::API
18
+ include DeviseToken::Concerns::AuthenticateToken
19
+ end
20
+ ```
21
+ This will help you scope your resources by calling `authenticate_user!` as a before_action
22
+ inside your controllers:
23
+ ```ruby
24
+ class TasksController < ApplicationController
25
+ before_action :authenticate_user!
26
+
27
+ end
28
+ ```
29
+
30
+ This will make available available methods such us `current_user` in your controller actions.
31
+
32
+ ## Installation
33
+ Add this line to your application's Gemfile:
34
+
35
+ ```ruby
36
+ gem 'devise_token'
37
+ ```
38
+
39
+ And then execute:
40
+ ```bash
41
+ $ bundle
42
+ ```
43
+
44
+ Or install it yourself as:
45
+ ```bash
46
+ $ gem install devise_token
47
+ ```
48
+
49
+ Finally, run the install generator:
50
+
51
+ ```bash
52
+ $ rails g devise_token:install User auth
53
+
54
+ This generator accepts the following optional arguments:
55
+
56
+ | Argument | Default | Description |
57
+ |---|---|---|
58
+ | USER_CLASS | `User` | The name of the class to use for user authentication. |
59
+ | MOUNT_PATH | `auth` | The path at which to mount the authentication routes.
60
+
61
+ This will create the following:
62
+
63
+ * An initializer will be created at `config/initializers/devise_token.rb`
64
+
65
+ * A model will be created in the `app/models` directory. If the model already exists, a concern will be included at the top of the file.
66
+ ## Contributing
67
+ Contribution directions go here.
68
+
69
+ ## License
@@ -0,0 +1,36 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'DeviseToken'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'test'
31
+ t.pattern = 'test/**/*_test.rb'
32
+ t.verbose = false
33
+ end
34
+
35
+
36
+ task default: :test
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/devise_token .js
2
+ //= link_directory ../stylesheets/devise_token .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,77 @@
1
+ module DeviseToken
2
+ class ApplicationController < DeviseController
3
+ include ::DeviseToken::Concerns::AuthenticateToken
4
+
5
+ before_action :set_default_format
6
+
7
+
8
+ def resource_data(opts={})
9
+ response_data = opts[:resource_json] || @resource.as_json
10
+ response_data
11
+ end
12
+
13
+ def resource_errors
14
+ return @resource.errors.to_hash.merge(full_messages: @resource.errors.full_messages)
15
+ end
16
+
17
+ protected
18
+
19
+
20
+ def params_for_resource(resource)
21
+ devise_parameter_sanitizer.instance_values['permitted'][resource].each do |type|
22
+ params[type.to_s] ||= request.headers[type.to_s] unless request.headers[type.to_s].nil?
23
+ end
24
+ devise_parameter_sanitizer.instance_values['permitted'][resource]
25
+ end
26
+
27
+ def resource_class(m=nil)
28
+ if m
29
+ mapping = Devise.mappings[m]
30
+ else
31
+ mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
32
+ end
33
+
34
+ mapping.to
35
+ end
36
+
37
+ def recoverable_enabled?
38
+ resource_class.devise_modules.include?(:recoverable)
39
+ end
40
+
41
+ def confirmable_enabled?
42
+ resource_class.devise_modules.include?(:confirmable)
43
+ end
44
+
45
+ def render_error(status, message, data = nil)
46
+ response = {
47
+ success: false,
48
+ errors: [message]
49
+ }
50
+ response = response.merge(data) if data
51
+ render json: response, status: status
52
+ end
53
+
54
+
55
+ def set_default_format
56
+ request.format = :json
57
+ end
58
+
59
+ def auth_token
60
+ if resource.respond_to? :auth_response_token
61
+ DeviseToken::JsonWebToken.new payload: resource.auth_response_token
62
+ else
63
+ DeviseToken::JsonWebToken.new payload: { sub: resource.id }
64
+ end
65
+ end
66
+
67
+ def resource
68
+ @resource ||=
69
+ if resource_class.respond_to? :auth_request_payload
70
+ resource_class.auth_request_payload request
71
+ else
72
+ resource_class.find_by email: resource_params[:email]
73
+ end
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,87 @@
1
+ module DeviseToken
2
+ class AuthenticationsController < DeviseToken::ApplicationController
3
+
4
+ def new
5
+ render_new_error
6
+ end
7
+
8
+ def create
9
+ # Check
10
+ field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first
11
+
12
+ @resource = nil
13
+ if field
14
+ q_value = get_case_insensitive_field_from_resource_params(field)
15
+
16
+ @resource = find_resource(field, q_value)
17
+ end
18
+
19
+ if @resource && valid_params?(field, q_value) && (!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
20
+ valid_password = @resource.valid_password?(resource_params[:password])
21
+ if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
22
+ render_create_error_bad_credentials
23
+ return
24
+ end
25
+
26
+ @resource.save
27
+
28
+ sign_in(:user, @resource, store: false, bypass: false)
29
+
30
+ yield @resource if block_given?
31
+
32
+ render_create_success
33
+
34
+ elsif @resource && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
35
+ render_create_error_not_confirmed
36
+ else
37
+ render_create_error_bad_credentials
38
+ end
39
+ end
40
+
41
+
42
+
43
+ protected
44
+
45
+ def valid_params?(key, val)
46
+ resource_params[:password] && key && val
47
+ end
48
+
49
+
50
+ def render_new_error
51
+ render_error(405, I18n.t("devise_token.sessions.not_supported"))
52
+ end
53
+
54
+ def render_create_success
55
+ render json: {
56
+ status: 'success',
57
+ header: auth_token,
58
+ data: resource_data
59
+ }
60
+ end
61
+
62
+ def render_create_error_not_confirmed
63
+ render_error(401, I18n.t("devise_token.sessions.not_confirmed", email: @resource.email))
64
+ end
65
+
66
+ def render_create_error_bad_credentials
67
+ render_error(401, I18n.t("devise_token.sessions.bad_credentials"))
68
+ end
69
+
70
+ def render_destroy_success
71
+ render json: {
72
+ success:true
73
+ }, status: 200
74
+ end
75
+
76
+ def render_destroy_error
77
+ render_error(404, I18n.t("devise_token.sessions.user_not_found"))
78
+ end
79
+
80
+ private
81
+
82
+ def resource_params
83
+ params.permit(*params_for_resource(:sign_in))
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,71 @@
1
+ module DeviseToken::Concerns::AuthenticateToken
2
+ extend ActiveSupport::Concern
3
+ include DeviseToken::Controllers::Helpers
4
+ include DeviseToken::Authenticable
5
+
6
+
7
+
8
+ def get_case_insensitive_field_from_resource_params(field)
9
+ # honor Devise configuration for case_insensitive keys
10
+ q_value = resource_params[field.to_sym]
11
+
12
+ if resource_class.case_insensitive_keys.include?(field.to_sym)
13
+ q_value.downcase!
14
+ end
15
+ q_value
16
+ end
17
+
18
+ def find_resource(field, value)
19
+ # fix for mysql default case insensitivity
20
+ q = "#{field.to_s} = ? AND provider='#{provider.to_s}'"
21
+ if ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'mysql'
22
+ q = "BINARY " + q
23
+ end
24
+
25
+ @resource = resource_class.where(q, value).first
26
+ end
27
+
28
+ def resource_class(m=nil)
29
+ if m
30
+ mapping = Devise.mappings[m]
31
+ else
32
+ mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
33
+ end
34
+
35
+ mapping.to
36
+ end
37
+
38
+ def provider
39
+ 'email'
40
+ end
41
+
42
+
43
+ protected
44
+
45
+
46
+ # user auth
47
+ def authenticate_token(mapping=nil)
48
+ # determine target authentication class
49
+ rc = resource_class(mapping)
50
+ @token = token
51
+
52
+ return false unless @token
53
+ # no default user defined
54
+ return unless rc
55
+ @resource = authenticate_auth(rc)
56
+ # user has already been found and authenticated
57
+ return @resource if @resource && @resource.is_a?(rc)
58
+
59
+ end
60
+
61
+ def resource_class(m=nil)
62
+ if m
63
+ mapping = Devise.mappings[m]
64
+ else
65
+ mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
66
+ end
67
+
68
+ mapping.to
69
+ end
70
+
71
+ end
@@ -0,0 +1,18 @@
1
+ module DeviseToken
2
+ class ConfirmationsController < DeviseToken::ApplicationController
3
+ def show
4
+ @resource = resource_class.confirm_by_token(params[:confirmation_token])
5
+
6
+ if @resource && @resource.id
7
+
8
+ sign_in(@resource)
9
+ @resource.save!
10
+
11
+ yield @resource if block_given?
12
+
13
+ else
14
+ raise ActionController::RoutingError.new('Not Found')
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,189 @@
1
+ module DeviseToken
2
+ class RegistrationsController < DeviseToken::ApplicationController
3
+ before_action :validate_sign_up_params, :only => :create
4
+ before_action :validate_account_update_params, :only => :update
5
+
6
+ def create
7
+ @resource = resource_class.new(sign_up_params.except(:confirm_success_url))
8
+ @resource.provider = provider
9
+
10
+
11
+ if resource_class.case_insensitive_keys.include?(:email)
12
+ @resource.email = sign_up_params[:email].try :downcase
13
+ else
14
+ @resource.email = sign_up_params[:email]
15
+ end
16
+
17
+
18
+ @redirect_url = sign_up_params[:confirm_success_url]
19
+
20
+
21
+ @redirect_url ||= DeviseToken.default_confirm_success_url
22
+
23
+
24
+ if confirmable_enabled? && !@redirect_url
25
+ return render_create_error_missing_confirm_success_url
26
+ end
27
+
28
+
29
+ if DeviseToken.redirect_whitelist
30
+ unless DeviseToken::Url.whitelisted?(@redirect_url)
31
+ return render_create_error_redirect_url_not_allowed
32
+ end
33
+ end
34
+
35
+ begin
36
+ # override email confirmation, must be sent manually from ctrl
37
+ resource_class.set_callback("create", :after, :send_on_create_confirmation_instructions)
38
+ resource_class.skip_callback("create", :after, :send_on_create_confirmation_instructions)
39
+ if @resource.respond_to? :skip_confirmation_notification!
40
+ # Fix duplicate e-mails by disabling Devise confirmation e-mail
41
+ @resource.skip_confirmation_notification!
42
+ end
43
+ if @resource.save
44
+ yield @resource if block_given?
45
+
46
+ unless @resource.confirmed?
47
+ # user will require email authentication
48
+ @resource.send_confirmation_instructions({
49
+ redirect_url: @redirect_url
50
+ })
51
+
52
+ else
53
+
54
+ @resource.save!
55
+
56
+
57
+ end
58
+ render_create_success
59
+ else
60
+ clean_up_passwords @resource
61
+ render_create_error
62
+ end
63
+ rescue ActiveRecord::RecordNotUnique
64
+ clean_up_passwords @resource
65
+ render_create_error_email_already_exists
66
+ end
67
+ end
68
+
69
+
70
+ def destroy
71
+ if @resource
72
+ @resource.destroy
73
+ yield @resource if block_given?
74
+
75
+ render_destroy_success
76
+ else
77
+ render_destroy_error
78
+ end
79
+ end
80
+
81
+ def sign_up_params
82
+ params.permit([*params_for_resource(:sign_up), :confirm_success_url])
83
+ end
84
+
85
+ def account_update_params
86
+ params.permit(*params_for_resource(:account_update))
87
+ end
88
+
89
+ protected
90
+
91
+ def render_create_error_missing_confirm_success_url
92
+ response = {
93
+ status: 'error',
94
+ data: resource_data
95
+ }
96
+ message = I18n.t("devise_token_auth.registrations.missing_confirm_success_url")
97
+ render_error(422, message, response)
98
+ end
99
+
100
+ def render_create_error_redirect_url_not_allowed
101
+ response = {
102
+ status: 'error',
103
+ data: resource_data
104
+ }
105
+ message = I18n.t("devise_token_auth.registrations.redirect_url_not_allowed", redirect_url: @redirect_url)
106
+ render_error(422, message, response)
107
+ end
108
+
109
+ def render_create_success
110
+ render json: {
111
+ status: 'success',
112
+ header: auth_token,
113
+ data: resource_data
114
+ }
115
+ end
116
+
117
+ def render_create_error
118
+ render json: {
119
+ status: 'error',
120
+ data: resource_data,
121
+ errors: resource_errors
122
+ }, status: 422
123
+ end
124
+
125
+ def render_create_error_email_already_exists
126
+ response = {
127
+ status: 'error',
128
+ data: resource_data
129
+ }
130
+ message = I18n.t("devise_token_auth.registrations.email_already_exists", email: @resource.email)
131
+ render_error(422, message, response)
132
+ end
133
+
134
+ def render_update_success
135
+ render json: {
136
+ status: 'success',
137
+ data: resource_data
138
+ }
139
+ end
140
+
141
+ def render_update_error
142
+ render json: {
143
+ status: 'error',
144
+ errors: resource_errors
145
+ }, status: 422
146
+ end
147
+
148
+ def render_update_error_user_not_found
149
+ render_error(404, I18n.t("devise_token_auth.registrations.user_not_found"), { status: 'error' })
150
+ end
151
+
152
+ def render_destroy_success
153
+ render json: {
154
+ status: 'success',
155
+ message: I18n.t("devise_token_auth.registrations.account_with_uid_destroyed", uid: @resource.uid)
156
+ }
157
+ end
158
+
159
+ def render_destroy_error
160
+ render_error(404, I18n.t("devise_token_auth.registrations.account_to_destroy_not_found"), { status: 'error' })
161
+ end
162
+
163
+ private
164
+
165
+ def resource_update_method
166
+ if DeviseToken.check_current_password_before_update == :attributes
167
+ "update_with_password"
168
+ elsif DeviseToken.check_current_password_before_update == :password && account_update_params.has_key?(:password)
169
+ "update_with_password"
170
+ elsif account_update_params.has_key?(:current_password)
171
+ "update_with_password"
172
+ else
173
+ "update_attributes"
174
+ end
175
+ end
176
+
177
+ def validate_sign_up_params
178
+ validate_post_data sign_up_params, I18n.t("errors.messages.validate_sign_up_params")
179
+ end
180
+
181
+ def validate_account_update_params
182
+ validate_post_data account_update_params, I18n.t("errors.messages.validate_account_update_params")
183
+ end
184
+
185
+ def validate_post_data which, message
186
+ render_error(:unprocessable_entity, message, { status: 'error' }) if which.empty?
187
+ end
188
+ end
189
+ end