api_user_auth 0.0.12

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 (90) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +36 -0
  4. data/Rakefile +53 -0
  5. data/app/assets/config/api_user_auth_manifest.js +2 -0
  6. data/app/assets/javascripts/api_user_auth/application.js +15 -0
  7. data/app/assets/stylesheets/api_user_auth/application.css +15 -0
  8. data/app/controllers/api_user_auth/auth_controller.rb +75 -0
  9. data/app/helpers/api_user_auth/application_helper.rb +5 -0
  10. data/app/jobs/api_user_auth/application_job.rb +4 -0
  11. data/app/mailers/api_user_auth/application_mailer.rb +7 -0
  12. data/app/mailers/api_user_auth/forgot_password_mailer.rb +12 -0
  13. data/app/mailers/api_user_auth/welcome_mailer.rb +11 -0
  14. data/app/models/api_user_auth/application_record.rb +5 -0
  15. data/app/models/api_user_auth/auth_user.rb +167 -0
  16. data/app/views/api_user_auth/forgot_password_mailer/reset_code.html.erb +3 -0
  17. data/app/views/api_user_auth/welcome_mailer/welcome.html.erb +2 -0
  18. data/app/views/layouts/api_user_auth/application.html.erb +16 -0
  19. data/config/routes.rb +12 -0
  20. data/db/migrate/20180703111608_create_api_user_auth_auth_users.rb +16 -0
  21. data/lib/api_user_auth/controller.rb +39 -0
  22. data/lib/api_user_auth/engine.rb +10 -0
  23. data/lib/api_user_auth/exceptions.rb +9 -0
  24. data/lib/api_user_auth/providers/facebook.rb +61 -0
  25. data/lib/api_user_auth/providers/google.rb +59 -0
  26. data/lib/api_user_auth/providers/instagram.rb +53 -0
  27. data/lib/api_user_auth/version.rb +3 -0
  28. data/lib/api_user_auth.rb +11 -0
  29. data/lib/generators/api_user_auth_generator.rb +7 -0
  30. data/lib/tasks/api_user_auth_tasks.rake +4 -0
  31. data/spec/controllers/api_user_auth/auth_controller_spec.rb +344 -0
  32. data/spec/controllers/api_user_auth/controller_spec.rb +39 -0
  33. data/spec/dummy/Rakefile +6 -0
  34. data/spec/dummy/app/assets/config/manifest.js +4 -0
  35. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  36. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  37. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  38. data/spec/dummy/app/controllers/test_controller.rb +8 -0
  39. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  40. data/spec/dummy/app/jobs/application_job.rb +2 -0
  41. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  42. data/spec/dummy/app/models/application_record.rb +3 -0
  43. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  44. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  45. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  46. data/spec/dummy/bin/bundle +3 -0
  47. data/spec/dummy/bin/rails +4 -0
  48. data/spec/dummy/bin/rake +4 -0
  49. data/spec/dummy/bin/setup +36 -0
  50. data/spec/dummy/bin/update +31 -0
  51. data/spec/dummy/bin/yarn +11 -0
  52. data/spec/dummy/config/application.rb +30 -0
  53. data/spec/dummy/config/boot.rb +5 -0
  54. data/spec/dummy/config/database.yml +16 -0
  55. data/spec/dummy/config/environment.rb +5 -0
  56. data/spec/dummy/config/environments/development.rb +63 -0
  57. data/spec/dummy/config/environments/production.rb +89 -0
  58. data/spec/dummy/config/environments/test.rb +46 -0
  59. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  60. data/spec/dummy/config/initializers/assets.rb +14 -0
  61. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  62. data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
  63. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  64. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  65. data/spec/dummy/config/initializers/inflections.rb +16 -0
  66. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  67. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  68. data/spec/dummy/config/locales/en.yml +33 -0
  69. data/spec/dummy/config/puma.rb +34 -0
  70. data/spec/dummy/config/routes.rb +4 -0
  71. data/spec/dummy/config/spring.rb +6 -0
  72. data/spec/dummy/config/storage.yml +34 -0
  73. data/spec/dummy/config.ru +5 -0
  74. data/spec/dummy/db/development.sqlite3 +0 -0
  75. data/spec/dummy/db/schema.rb +31 -0
  76. data/spec/dummy/db/test.sqlite3 +0 -0
  77. data/spec/dummy/log/development.log +507 -0
  78. data/spec/dummy/log/test.log +42941 -0
  79. data/spec/dummy/package.json +5 -0
  80. data/spec/dummy/public/404.html +67 -0
  81. data/spec/dummy/public/422.html +67 -0
  82. data/spec/dummy/public/500.html +66 -0
  83. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  84. data/spec/dummy/public/apple-touch-icon.png +0 -0
  85. data/spec/dummy/public/favicon.ico +0 -0
  86. data/spec/models/api_user_auth/auth_user_spec.rb +16 -0
  87. data/spec/rails_helper.rb +61 -0
  88. data/spec/spec_helper.rb +93 -0
  89. data/spec/support/request_spec_helper.rb +13 -0
  90. metadata +273 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7e9ccdf1f2cf33bd87ef4749039eba166b88f5ee6672a5c7d9bcce66fe7f71be
