groovestack-auth 0.1.5 → 0.1.6
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 +4 -4
- data/Gemfile +8 -1
- data/Gemfile.lock +120 -20
- data/{lib/groovestack/auth/action_cable.rb → app/channels/groovestack/auth/action_cable/connection.rb} +2 -2
- data/app/controllers/concerns/groovestack/auth/graphql/controllers/auth_helpers.rb +69 -0
- data/app/controllers/concerns/groovestack/auth/graphql/controllers/authed_execute.rb +16 -0
- data/app/controllers/groovestack/auth/authenticated_api_controller.rb +10 -0
- data/app/controllers/groovestack/auth/omniauth_callbacks_controller.rb +138 -0
- data/app/controllers/groovestack/auth/passwordless/magic_links_controller.rb +58 -0
- data/app/controllers/groovestack/auth/passwordless/sessions_controller.rb +75 -0
- data/app/graphql/graphql/identity_extensions.rb +11 -0
- data/app/graphql/graphql/user_extensions.rb +14 -0
- data/app/models/concerns/groovestack/auth/authorized_fields_for_serialization.rb +21 -0
- data/app/models/concerns/groovestack/auth/identity.rb +39 -0
- data/app/models/concerns/groovestack/auth/user.rb +14 -0
- data/app/views/devise/mailer/magic_link.html.erb +9 -0
- data/config/initializers/core_config.rb +0 -6
- data/config/initializers/devise.rb +387 -302
- data/config/initializers/omniauth.rb +0 -19
- data/config/locales/devise.en.yml +71 -0
- data/db/migrate/20231103174050_add_devise_to_users_and_identities.rb +59 -0
- data/groovestack-auth.gemspec +7 -7
- data/lib/groovestack/auth/{railtie.rb → engine.rb} +13 -2
- data/lib/groovestack/auth/graphql/authorized_field.rb +19 -0
- data/lib/groovestack/auth/graphql/authorized_object.rb +11 -0
- data/lib/groovestack/auth/graphql/schema_visibility.rb +40 -0
- data/lib/groovestack/auth/graphql/visible_field.rb +21 -0
- data/lib/groovestack/auth/graphql/visible_object.rb +17 -0
- data/lib/groovestack/auth/passwordless/t_otp_tokenizer.rb +89 -0
- data/lib/groovestack/auth/provider.rb +7 -0
- data/lib/groovestack/auth/providers/apple.rb +5 -5
- data/lib/groovestack/auth/providers/facebook.rb +17 -0
- data/lib/groovestack/auth/providers/google.rb +1 -1
- data/lib/groovestack/auth/providers/omni_auth.rb +2 -2
- data/lib/groovestack/auth/routes.rb +26 -0
- data/lib/groovestack/auth/settings.rb +43 -0
- data/lib/groovestack/auth/version.rb +1 -1
- data/lib/groovestack/auth.rb +33 -83
- metadata +55 -50
- data/config/initializers/devise_token_auth.rb +0 -72
- data/config/initializers/graphql_devise.rb +0 -58
- data/config/routes.rb +0 -11
- data/db/migrate/20231103172517_create_users.rb +0 -54
- data/db/migrate/20231103174037_create_identities.rb +0 -19
- data/lib/fabricators/user_fabricator.rb +0 -17
- data/lib/graphql/identity/filter.rb +0 -13
- data/lib/graphql/identity/mutations.rb +0 -27
- data/lib/graphql/identity/queries.rb +0 -25
- data/lib/graphql/identity/type.rb +0 -22
- data/lib/graphql/user/filter.rb +0 -15
- data/lib/graphql/user/mutations.rb +0 -63
- data/lib/graphql/user/queries.rb +0 -40
- data/lib/graphql/user/type.rb +0 -30
- data/lib/groovestack/auth/authenticated_api_controller.rb +0 -13
- data/lib/groovestack/auth/omniauth_callbacks_controller.rb +0 -111
- data/lib/groovestack/auth/schema_plugin.rb +0 -19
- data/lib/identity.rb +0 -31
- data/lib/user.rb +0 -53
- data/lib/users/roles.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8adb7a1b84a4b5aded491344181c888900557ce327e15b5d441581c6e3c88146
|
4
|
+
data.tar.gz: 9fef159454b305cc1b4a4a587bfb84a249cf9357e7bf28712b795492d1568dac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54f7df9d82696102e44a34d251322042e030680c5fad56dca3b3c1ee92c6d6bd60e05629bf3cdd5bebe4baab2ddde555cc6e50ef47837b3b0e5d114fadcc3635
|
7
|
+
data.tar.gz: 8215562c42ae838366b9b79048f24d4c263059c017dcf1a0aaae616107d123ad88fb192f88e5a3d2a8f748af7441225ec1d32ed1e87bccb9a1d5fe89306c50a4
|
data/Gemfile
CHANGED
@@ -5,8 +5,15 @@ source 'https://rubygems.org'
|
|
5
5
|
# Specify your gem's dependencies in groovestack-auth.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
gem 'devise-passwordless'
|
9
|
+
gem 'omniauth-apple'
|
10
|
+
gem 'omniauth-facebook'
|
11
|
+
gem 'omniauth-google-oauth2', '1.1.2'
|
12
|
+
|
8
13
|
gem 'fabrication'
|
9
14
|
gem 'faker'
|
10
15
|
gem 'racksh' # get a console without a full Rails application
|
11
16
|
gem 'rspec'
|
12
|
-
gem 'rubocop'
|
17
|
+
gem 'rubocop'
|
18
|
+
gem 'rubocop-graphql', '~> 1.0'
|
19
|
+
gem 'rubocop-rails', '~> 2.18'
|
data/Gemfile.lock
CHANGED
@@ -2,15 +2,32 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
groovestack-auth (0.1.5)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
devise
|
6
|
+
devise-passwordless
|
7
|
+
groovestack-config (~> 0.1, >= 0.1.4)
|
8
|
+
groovestack-identities (~> 0.1, >= 0.1.3)
|
9
|
+
rotp
|
10
10
|
|
11
11
|
GEM
|
12
12
|
remote: https://rubygems.org/
|
13
13
|
specs:
|
14
|
+
actionpack (7.2.2.1)
|
15
|
+
actionview (= 7.2.2.1)
|
16
|
+
activesupport (= 7.2.2.1)
|
17
|
+
nokogiri (>= 1.8.5)
|
18
|
+
racc
|
19
|
+
rack (>= 2.2.4, < 3.2)
|
20
|
+
rack-session (>= 1.0.1)
|
21
|
+
rack-test (>= 0.6.3)
|
22
|
+
rails-dom-testing (~> 2.2)
|
23
|
+
rails-html-sanitizer (~> 1.6)
|
24
|
+
useragent (~> 0.16)
|
25
|
+
actionview (7.2.2.1)
|
26
|
+
activesupport (= 7.2.2.1)
|
27
|
+
builder (~> 3.1)
|
28
|
+
erubi (~> 1.11)
|
29
|
+
rails-dom-testing (~> 2.2)
|
30
|
+
rails-html-sanitizer (~> 1.6)
|
14
31
|
activemodel (7.2.2.1)
|
15
32
|
activesupport (= 7.2.2.1)
|
16
33
|
activerecord (7.2.2.1)
|
@@ -32,12 +49,25 @@ GEM
|
|
32
49
|
aes_key_wrap (1.1.0)
|
33
50
|
ast (2.4.3)
|
34
51
|
base64 (0.2.0)
|
52
|
+
bcrypt (3.1.20)
|
35
53
|
benchmark (0.4.0)
|
36
54
|
bigdecimal (3.1.9)
|
37
55
|
bindata (2.5.0)
|
56
|
+
builder (3.3.0)
|
38
57
|
concurrent-ruby (1.3.5)
|
39
58
|
connection_pool (2.5.0)
|
40
|
-
|
59
|
+
crass (1.0.6)
|
60
|
+
date (3.4.1)
|
61
|
+
devise (4.9.4)
|
62
|
+
bcrypt (~> 3.0)
|
63
|
+
orm_adapter (~> 0.1)
|
64
|
+
railties (>= 4.1.0)
|
65
|
+
responders
|
66
|
+
warden (~> 1.2.3)
|
67
|
+
devise-passwordless (1.1.0)
|
68
|
+
devise
|
69
|
+
globalid
|
70
|
+
diff-lcs (1.6.2)
|
41
71
|
drb (2.2.1)
|
42
72
|
dry-configurable (1.3.0)
|
43
73
|
dry-core (~> 1.1)
|
@@ -46,6 +76,7 @@ GEM
|
|
46
76
|
concurrent-ruby (~> 1.0)
|
47
77
|
logger
|
48
78
|
zeitwerk (~> 2.6)
|
79
|
+
erubi (1.13.1)
|
49
80
|
fabrication (2.31.0)
|
50
81
|
faker (3.5.1)
|
51
82
|
i18n (>= 1.8.11, < 2)
|
@@ -58,21 +89,30 @@ GEM
|
|
58
89
|
faraday-net_http (3.4.0)
|
59
90
|
net-http (>= 0.5.0)
|
60
91
|
fiber-storage (1.0.0)
|
92
|
+
globalid (1.2.1)
|
93
|
+
activesupport (>= 6.1)
|
61
94
|
graphql (2.3.22)
|
62
95
|
base64
|
63
96
|
fiber-storage
|
64
|
-
groovestack-base (0.1.
|
97
|
+
groovestack-base (0.1.12)
|
65
98
|
activerecord (~> 7.0)
|
66
99
|
dry-configurable (~> 1.0)
|
67
100
|
graphql (>= 1.8, < 2.4)
|
68
101
|
pg (~> 1.0)
|
69
102
|
pg_lock (~> 1.0)
|
70
|
-
puma (
|
71
|
-
groovestack-config (0.1.
|
72
|
-
groovestack-base (~> 0.1.
|
103
|
+
puma (>= 5.0, < 7)
|
104
|
+
groovestack-config (0.1.4)
|
105
|
+
groovestack-base (~> 0.1, >= 0.1.12)
|
106
|
+
groovestack-identities (0.1.3)
|
107
|
+
groovestack-base (~> 0.1, >= 0.1.12)
|
73
108
|
hashie (5.0.0)
|
74
109
|
i18n (1.14.7)
|
75
110
|
concurrent-ruby (~> 1.0)
|
111
|
+
io-console (0.8.0)
|
112
|
+
irb (1.15.2)
|
113
|
+
pp (>= 0.6.0)
|
114
|
+
rdoc (>= 4.0.0)
|
115
|
+
reline (>= 0.4.2)
|
76
116
|
json (2.10.2)
|
77
117
|
json-jwt (1.16.7)
|
78
118
|
activesupport (>= 4.2)
|
@@ -81,21 +121,22 @@ GEM
|
|
81
121
|
bindata
|
82
122
|
faraday (~> 2.0)
|
83
123
|
faraday-follow_redirects
|
84
|
-
jsonb_accessor (1.4)
|
85
|
-
activerecord (>= 6.1)
|
86
|
-
activesupport (>= 6.1)
|
87
|
-
pg (>= 0.18.1)
|
88
124
|
jwt (2.10.1)
|
89
125
|
base64
|
90
126
|
language_server-protocol (3.17.0.4)
|
91
127
|
lint_roller (1.1.0)
|
92
128
|
logger (1.7.0)
|
129
|
+
loofah (2.24.0)
|
130
|
+
crass (~> 1.0.2)
|
131
|
+
nokogiri (>= 1.12.0)
|
93
132
|
minitest (5.25.5)
|
94
133
|
multi_xml (0.7.1)
|
95
134
|
bigdecimal (~> 3.1)
|
96
135
|
net-http (0.6.0)
|
97
136
|
uri
|
98
137
|
nio4r (2.7.4)
|
138
|
+
nokogiri (1.18.8-arm64-darwin)
|
139
|
+
racc (~> 1.4)
|
99
140
|
oauth2 (2.0.9)
|
100
141
|
faraday (>= 0.17.3, < 3.0)
|
101
142
|
jwt (>= 1.0, < 3.0)
|
@@ -110,21 +151,31 @@ GEM
|
|
110
151
|
omniauth-apple (1.3.0)
|
111
152
|
json-jwt
|
112
153
|
omniauth-oauth2
|
113
|
-
omniauth-
|
114
|
-
|
154
|
+
omniauth-facebook (10.0.0)
|
155
|
+
bigdecimal
|
156
|
+
omniauth-oauth2 (>= 1.2, < 3)
|
157
|
+
omniauth-google-oauth2 (1.1.2)
|
158
|
+
jwt (>= 2.0)
|
115
159
|
oauth2 (~> 2.0)
|
116
160
|
omniauth (~> 2.0)
|
117
161
|
omniauth-oauth2 (~> 1.8)
|
118
162
|
omniauth-oauth2 (1.8.0)
|
119
163
|
oauth2 (>= 1.4, < 3)
|
120
164
|
omniauth (~> 2.0)
|
165
|
+
orm_adapter (0.5.0)
|
121
166
|
parallel (1.26.3)
|
122
167
|
parser (3.3.7.4)
|
123
168
|
ast (~> 2.4.1)
|
124
169
|
racc
|
125
170
|
pg (1.5.9)
|
126
171
|
pg_lock (1.0.0)
|
172
|
+
pp (0.6.2)
|
173
|
+
prettyprint
|
174
|
+
prettyprint (0.2.0)
|
127
175
|
prism (1.4.0)
|
176
|
+
psych (5.2.3)
|
177
|
+
date
|
178
|
+
stringio
|
128
179
|
puma (5.6.9)
|
129
180
|
nio4r (~> 2.0)
|
130
181
|
racc (1.8.1)
|
@@ -133,26 +184,55 @@ GEM
|
|
133
184
|
base64 (>= 0.1.0)
|
134
185
|
logger (>= 1.6.0)
|
135
186
|
rack (>= 3.0.0, < 4)
|
187
|
+
rack-session (2.1.0)
|
188
|
+
base64 (>= 0.1.0)
|
189
|
+
rack (>= 3.0.0)
|
136
190
|
rack-test (2.2.0)
|
137
191
|
rack (>= 1.3)
|
138
192
|
racksh (1.0.1)
|
139
193
|
rack (>= 1.0)
|
140
194
|
rack-test (>= 0.5)
|
195
|
+
rackup (2.2.1)
|
196
|
+
rack (>= 3)
|
197
|
+
rails-dom-testing (2.2.0)
|
198
|
+
activesupport (>= 5.0.0)
|
199
|
+
minitest
|
200
|
+
nokogiri (>= 1.6)
|
201
|
+
rails-html-sanitizer (1.6.2)
|
202
|
+
loofah (~> 2.21)
|
203
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
204
|
+
railties (7.2.2.1)
|
205
|
+
actionpack (= 7.2.2.1)
|
206
|
+
activesupport (= 7.2.2.1)
|
207
|
+
irb (~> 1.13)
|
208
|
+
rackup (>= 1.0.0)
|
209
|
+
rake (>= 12.2)
|
210
|
+
thor (~> 1.0, >= 1.2.2)
|
211
|
+
zeitwerk (~> 2.6)
|
141
212
|
rainbow (3.1.1)
|
213
|
+
rake (13.2.1)
|
214
|
+
rdoc (6.13.1)
|
215
|
+
psych (>= 4.0.0)
|
142
216
|
regexp_parser (2.10.0)
|
217
|
+
reline (0.6.1)
|
218
|
+
io-console (~> 0.5)
|
219
|
+
responders (3.1.1)
|
220
|
+
actionpack (>= 5.2)
|
221
|
+
railties (>= 5.2)
|
222
|
+
rotp (6.3.0)
|
143
223
|
rspec (3.13.0)
|
144
224
|
rspec-core (~> 3.13.0)
|
145
225
|
rspec-expectations (~> 3.13.0)
|
146
226
|
rspec-mocks (~> 3.13.0)
|
147
227
|
rspec-core (3.13.3)
|
148
228
|
rspec-support (~> 3.13.0)
|
149
|
-
rspec-expectations (3.13.
|
229
|
+
rspec-expectations (3.13.4)
|
150
230
|
diff-lcs (>= 1.2.0, < 2.0)
|
151
231
|
rspec-support (~> 3.13.0)
|
152
|
-
rspec-mocks (3.13.
|
232
|
+
rspec-mocks (3.13.4)
|
153
233
|
diff-lcs (>= 1.2.0, < 2.0)
|
154
234
|
rspec-support (~> 3.13.0)
|
155
|
-
rspec-support (3.13.
|
235
|
+
rspec-support (3.13.3)
|
156
236
|
rubocop (1.75.2)
|
157
237
|
json (~> 2.3)
|
158
238
|
language_server-protocol (~> 3.17.0.2)
|
@@ -167,11 +247,22 @@ GEM
|
|
167
247
|
rubocop-ast (1.44.0)
|
168
248
|
parser (>= 3.3.7.2)
|
169
249
|
prism (~> 1.4)
|
250
|
+
rubocop-graphql (1.5.5)
|
251
|
+
lint_roller (~> 1.1)
|
252
|
+
rubocop (>= 1.72.1, < 2)
|
253
|
+
rubocop-rails (2.31.0)
|
254
|
+
activesupport (>= 4.2.0)
|
255
|
+
lint_roller (~> 1.1)
|
256
|
+
rack (>= 1.1)
|
257
|
+
rubocop (>= 1.75.0, < 2.0)
|
258
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
170
259
|
ruby-progressbar (1.13.0)
|
171
260
|
securerandom (0.4.1)
|
172
261
|
snaky_hash (2.0.1)
|
173
262
|
hashie
|
174
263
|
version_gem (~> 1.1, >= 1.1.1)
|
264
|
+
stringio (3.1.7)
|
265
|
+
thor (1.3.2)
|
175
266
|
timeout (0.4.3)
|
176
267
|
tzinfo (2.0.6)
|
177
268
|
concurrent-ruby (~> 1.0)
|
@@ -179,19 +270,28 @@ GEM
|
|
179
270
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
180
271
|
unicode-emoji (4.0.4)
|
181
272
|
uri (1.0.3)
|
273
|
+
useragent (0.16.11)
|
182
274
|
version_gem (1.1.6)
|
275
|
+
warden (1.2.9)
|
276
|
+
rack (>= 2.0.9)
|
183
277
|
zeitwerk (2.6.18)
|
184
278
|
|
185
279
|
PLATFORMS
|
186
280
|
arm64-darwin-23
|
187
281
|
|
188
282
|
DEPENDENCIES
|
283
|
+
devise-passwordless
|
189
284
|
fabrication
|
190
285
|
faker
|
191
286
|
groovestack-auth!
|
287
|
+
omniauth-apple
|
288
|
+
omniauth-facebook
|
289
|
+
omniauth-google-oauth2 (= 1.1.2)
|
192
290
|
racksh
|
193
291
|
rspec
|
194
292
|
rubocop
|
293
|
+
rubocop-graphql (~> 1.0)
|
294
|
+
rubocop-rails (~> 2.18)
|
195
295
|
|
196
296
|
BUNDLED WITH
|
197
|
-
2.6.
|
297
|
+
2.6.2
|
@@ -7,11 +7,11 @@ module Groovestack
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
10
|
-
identified_by :
|
10
|
+
identified_by :current_user
|
11
11
|
end
|
12
12
|
|
13
13
|
def connect
|
14
|
-
self.
|
14
|
+
self.current_user = find_verified_resource
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Groovestack
|
4
|
+
module Auth
|
5
|
+
module GraphQL
|
6
|
+
module Controllers
|
7
|
+
module AuthHelpers
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
include Pundit::Authorization if defined?(::Pundit)
|
12
|
+
|
13
|
+
before_action :authenticate_request!, unless: :graphiql?
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def context
|
18
|
+
{
|
19
|
+
visibility_profile: visibility_profile,
|
20
|
+
current_user: current_user
|
21
|
+
}.tap do |ctx|
|
22
|
+
ctx[:authorize] = method(:authorize) if defined?(::Pundit)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def graphiql?
|
27
|
+
return false unless defined?(::GraphiQL)
|
28
|
+
|
29
|
+
Rails.env.development? && request.referer == ::Rails.application.routes.url_helpers.graphiql_rails_url
|
30
|
+
end
|
31
|
+
|
32
|
+
def graphiql_visibility_profile
|
33
|
+
@graphiql_visibility_profile ||= request.headers['HTTP_GRAPHIQL_VISIBILITY_PROFILE']
|
34
|
+
end
|
35
|
+
|
36
|
+
def visibility_profile
|
37
|
+
@visibility_profile ||= if graphiql?
|
38
|
+
if graphiql_visibility_profile.blank?
|
39
|
+
raise 'must set GRAPHIQL_VISIBILITY_PROFILE header in graphiql initializer '
|
40
|
+
end
|
41
|
+
|
42
|
+
graphiql_visibility_profile.to_sym
|
43
|
+
elsif current_user.nil?
|
44
|
+
:anon
|
45
|
+
elsif current_user.admin?
|
46
|
+
::User::Role::ADMIN.to_sym
|
47
|
+
elsif current_user.exec?
|
48
|
+
::User::Role::EXEC.to_sym
|
49
|
+
elsif current_user.staff?
|
50
|
+
::User::Role::STAFF.to_sym
|
51
|
+
else
|
52
|
+
:user
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def authenticate_request!
|
57
|
+
# skip authentication for AppConfig queries
|
58
|
+
# introspection query is public. fields will be visible/not based on visibility_profile
|
59
|
+
|
60
|
+
return if %w[AppConfig IntrospectionQuery].include?(operation_name)
|
61
|
+
|
62
|
+
authenticate_user!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Groovestack
|
4
|
+
module Auth
|
5
|
+
module GraphQL
|
6
|
+
module Controllers
|
7
|
+
module AuthedExecute
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
include ::Groovestack::Base::GraphQL::Controllers::Execute
|
11
|
+
include ::Groovestack::Auth::GraphQL::Controllers::AuthHelpers
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Groovestack
|
4
|
+
module Auth
|
5
|
+
class AuthenticatedApiController < ApplicationController
|
6
|
+
prepend_before_action :authenticate_user!
|
7
|
+
protect_from_forgery prepend: true, with: :reset_session unless -> { Rails.env.test? }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Groovestack
|
4
|
+
module Auth
|
5
|
+
class OmniauthCallbacksController < ::Devise::OmniauthCallbacksController
|
6
|
+
include ::Devise::Controllers::Rememberable
|
7
|
+
|
8
|
+
skip_before_action :verify_authenticity_token
|
9
|
+
before_action :set_auth_hash
|
10
|
+
|
11
|
+
Groovestack::Auth.configured_providers(ancestor: Groovestack::Auth::Providers::OmniAuth).each do |p|
|
12
|
+
define_method(p.k) do
|
13
|
+
handle_oauth(provider: p.k.to_s.capitalize)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure
|
18
|
+
# override for error notifications & to support custom origin redirects
|
19
|
+
|
20
|
+
known_reasons = %w[user_cancelled_authorize user_denied]
|
21
|
+
|
22
|
+
handled = known_reasons.any? do |reason|
|
23
|
+
request.params&.dig('error_reason') == reason || request.params&.dig('error') == reason
|
24
|
+
end
|
25
|
+
|
26
|
+
if !handled && Rails.env.production?
|
27
|
+
failure_kind = OmniAuth::Utils.camelize(failed_strategy.name)
|
28
|
+
blob = {
|
29
|
+
kind: failure_kind,
|
30
|
+
reason: failure_message,
|
31
|
+
params: request.params,
|
32
|
+
omniauth: request.env['omniauth.auth']
|
33
|
+
}
|
34
|
+
|
35
|
+
exception = OmniauthFailureError.new(blob.to_s)
|
36
|
+
|
37
|
+
::Groovestack::Base.notify_error('Groovestack::Auth::OmniauthCallbacksController.omniauth_failure', exception)
|
38
|
+
end
|
39
|
+
|
40
|
+
set_flash_message! :alert, :failure, kind: failure_kind, reason: failure_message
|
41
|
+
redirect_to after_omniauth_failure_path_for(resource_name),
|
42
|
+
allow_other_host: ::Groovestack::Auth.allow_other_host_redirects
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def set_auth_hash
|
48
|
+
# set omniauth.auth for /callback requests (that skip the request phase)
|
49
|
+
return if params['omniauth'].blank?
|
50
|
+
|
51
|
+
# only set if not already set (i.e. no override)
|
52
|
+
request.env['omniauth.auth'] ||= JSON.parse(params['omniauth'].to_json, object_class: OmniAuth::AuthHash)
|
53
|
+
end
|
54
|
+
|
55
|
+
def after_omniauth_failure_path_for(scope)
|
56
|
+
if (origin_url = omniauth_request_origin)
|
57
|
+
return origin_url
|
58
|
+
end
|
59
|
+
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
def omniauth_request_origin
|
64
|
+
return ::Groovestack::Auth.omniauth_origin_url if ::Groovestack::Auth.omniauth_origin_url.present?
|
65
|
+
return ::Rails.application.routes.url_helpers.root_url if same_origin_request?
|
66
|
+
|
67
|
+
request.env['omniauth.origin']
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def json_format?
|
73
|
+
# WHY necessary?
|
74
|
+
# request.format.json? may not be set correctly for FE auth that handles the
|
75
|
+
# request phase and only hits /callback
|
76
|
+
# request.path_parameters[:format] is set to 'json' in the same case
|
77
|
+
request.format.json? || request.path_parameters[:format] == 'json'
|
78
|
+
end
|
79
|
+
|
80
|
+
def same_origin_request?
|
81
|
+
return true if request.env['omniauth.origin'].blank?
|
82
|
+
|
83
|
+
request.env['omniauth.origin'].starts_with?(request.base_url)
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_search_params(url, params)
|
87
|
+
uri = URI.parse(url)
|
88
|
+
existing_params = Rack::Utils.parse_nested_query(uri.query)
|
89
|
+
merged_params = existing_params.merge(params.deep_stringify_keys)
|
90
|
+
uri.query = Rack::Utils.build_nested_query(merged_params)
|
91
|
+
uri.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
def handle_oauth(provider:)
|
95
|
+
auth = request.env['omniauth.auth']
|
96
|
+
|
97
|
+
identity_params = {
|
98
|
+
auth: auth,
|
99
|
+
user_attrs: {
|
100
|
+
defaults: {
|
101
|
+
password: ::Devise.friendly_token[0, 20]
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
@user = ::Identity.find_or_create_from_omniauth!(**identity_params).user
|
107
|
+
|
108
|
+
begin
|
109
|
+
unless @user.confirmed?
|
110
|
+
@user.skip_confirmation_notification! # skip sending confirmation email
|
111
|
+
@user.confirm
|
112
|
+
end
|
113
|
+
rescue StandardError => e
|
114
|
+
msg = "OmniAuth Callback Confirmation Failure: #{e}"
|
115
|
+
::Groovestack::Base.notify_error(e.class, msg)
|
116
|
+
end
|
117
|
+
|
118
|
+
remember_me @user
|
119
|
+
sign_in(:user, @user)
|
120
|
+
|
121
|
+
if json_format?
|
122
|
+
# return connected identity info for FE user hydration
|
123
|
+
render json: @user.slice(:id, :email, :name), status: :ok
|
124
|
+
elsif same_origin_request?
|
125
|
+
redirect_to after_sign_in_path_for(@user)
|
126
|
+
else
|
127
|
+
redirect_to omniauth_request_origin, allow_other_host: ::Groovestack::Auth.allow_other_host_redirects
|
128
|
+
end
|
129
|
+
rescue ActiveRecord::RecordInvalid => e
|
130
|
+
# let FE handle missing email gracefully
|
131
|
+
raise e unless e.record.errors[:email].present? && e.record.errors[:email].include?("can't be blank")
|
132
|
+
|
133
|
+
redirect_to add_search_params(after_omniauth_failure_path_for(resource_name), { errors: { email_missing: true } }),
|
134
|
+
allow_other_host: ::Groovestack::Auth.allow_other_host_redirects
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Groovestack
|
4
|
+
module Auth
|
5
|
+
module Passwordless
|
6
|
+
class MagicLinksController < ::DeviseController
|
7
|
+
skip_before_action :verify_authenticity_token
|
8
|
+
prepend_before_action :require_no_authentication, only: :show
|
9
|
+
prepend_before_action :allow_params_authentication!, only: :show
|
10
|
+
prepend_before_action(only: [:show]) { request.env['devise.skip_timeout'] = true }
|
11
|
+
|
12
|
+
def show
|
13
|
+
self.resource = warden.authenticate!(auth_options)
|
14
|
+
|
15
|
+
begin
|
16
|
+
unless resource.confirmed?
|
17
|
+
resource.skip_confirmation_notification! # skip sending confirmation email
|
18
|
+
resource.confirm
|
19
|
+
end
|
20
|
+
rescue StandardError => e
|
21
|
+
msg = "Passwordless Magic Link Confirmation Failure: #{e}"
|
22
|
+
::Groovestack::Base.notify_error(e.class, msg)
|
23
|
+
end
|
24
|
+
|
25
|
+
set_flash_message!(:notice, :signed_in)
|
26
|
+
sign_in(resource_name, resource, event: :authentication)
|
27
|
+
yield resource if block_given?
|
28
|
+
|
29
|
+
respond_to do |format|
|
30
|
+
format.html { redirect_to after_sign_in_path_for(resource) }
|
31
|
+
format.json { render json: { success: true, resource: resource.as_json, status: :ok } }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def auth_options
|
38
|
+
mapping = ::Devise.mappings[resource_name]
|
39
|
+
{ scope: resource_name, recall: "#{mapping.controllers[:sessions]}#new" }
|
40
|
+
end
|
41
|
+
|
42
|
+
def translation_scope
|
43
|
+
'devise.sessions'
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def resource_params
|
49
|
+
params.permit({ user: %i[email remember_me token] })
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_params
|
53
|
+
resource_params.permit({ user: %i[email remember_me] })
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Groovestack
|
4
|
+
module Auth
|
5
|
+
module Passwordless
|
6
|
+
class SessionsController < ::Devise::SessionsController
|
7
|
+
skip_before_action :verify_authenticity_token # , only: :destroy
|
8
|
+
|
9
|
+
def create
|
10
|
+
self.resource = resource_class.find_for_authentication(email: create_params[:email])
|
11
|
+
|
12
|
+
if resource.blank?
|
13
|
+
# register a new account
|
14
|
+
|
15
|
+
u = ::User.new(email: create_params[:email], password: ::Devise.friendly_token[0, 20])
|
16
|
+
# u.skip_confirmation_notification!
|
17
|
+
u.save!
|
18
|
+
|
19
|
+
self.resource = u
|
20
|
+
end
|
21
|
+
|
22
|
+
send_magic_link(resource)
|
23
|
+
|
24
|
+
respond_to do |format|
|
25
|
+
format.html { redirect_to(after_magic_link_sent_path_for(resource), status: devise_redirect_status) }
|
26
|
+
format.json do
|
27
|
+
render json: { success: true, resource: resource.as_json, message: 'Magic link sent.' }, status: :ok
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue StandardError => e
|
31
|
+
msg = "Passwordless Magic Link Send Failure for #{create_params[:email]}: #{e}"
|
32
|
+
::Groovestack::Base.notify_error('Groovestack::Auth::Passwordless::SessionsController', msg)
|
33
|
+
|
34
|
+
respond_to do |format|
|
35
|
+
format.html { render :new, status: devise_error_status }
|
36
|
+
format.json do
|
37
|
+
render json: { error: 'User not found or unable to register account. Our team has been notified.' },
|
38
|
+
status: :not_found
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def send_magic_link(resource)
|
46
|
+
resource.send_magic_link(remember_me: create_params[:remember_me])
|
47
|
+
end
|
48
|
+
|
49
|
+
def translation_scope
|
50
|
+
if action_name == 'create'
|
51
|
+
'devise.passwordless'
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def create_params
|
60
|
+
resource_params.permit(:email, :remember_me)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Devise < 4.9 fallback support
|
64
|
+
# See: https://github.com/heartcombo/devise/wiki/How-To:-Upgrade-to-Devise-4.9.0-%5BHotwire-Turbo-integration%5D
|
65
|
+
def devise_redirect_status
|
66
|
+
::Devise.try(:responder).try(:redirect_status) || :found
|
67
|
+
end
|
68
|
+
|
69
|
+
def devise_error_status
|
70
|
+
::Devise.try(:responder).try(:error_status) || :ok
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|