devise_api_auth 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d64c0bde2b6418360e798e9f84644c4cce2f202
4
+ data.tar.gz: 12a5b6bc4a801786d567d1c05f6535f89ec997f8
5
+ SHA512:
6
+ metadata.gz: 3f46f95e88fbf63cacc0b73f48637f10d3ca5749cdb76ec2b5ffa7b22d01fe7d494ef5b4e80c9c08c8ee144582dee135b862fb852f84c8157eadb64300b357a9
7
+ data.tar.gz: 1643aa19a2f8a12787a57c2297614c8d3926676208d940a21048460c2eb069561987b6ff59952308c141adfc43752da6848a1d0eda04f02b2608d636b02e8542
@@ -0,0 +1,71 @@
1
+ require 'devise'
2
+ module ApiTokenAuthentication
3
+
4
+ class ApiTokenAuthenticatable < Devise::Strategies::Authenticatable
5
+ def valid?
6
+ _token_matcher.valid?
7
+ end
8
+
9
+ def authenticate!
10
+ if _token_matcher.match! && !_user.nil?
11
+ success!(_user)
12
+ else
13
+ fail('Failed api token authentication')
14
+ end
15
+ end
16
+
17
+ private
18
+ def _validate_user
19
+ return true unless _config_options.include?(:model_auth_validation_method)
20
+ _user.send(_config_options[:model_auth_validation_method].to_sym)
21
+ end
22
+
23
+ def _token_utils
24
+ DeviseApiAuth::TokenUtils::SecureCredentials.new(headers: request.headers)
25
+ end
26
+
27
+ def _requested_time
28
+ _secure_credentials[:date]
29
+ end
30
+
31
+ def _user_token
32
+ _secure_credentials[:token]
33
+ end
34
+
35
+ def _user_id
36
+ _secure_credentials[:id]
37
+ end
38
+
39
+ def _secure_credentials
40
+ @_secure_credentials ||= _token_utils.get_header_credentials
41
+ end
42
+
43
+ def _token_matcher
44
+ @_token_matcher ||= DeviseApiAuth::TokenUtils::DateTokenMatcher.new(strings: [_requested_time, DeviseApiAuth::TokenUtils.generate_user_token(_user_id)], date: _requested_time, token: _user_token)
45
+ end
46
+
47
+ def _user
48
+ @_user ||= begin
49
+ return if _user_id.blank?
50
+ if _config_options[:model_find_method].nil?
51
+ mapping.to.where(_config_options[:model_id_attribute].to_sym => _user_id).first
52
+ else
53
+ mapping.to.send(_config_options[:model_find_method].to_sym, _user_id)
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ def _config_options
60
+ DeviseApiAuth::Config.options
61
+ end
62
+
63
+ end
64
+
65
+
66
+ end
67
+
68
+ Warden::Strategies.add(:api_token_authentication, ApiTokenAuthentication::ApiTokenAuthenticatable)
69
+ Devise.add_module :api_token_authentication, strategy: true
70
+
71
+
@@ -0,0 +1,35 @@
1
+ module DeviseApiAuth
2
+ module Config
3
+ extend self
4
+ attr_reader :options
5
+ def configure
6
+ yield self.options
7
+ end
8
+ def options
9
+ @options ||= {
10
+ header_iv: 'x-app-iv',
11
+ header_credentials: 'x-app-credentials',
12
+ param_iv: '_iv',
13
+ param_credentials: '_credentials',
14
+ model_id_attribute: 'id'
15
+ }
16
+ end
17
+ end
18
+ end
19
+
20
+
21
+ # DeviseApiAuth::Config.configure do |options|
22
+ # options[:app_token] # mobile app has this as well and it is used for csrf
23
+ # options[:encryption_key] # mobile app uses this to encrypt and decrypt
24
+ # options[:user_salt] # rails uses this to create a user token from a user
25
+
26
+ # # optional
27
+ # options[:header_iv] # name of the iv field in the header
28
+ # options[:header_credentials] # name of credentials field in header
29
+ # options[:param_iv] # name of the iv field in the parameters
30
+ # options[:param_credentials] # name of credentials field in parameters
31
+ # options[:model_id_attribute] # the field that is used to search for the user model in authentication
32
+ # options[:model_find_method] # passes the user identifier to a class method on the model to act as initialization and verification. Return an instance of your user model on succes and nil upon failure.
33
+ # options[:model_auth_validation_method] # name of method to run on the model during authentication
34
+
35
+ # end
@@ -0,0 +1,39 @@
1
+ module DeviseApiAuth
2
+ module DateCSRF
3
+
4
+ def verified_request?
5
+ super || app_token_verified?
6
+ end
7
+
8
+ def app_token_verified?
9
+ verify_app_token
10
+ end
11
+
12
+ def verify_app_token
13
+ TokenUtils::DateTokenMatcher.new(strings: [_requested_time,DeviseApiAuth::Config.options[:app_token]],date: _requested_time, token: _date_token).match!
14
+ end
15
+
16
+ private
17
+ def _token_utils
18
+ @_token_utils ||= TokenUtils::SecureCredentials.new(headers: request.headers, params: params)
19
+ end
20
+
21
+ def _requested_time
22
+ _secure_header_credentials[:date]
23
+ end
24
+
25
+ def _date_token
26
+ _secure_param_credentials[:token]
27
+ end
28
+
29
+ def _secure_header_credentials
30
+ _token_utils.get_header_credentials
31
+ end
32
+
33
+ def _secure_param_credentials
34
+ _token_utils.get_param_credentials
35
+ end
36
+
37
+
38
+ end
39
+ end
@@ -0,0 +1,175 @@
1
+ module DeviseApiAuth
2
+ module TokenUtils
3
+ extend self
4
+ require 'openssl'
5
+
6
+ def generate_user_token(string, digester: Digest::SHA2)
7
+ return if !string.is_a?(String) || string.blank?
8
+ digester.hexdigest(string+DeviseApiAuth::Config.options[:user_salt])
9
+ end
10
+
11
+ class SecureCredentials
12
+ def initialize(headers: nil, params: nil)
13
+ @headers = headers
14
+ @params = params
15
+ end
16
+
17
+ def get_header_credentials
18
+ decrypt(encrypted_header_credentials, iv: header_iv).symbolize_keys
19
+ end
20
+
21
+ def get_param_credentials
22
+ decrypt(encrypted_param_credentials, iv: params_iv).symbolize_keys
23
+ end
24
+
25
+ def header_iv
26
+ @headers&.fetch(config_options[:header_iv],nil)
27
+ end
28
+
29
+ def params_iv
30
+ @params&.fetch(config_options[:param_iv],nil)
31
+ end
32
+
33
+ def encrypted_header_credentials
34
+ @headers&.fetch(config_options[:header_credentials],nil)
35
+ end
36
+
37
+ def encrypted_param_credentials
38
+ @params&.fetch(config_options[:param_credentials],nil)
39
+ end
40
+
41
+ protected
42
+ def decrypt(encrypted, iv:)
43
+ decrypted = DeviseApiAuth::TokenUtils::Decryptor.new(iv).decrypt(encrypted)
44
+ JSON.parse decrypted
45
+ rescue StandardError => e
46
+ {}
47
+ end
48
+
49
+ def config_options
50
+ DeviseApiAuth::Config.options
51
+ end
52
+
53
+ end
54
+
55
+
56
+ private
57
+ class Crypt
58
+ # Subclasses must provide block to initializer to set encrypt or decrypt on the cipher
59
+
60
+ # Accepts iv as hex string
61
+ def initialize(iv)
62
+ @failed = false
63
+ @cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
64
+ yield @cipher # setting encrypt or decrypt to the cipher
65
+ @cipher.key = DeviseApiAuth::Config.options[:encryption_key]
66
+ @cipher.iv = [iv].pack('H*')
67
+ rescue StandardError => e
68
+ @failed = true
69
+ end
70
+
71
+ private
72
+ def crypt(to_crypt)
73
+ return if @failed
74
+ crypted = @cipher.update(to_crypt)
75
+ crypted << @cipher.final
76
+ rescue StandardError => e
77
+ nil
78
+ end
79
+ end
80
+
81
+ public
82
+ class Decryptor < Crypt
83
+ def initialize(iv)
84
+ super {|cipher| cipher.decrypt}
85
+ end
86
+
87
+ # Accepts an encrypted hex string and returns plain text
88
+ def decrypt(encrypted)
89
+ return if encrypted.blank?
90
+ crypt([encrypted].pack('H*'))
91
+ end
92
+
93
+ end
94
+
95
+ class Encryptor < Crypt
96
+ def initialize(iv)
97
+ super{|cipher| cipher.encrypt}
98
+ end
99
+
100
+ # Accepts string and returns encrypted hex string
101
+ def encrypt(encrypted)
102
+ crypt(encrypted)&.unpack('H*')&.first
103
+ end
104
+
105
+ end
106
+
107
+
108
+ class SimpleTokenMatcher
109
+ def initialize(strings: nil, token: nil, digester: nil)
110
+ @digester = digester || Digest::SHA2
111
+ @string = strings
112
+ if @string.is_a?(Array)
113
+ @string = @string.include?(nil) ? nil : strings.join
114
+ end
115
+ @token = token
116
+ @_token = token_for @string
117
+ end
118
+ def match!
119
+ valid? && @_token == @token
120
+ end
121
+ def token_for(string)
122
+ return unless string.is_a?(String) && !string.blank?
123
+ @digester.hexdigest(string)
124
+ end
125
+ def valid?
126
+ !@_token.blank?
127
+ end
128
+ end
129
+
130
+ class DateTokenMatcher < SimpleTokenMatcher
131
+ # adds date and options hash that contains a verify_date bool and cutoff which is the amount of seconds to allow the date to be within
132
+ def initialize(strings: nil, token: nil, digester: nil, date: nil, **options)
133
+ @options = {verify_date: true, cutoff: 60 * 10}.merge(options)
134
+ self.date = date
135
+ super(strings: strings, token: token, digester: digester)
136
+ end
137
+ def match!
138
+ return false if verify_date? && !verify_date!
139
+ super
140
+ end
141
+ def valid?
142
+ return super if !verify_date?
143
+ verify_date! && super
144
+ end
145
+
146
+ private
147
+ def date=(d)
148
+ if d.is_a? String
149
+ @date = DateTime.parse(d)
150
+ elsif d.is_a? DateTime
151
+ @date = d
152
+ end
153
+ rescue ArgumentError
154
+ @date = nil
155
+ end
156
+ def verify_date!
157
+ return false if @date.nil?
158
+ a = cutoff.seconds.ago.to_datetime
159
+ b = cutoff.seconds.since.to_datetime
160
+ @date.between?(a,b)
161
+ end
162
+ def verify_date?
163
+ !!(@options[:verify_date])
164
+ end
165
+ def cutoff
166
+ [@options[:cutoff].to_i, 30].max
167
+ end
168
+
169
+ end
170
+
171
+ end
172
+
173
+
174
+
175
+ end
@@ -0,0 +1,13 @@
1
+ require 'devise_api_auth/api_token_authentication'
2
+ require 'devise_api_auth/config'
3
+ require 'devise_api_auth/date_csrf'
4
+ require 'devise_api_auth/token_utils'
5
+
6
+ module DeviseApiAuth
7
+ # VERSION = '0.0.1'
8
+ end
9
+
10
+
11
+
12
+
13
+
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise_api_auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jose Castellanos
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: devise
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: This gem provides functionality to authenticate mobile apps in addition
42
+ to web apps using devise.
43
+ email: nextgenappsllc@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/devise_api_auth.rb
49
+ - lib/devise_api_auth/api_token_authentication.rb
50
+ - lib/devise_api_auth/config.rb
51
+ - lib/devise_api_auth/date_csrf.rb
52
+ - lib/devise_api_auth/token_utils.rb
53
+ homepage: http://rubygems.org/gems/devise_api_auth
54
+ licenses:
55
+ - MIT
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - "~>"
64
+ - !ruby/object:Gem::Version
65
+ version: '2.3'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 2.5.1
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Devise API Authentication
77
+ test_files: []