4
+ data.tar.gz: 4aa20626e6910277f8e91d8f81bfe370e2676d90e7f8fe1cbd2548faa6cf755e
5
+ SHA512:
6
+ metadata.gz: 2413f31cedcce9f135953583590c298f5665e06c04a94b1ba6d202618d65d8d6b95ccb3d141a8312778efb36659e8394a3d00cc5e323b860bc089c1078d4a261
7
+ data.tar.gz: 8207fe9d20aaa88a85b353a6b6f9e6d236ebce8e49619013729325f113e64b84f9b7696dc7bb0f197a630ab92dc0bda55077b7b761ad6a04743a80e51d36371b
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Alex M
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.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # ApiUserAuth
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'api_user_auth'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install api_user_auth
22
+ ```
23
+
24
+ Run rails generator:
25
+ ```bash
26
+ rails generate api_user_auth
27
+ # OR
28
+ rails g api_user_auth
29
+ ```
30
+
31
+
32
+ ## Contributing
33
+ Contribution directions go here.
34
+
35
+ ## License
36
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,53 @@
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 = 'ApiUserAuth'
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("spec/dummy/Rakefile", __dir__)
18
+ # load 'rails/tasks/engine.rake'
19
+ #
20
+ # load 'rails/tasks/statistics.rake'
21
+ #
22
+ # require 'bundler/gem_tasks'
23
+ #
24
+ # require 'rake/testtask'
25
+ #
26
+ # Rake::TestTask.new(:test) do |t|
27
+ # t.libs << 'test'
28
+ # t.pattern = 'test/**/*_test.rb'
29
+ # t.verbose = false
30
+ # end
31
+ #
32
+ # task default: :test
33
+
34
+ begin
35
+ require 'bundler/setup'
36
+ rescue LoadError
37
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
38
+ end
39
+
40
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
41
+ load 'rails/tasks/engine.rake'
42
+
43
+ Bundler::GemHelper.install_tasks
44
+
45
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f }
46
+
47
+ require 'rspec/core'
48
+ require 'rspec/core/rake_task'
49
+
50
+ desc 'Run all specs in spec directory (excluding plugin specs)'
51
+ RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
52
+
53
+ task default: :spec
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/api_user_auth .js
2
+ //= link_directory ../stylesheets/api_user_auth .css
@@ -0,0 +1,15 @@
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 rails-ujs
14
+ //= require activestorage
15
+ //= 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,75 @@
1
+ module ApiUserAuth
2
+ # Registration/Login controller
3
+ class AuthController < ActionController::API
4
+ rescue_from Exceptions::Unauthorized, with: :auth_user_unauthorized
5
+ rescue_from Exceptions::WrongParams, with: :auth_user_wrong_params
6
+ rescue_from ApiUserAuth::Exceptions::ProviderError,
7
+ with: :auth_user_provider
8
+ rescue_from ::ActiveRecord::RecordNotFound, with: :auth_user_not_found
9
+
10
+ def create
11
+ auth_user = AuthUser.create_by_params(base_params)
12
+ render json: auth_user.to_json, status: 201
13
+ end
14
+
15
+ def login
16
+ auth_user = AuthUser.login_by_params(base_params)
17
+ render json: auth_user.to_json, status: 200
18
+ end
19
+
20
+ def password
21
+ auth_user = AuthUser.update_password(
22
+ params.permit(:email, :password, :code).to_h
23
+ )
24
+ render json: auth_user.to_json, status: 200
25
+ end
26
+
27
+ def forgot_password
28
+ auth_user = AuthUser.forgot_password(
29
+ params.permit(:email)
30
+ )
31
+ render json: auth_user.to_json
32
+ end
33
+
34
+ def logout
35
+ if request.headers['Authorization'].blank?
36
+ raise Exceptions::Unauthorized,
37
+ 'Header [Authorization] can not be blank!'
38
+ end
39
+ token = request.headers['Authorization'].sub(/Bearer\s*=?/, '')
40
+ auth_user = AuthUser.find_fy_token(token)
41
+ if auth_user.present? && auth_user.logout(token)
42
+ render json: {}, status: 200
43
+ else
44
+ render json: {}, status: 400
45
+ end
46
+ end
47
+
48
+ def provider
49
+ auth_user = AuthUser.create_by_provider(params)
50
+ render json: auth_user.to_json
51
+ end
52
+
53
+ private
54
+
55
+ def base_params
56
+ params.permit(:email, :password).to_h
57
+ end
58
+
59
+ def auth_user_not_found
60
+ render json: {}, status: 404
61
+ end
62
+
63
+ def auth_user_unauthorized(exception)
64
+ render json: { message: exception.message || 'Unauthorized' }, status: 401
65
+ end
66
+
67
+ def auth_user_provider(exception)
68
+ render json: { message: exception.message }, status: 422
69
+ end
70
+
71
+ def auth_user_wrong_params(exception)
72
+ render json: { message: exception.message }, status: 422
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ module ApiUserAuth
2
+ # Application helper
3
+ module ApplicationHelper
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module ApiUserAuth
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module ApiUserAuth
2
+ # Application mailer
3
+ class ApplicationMailer < ActionMailer::Base
4
+ default from: 'from@example.com'
5
+ layout 'mailer'
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module ApiUserAuth
2
+ # Forgot password mailer
3
+ class ForgotPasswordMailer < ActionMailer::Base
4
+ default from: 'support@example.com'
5
+
6
+ def reset_code(user)
7
+ @email = user.email
8
+ @code = user.code
9
+ mail to: user.email, subject: 'Password Code Reset'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module ApiUserAuth
2
+ # Welcome mailer
3
+ class WelcomeMailer < ActionMailer::Base
4
+ default from: 'support@example.com'
5
+
6
+ def welcome(user)
7
+ @email = user.email
8
+ mail to: user.email, subject: 'Welcome !'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module ApiUserAuth
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,167 @@
1
+ module ApiUserAuth
2
+ # Base user auth model
3
+ class AuthUser < ApplicationRecord
4
+ after_create :send_welcome
5
+
6
+ attr_accessor :is_new
7
+
8
+ def self.create_by_params(params)
9
+ if params[:email].blank?
10
+ raise Exceptions::WrongParams, 'Email can not be blank!'
11
+ end
12
+ if params[:password].blank?
13
+ raise Exceptions::WrongParams, 'Password can not be blank!'
14
+ end
15
+ auth_user = AuthUser.find_or_initialize_by(email: params[:email])
16
+
17
+ if auth_user.new_record?
18
+ auth_user.is_new = true
19
+ auth_user.update_password(params[:password])
20
+ else
21
+ raise Exceptions::WrongParams, 'User already exists !'
22
+ end
23
+ auth_user
24
+ end
25
+
26
+ def self.login_by_params(params)
27
+ if params[:email].blank?
28
+ raise Exceptions::WrongParams, 'Email can not be blank!'
29
+ end
30
+ if params[:password].blank?
31
+ raise Exceptions::WrongParams, 'Password can not be blank!'
32
+ end
33
+
34
+ auth_user = AuthUser.find_by(email: params[:email])
35
+
36
+ if auth_user && auth_user.valid_password?(params[:password])
37
+ auth_user.generate_token
38
+ auth_user.save
39
+ auth_user.is_new = false
40
+ else
41
+ raise Exceptions::Unauthorized, 'Invalid Email or Password!'
42
+ end
43
+ auth_user
44
+ end
45
+
46
+ def self.update_password(params)
47
+ if params[:email].blank?
48
+ raise Exceptions::WrongParams, 'Email can not be blank!'
49
+ end
50
+ if params[:password].blank?
51
+ raise Exceptions::WrongParams, 'Password can not be blank!'
52
+ end
53
+ if params[:code].blank?
54
+ raise Exceptions::WrongParams, 'Code can not be blank!'
55
+ end
56
+ auth_user = AuthUser.find_by(email: params[:email])
57
+
58
+ if auth_user.blank?
59
+ raise Exceptions::WrongParams, 'Email is invalid!'
60
+ end
61
+
62
+ if auth_user.code.eql?(params[:code])
63
+ auth_user.update_password(params[:password])
64
+ else
65
+ raise Exceptions::WrongParams, 'Code is invalid!'
66
+ end
67
+ end
68
+
69
+ def update_password(password)
70
+ self.password = password
71
+ generate_token
72
+ save
73
+ end
74
+
75
+ def self.forgot_password(params)
76
+ if params[:email].blank?
77
+ raise Exceptions::WrongParams, 'Email can not be blank!'
78
+ end
79
+ auth_user = AuthUser.find_by(email: params[:email])
80
+ if auth_user.blank?
81
+ raise Exceptions::WrongParams, 'Email is invalid!'
82
+ end
83
+ auth_user.send_reset_password
84
+ end
85
+
86
+ def self.create_by_provider(params)
87
+ if params[:provider].blank?
88
+ raise Exceptions::WrongParams, 'Provider can not be blank!'
89
+ end
90
+ if params[:token].blank?
91
+ raise Exceptions::WrongParams, 'Token can not be blank!'
92
+ end
93
+
94
+ provider_data = case params[:provider]
95
+ when /facebook/i
96
+ Providers::Facebook.get_user(params[:token])
97
+ when /google/i
98
+ Providers::Google.get_user(params[:token])
99
+ when /instagram/i
100
+ Providers::Instagram.get_user(params[:token])
101
+ else
102
+ raise ::ApiUserAuth::Exceptions::ProviderError,
103
+ 'Wrong provider!'
104
+ end
105
+
106
+ auth_user = AuthUser.find_or_initialize_by(email: provider_data[:email])
107
+ auth_user.encrypted_password = params[:token]
108
+ auth_user.generate_token
109
+ auth_user.is_new = auth_user.new_record?
110
+ auth_user.user_provider_data = provider_data
111
+ auth_user.provider = params[:provider]
112
+ auth_user.save
113
+ auth_user
114
+ end
115
+
116
+ def self.find_fy_token(token)
117
+ unless token =~ ApiUserAuth::UUID_REGEX
118
+ raise Exceptions::Unauthorized,
119
+ 'Header [Authorization] token is invalid!'
120
+ end
121
+ where(
122
+ '? = ANY("api_user_auth_auth_users"."auth_tokens")',
123
+ token
124
+ ).limit(1).first
125
+ end
126
+
127
+ def to_json
128
+ { id: id, email: email, auth_token: auth_tokens.last, is_new: is_new }
129
+ end
130
+
131
+ def generate_token
132
+ auth_tokens << SecureRandom.uuid
133
+ end
134
+
135
+ def password=(passwd)
136
+ self.encrypted_password = hexdigest(passwd) if passwd.present?
137
+ end
138
+
139
+ def valid_password?(passwd)
140
+ encrypted_password == hexdigest(passwd)
141
+ end
142
+
143
+ def send_reset_password
144
+ self.code = Random.new.rand((10**(6 - 1))..(10**6)).to_s
145
+ ForgotPasswordMailer.reset_code(self).deliver_now if save
146
+ end
147
+
148
+ def logout(token)
149
+ auth_tokens.delete(token)
150
+ save
151
+ end
152
+
153
+ private
154
+
155
+ def send_welcome
156
+ WelcomeMailer.welcome(self).deliver_now
157
+ end
158
+
159
+ def hexdigest(text)
160
+ Digest::SHA256.hexdigest(text + secure_salt)
161
+ end
162
+
163
+ def secure_salt
164
+ Digest::MD5.hexdigest('a18a9143-f193-4e76-a6de-f2912e96b71f')
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,3 @@
1
+ <%= @email %>
2
+ </br>
3
+ <strong><%= @code %></strong>
@@ -0,0 +1,2 @@
1
+ <h1>Welcome</h1>
2
+ <%= @email %>
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Api user auth</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "api_user_auth/application", media: "all" %>
9
+ <%= javascript_include_tag "api_user_auth/application" %>
10
+ </head>
11
+ <body>
12
+
13
+ <%= yield %>
14
+
15
+ </body>
16
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,12 @@
1
+ ApiUserAuth::Engine.routes.draw do
2
+ resources :auth, only: :create do
3
+ collection do
4
+ post 'login', action: :login
5
+ post 'provider', action: :provider
6
+ patch 'forgot_password', action: :forgot_password
7
+ patch 'password', action: :password
8
+ post 'login', action: :login
9
+ delete 'logout', action: :logout
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ class CreateApiUserAuthAuthUsers < ActiveRecord::Migration[5.2]
2
+ def change
3
+ enable_extension 'pgcrypto'
4
+
5
+ create_table :api_user_auth_auth_users do |t|
6
+ t.string :email, null: false, index: { unique: true }
7
+ t.string :encrypted_password, null: false
8
+ t.string :code, default: ''
9
+ t.uuid :auth_tokens, array: true, default: []
10
+ t.jsonb :user_provider_data, default: {}
11
+ t.string :provider, default: 'email'
12
+
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ module ApiUserAuth
2
+ # Base controller module
3
+ module Controller
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_action :authenticate
8
+ rescue_from Exceptions::Unauthorized, with: :auth_user_unauthorized
9
+ end
10
+
11
+ def auth_user_unauthorized(exception)
12
+ render json: { message: exception.message }, status: 401
13
+ end
14
+
15
+ private
16
+
17
+ def authenticate
18
+ if request.headers['Authorization'].blank?
19
+ raise Exceptions::Unauthorized,
20
+ 'Header [Authorization] can not be blank!'
21
+ end
22
+ http_authenticate
23
+ if @auth_user.blank?
24
+ raise Exceptions::Unauthorized,
25
+ 'Header [Authorization] token is invalid!'
26
+ end
27
+ end
28
+
29
+ def http_authenticate
30
+ authenticate_with_http_token do |token, _options|
31
+ unless token =~ ApiUserAuth::UUID_REGEX
32
+ raise Exceptions::Unauthorized,
33
+ 'Header [Authorization] token is invalid!'
34
+ end
35
+ @auth_user = AuthUser.find_fy_token(token)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ module ApiUserAuth
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ApiUserAuth
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ g.fixture_replacement :factory_bot, dir: 'spec/factories'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module ApiUserAuth
2
+ module Exceptions
3
+ class Unauthorized < ::StandardError; end
4
+ class WrongParams < ::StandardError; end
5
+
6
+ class ProviderError < ::StandardError; end
7
+ class InvalidToken < ProviderError; end
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiUserAuth
4
+ module Providers
5
+ # Get info from Facebook token
6
+ class Facebook
7
+ API_PATH = 'https://graph.facebook.com/v3.0/me'.freeze
8
+ FIELDS = %w[id name email birthday hometown gender picture].freeze
9
+
10
+ def initialize(token)
11
+ @token = token
12
+ @data = {}
13
+ end
14
+
15
+ def api_info_url
16
+ params = {
17
+ access_token: @token,
18
+ fields: FIELDS.join(','),
19
+ format: 'json'
20
+ }
21
+ URI("#{API_PATH}?#{params.to_query}")
22
+ end
23
+
24
+ def get_user_data
25
+ api_get_request
26
+ user_data
27
+ end
28
+
29
+ def user_data
30
+ {
31
+ id: @data[:id], name: @data[:name], email: @data[:email],
32
+ img_url: (@data[:picture] || {}).try(:[], :data).try(:[], :url),
33
+ info: {
34
+ birthday: @data[:birthday],
35
+ city: (@data[:hometown] || {}).try(:[], :name),
36
+ gender: @data[:gender]
37
+ }
38
+ }
39
+ end
40
+
41
+ def self.get_user(token)
42
+ fb = Facebook.new(token)
43
+ fb.get_user_data
44
+ end
45
+
46
+ private
47
+
48
+ def api_get_request
49
+ response = ::Net::HTTP.get_response(api_info_url)
50
+ case response.code.to_i
51
+ when 200
52
+ @data = JSON.parse(response.body, symbolize_names: true)
53
+ when 400
54
+ raise ApiUserAuth::Exceptions::InvalidToken, 'Invalid Token'
55
+ else
56
+ raise ApiUserAuth::Exceptions::ProviderError, 'Provider Error'
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end