rodauth-omniauth 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99f2cd5c47082f18ff2b0feeea0106c104edf6454e354fb04f8c74044b7796b2
4
- data.tar.gz: 58c096a1176cce8c2aa1ef0741c35e0ec81a36fb1d3d7bf9350b01c143daffdb
3
+ metadata.gz: b983cad17d20d291428c75e8efd5d59b313e83ac68f7998d7d1ec6c508db0ac4
4
+ data.tar.gz: e5db5f29f5d2897312d9dc4eca1cc34718ff56bd024bf2f79473a230a9e3cfbc
5
5
  SHA512:
6
- metadata.gz: 3ee9f500a18535a215d74d6cbc250561a7d0b61cc5818166e7bf481b74bf67dedf2547228ffbec177273715d1001cce2178e7590cadc8c21b102ef1b9585f9c5
7
- data.tar.gz: fe0337fdc2ef82ea53c5b8514383df10d60433900deaa35fa519ffe0e846c3a5e79deead46b1e790e29a35ffac0a00a2301c6437376d8fd91832d8ee1cb49a8e
6
+ metadata.gz: d371d95667924f9e6ea1da68ab3e59471d794230de35eefa7d816edba211c734c981e1ef8ce724987cf58e0e10bf52773bad671dbb352dbbcd6b2762725bba1d
7
+ data.tar.gz: 78feadd9466089523ec85e021c980fc90af051c92d6c981e04476888546524f4bcd010321bd89d0f5a4f788c68221ac87dfa6112ea97e67074c9c3ca532daadd
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # rodauth-omniauth
2
2
 
3
- [Rodauth] feature that offers login and registration via multiple external providers using [OmniAuth]. The external identities are automatically stored in the database, and associated to the main account record.
3
+ [Rodauth] feature that offers login and registration via multiple external providers using [OmniAuth], together with the persistence of external identities.
4
4
 
5
5
  ## Installation
6
6
 
@@ -15,16 +15,28 @@ $ bundle add rodauth-omniauth
15
15
  You'll first need to create the table for storing external identities:
16
16
 
17
17
  ```rb
18
- Sequel.migration do # class CreateAccountIdentities < ActiveRecord::Migration
19
- change do # def change
20
- create_table :account_identities do # create_table :account_identities do |t|
21
- primary_key :id # t.references :account, null: false, foreign_key: { on_delete: :cascade }
22
- foreign_key :account_id, :accounts # t.string :provider, null: false
23
- String :provider, null: false # t.string :uid, null: false
24
- String :uid, null: false # t.index [:provider, :uid], unique: true
25
- unique [:provider, :uid] # end
26
- end # end
27
- end # end
18
+ Sequel.migration do
19
+ change do
20
+ create_table :account_identities do
21
+ primary_key :id
22
+ foreign_key :account_id, :accounts
23
+ String :provider, null: false
24
+ String :uid, null: false
25
+ unique [:provider, :uid]
26
+ end
27
+ end
28
+ end
29
+ ```
30
+ ```rb
31
+ class CreateAccountIdentities < ActiveRecord::Migration
32
+ def change
33
+ create_table :account_identities do |t|
34
+ t.references :account, null: false, foreign_key: { on_delete: :cascade }
35
+ t.string :provider, null: false
36
+ t.string :uid, null: false
37
+ t.index [:provider, :uid], unique: true
38
+ end
39
+ end
28
40
  end
29
41
  ```
30
42
 
@@ -47,16 +59,11 @@ You can now add authentication links to your login form:
47
59
 
48
60
  ```erb
49
61
  <!-- app/views/rodauth/_login_form_footer.html.erb -->
50
- <%== rodauth.login_form_footer_links_heading %>
51
-
52
- <ul>
62
+ <!-- ... -->
53
63
  <li><%= button_to "Login via Facebook", rodauth.omniauth_request_path(:facebook), method: :post, data: { turbo: false }, class: "btn btn-link p-0" %></li>
54
64
  <li><%= button_to "Login via Twitter", rodauth.omniauth_request_path(:twitter), method: :post, data: { turbo: false }, class: "btn btn-link p-0" %></li>
55
65
  <li><%= button_to "Login via Google", rodauth.omniauth_request_path(:google), method: :post, data: { turbo: false }, class: "btn btn-link p-0" %></li>
56
- <% rodauth.login_form_footer_links.each do |_, link, text| %>
57
- <li><%= link_to text, link %></li>
58
- <% end %>
59
- </ul>
66
+ <!-- ... -->
60
67
  ```
61
68
 
62
69
  Assuming you configured the providers correctly, you should now be able to authenticate via an external provider. The `omniauth` feature handles the callback request, automatically creating new identities and verified accounts from those identities as needed.
@@ -73,7 +80,25 @@ Currently, provider login is required to return the user's email address, and ac
73
80
 
74
81
  ### Login
75
82
 
76
- If the local account associated to the external identity exists and is unverified (e.g. it was created through normal registration), the external login will abort during the callback phase. You can change the default error flash and redirect location in this case:
83
+ After provider login, you can perform custom logic at the start of the callback request:
84
+
85
+ ```rb
86
+ before_omniauth_callback_route do
87
+ omniauth_provider #=> :google
88
+ end
89
+ ```
90
+
91
+ If the external identity doesn't already exist, and there is an account with email matching the identity's, the new identity will be assigned to that account. You can change how existing accounts are searched after provider login:
92
+
93
+ ```rb
94
+ account_from_omniauth do
95
+ account_table_ds.first(email: omniauth_email) # roughly the default implementation
96
+ end
97
+ # or
98
+ account_from_omniauth {} # disable finding existing accounts for new identities
99
+ ```
100
+
101
+ If the account associated to the external identity exists and is unverified (e.g. it was created through normal registration), the callback phase will return an error response, as only verified accounts can be logged into. You can change the default error flash and redirect location in this case:
77
102
 
78
103
  ```rb
79
104
  omniauth_login_unverified_account_error_flash "The account matching the external identity is currently awaiting verification"
@@ -82,9 +107,7 @@ omniauth_login_failure_redirect { require_login_redirect }
82
107
 
83
108
  ### Account creation
84
109
 
85
- Since provider accounts have verified the email address, local accounts created via external logins are automatically considered verified.
86
-
87
- If you want to use extra user information for account creation, you can do so via hooks:
110
+ Accounts created via external login are automatically verified, because it's assumed your email address was verified by the external provider. If you want to use extra user information for account creation, you can do so via hooks:
88
111
 
89
112
  ```rb
90
113
  before_omniauth_create_account { account[:name] = omniauth_name }
@@ -94,7 +117,7 @@ after_omniauth_create_account do
94
117
  end
95
118
  ```
96
119
 
97
- When the account is closed, its external identities are automatically cleared from the database.
120
+ When the account is closed, its external identities are automatically deleted from the database.
98
121
 
99
122
  ### Identity data
100
123
 
@@ -118,7 +141,7 @@ omniauth_identity_update_hash do
118
141
  end
119
142
  ```
120
143
 
121
- With this configuration, the identity record will be automatically synced with most recent state on each provider login. If you would like to only save provider data on first login, you can override the insert hash instead:
144
+ With this configuration, the identity record will be automatically synced with most recent state on each provider login. If you would like to only save provider data when the identity is created, you can override the insert hash instead:
122
145
 
123
146
  ```rb
124
147
  # this data will be stored only on first login
@@ -131,6 +154,16 @@ omniauth_identity_insert_hash do
131
154
  end
132
155
  ```
133
156
 
157
+ You can change the table name or any of the column names:
158
+
159
+ ```rb
160
+ omniauth_identities_table :account_identities
161
+ omniauth_identities_id_column :id
162
+ omniauth_identities_account_id_column :account_id
163
+ omniauth_identities_provider_column :provider
164
+ omniauth_identities_uid_column :uid
165
+ ```
166
+
134
167
  ### Model associations
135
168
 
136
169
  When using the [rodauth-model] gem, an `identities` one-to-many association will be defined on the account model:
@@ -205,13 +238,13 @@ URL helpers are provided as well:
205
238
  rodauth.prefix #=> "/user"
206
239
  rodauth.omniauth_prefix #=> "/auth"
207
240
 
208
- rodauth.omniauth_request_route #=> "auth/facebook"
209
- rodauth.omniauth_request_path #=> "/user/auth/facebook"
210
- rodauth.omniauth_request_url #=> "https://example.com/user/auth/facebook"
241
+ rodauth.omniauth_request_route(:facebook) #=> "auth/facebook"
242
+ rodauth.omniauth_request_path(:facebook) #=> "/user/auth/facebook"
243
+ rodauth.omniauth_request_url(:facebook) #=> "https://example.com/user/auth/facebook"
211
244
 
212
- rodauth.omniauth_callback_route #=> "auth/facebook/callback"
213
- rodauth.omniauth_callback_path #=> "/user/auth/facebook/callback"
214
- rodauth.omniauth_callback_url #=> "https://example.com/user/auth/facebook/callback"
245
+ rodauth.omniauth_callback_route(:facebook) #=> "auth/facebook/callback"
246
+ rodauth.omniauth_callback_path(:facebook) #=> "/user/auth/facebook/callback"
247
+ rodauth.omniauth_callback_url(:facebook) #=> "https://example.com/user/auth/facebook/callback"
215
248
  ```
216
249
 
217
250
  The prefix for the OmniAuth app can be changed:
@@ -257,9 +290,9 @@ Or provide your own implementation:
257
290
  ```rb
258
291
  omniauth_on_failure do
259
292
  case omniauth_error_type
260
- when :no_authorization_code then ...
261
- when :uknown_signature_algorithm then ...
262
- else ...
293
+ when :no_authorization_code then # ...
294
+ when :uknown_signature_algorithm then # ...
295
+ else # ...
263
296
  end
264
297
  end
265
298
  ```
@@ -308,16 +341,32 @@ rodauth(:admin).omniauth_providers #=> [:google_oauth2, :twitter, :github]
308
341
 
309
342
  ### JSON
310
343
 
311
- JSON requests are supported for the request and callback phases. The request phase endpoint will return the authorize URL:
344
+ JSON requests are supported for request and callback phases. The request phase endpoint will return the authorize URL:
312
345
 
313
346
  ```http
314
- POST /auth/facebook
347
+ POST /auth/github
315
348
  Accept: application/json
316
349
  Content-Type: application/json
350
+ ```
351
+ ```http
352
+ 200 OK
353
+ Content-Type: application/json
354
+
355
+ { "authorize_url": "https://github.com/login/oauth/authorize?..." }
356
+ ```
357
+
358
+ When you redirect the user to the authorize URL, and they authorize the OAuth app, the callback endpoint they're redirected to will contain query parameters that need to passed for the callback request to the backend.
317
359
 
360
+ ```http
361
+ GET /auth/github/callback?code=...&state=...
362
+ Accept: application/json
363
+ Content-Type: application/json
364
+ ```
365
+ ```http
318
366
  200 OK
319
367
  Content-Type: application/json
320
- { "authorize_url": "https://external.com/login" }
368
+
369
+ { "success": "You have been logged in" }
321
370
  ```
322
371
 
323
372
  If there was a login failure, the error type will be included in the response:
@@ -326,12 +375,22 @@ If there was a login failure, the error type will be included in the response:
326
375
  POST /auth/facebook/callback
327
376
  Accept: application/json
328
377
  Content-Type: application/json
329
-
378
+ ```
379
+ ```http
330
380
  500 Internal Server Error
331
381
  Content-Type: application/json
382
+
332
383
  { "error_type": "some_error", "error": "There was an error logging in with the external provider" }
333
384
  ```
334
385
 
386
+ In this flow, you'll need to configure the callback URL on the OAuth app to point to the frontend app. On the OmniAuth strategy, you'll need to configure the same for GitHub requests, but keep the backend callback endpoint. For strategies based on [omniauth-oauth2], you can achieve this as follows:
387
+
388
+ ```rb
389
+ omniauth_provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"],
390
+ authorize_params: { redirect_uri: "https://frontend.example.com/github/callback" },
391
+ token_params: { redirect_uri: "https://frontend.example.com/github/callback" }
392
+ ```
393
+
335
394
  You can change authorize URL and error type keys:
336
395
 
337
396
  ```rb
@@ -341,7 +400,7 @@ omniauth_error_type_key "error_type"
341
400
 
342
401
  ### JWT
343
402
 
344
- JWT requests are supported for the request and callback phases. OmniAuth information will be stored in JWT session data during the request phase, and restored during the callback phase, as long as the updated JWT token is passed.
403
+ JWT requests are supported for the request and callback phases. OmniAuth data will be stored in the JWT token during the request phase, and restored during the callback phase, as long as the updated JWT token is passed.
345
404
 
346
405
  ## Development
347
406
 
@@ -351,6 +410,10 @@ Run tests with Rake:
351
410
  $ bundle exec rake test
352
411
  ```
353
412
 
413
+ ## Credits
414
+
415
+ The implementation of this gem was inspired by [this OmniAuth guide](https://github.com/omniauth/omniauth/wiki/Managing-Multiple-Providers).
416
+
354
417
  ## License
355
418
 
356
419
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -362,3 +425,4 @@ Everyone interacting in the rodauth-omniauth project's codebases, issue trackers
362
425
  [Rodauth]: https://github.com/jeremyevans/rodauth
363
426
  [OmniAuth]: https://github.com/omniauth/omniauth
364
427
  [rodauth-model]: https://github.com/janko/rodauth-model
428
+ [omniauth-oauth2]: https://github.com/omniauth/omniauth-oauth2
@@ -31,6 +31,7 @@ module Rodauth
31
31
 
32
32
  auth_private_methods(
33
33
  :retrieve_omniauth_identity,
34
+ :account_from_omniauth,
34
35
  :account_from_omniauth_identity,
35
36
  :omniauth_new_account,
36
37
  )
@@ -57,11 +58,11 @@ module Rodauth
57
58
  end
58
59
 
59
60
  unless account
60
- account_from_login(omniauth_email)
61
+ account_from_omniauth
61
62
  end
62
63
 
63
64
  if account && !open_account?
64
- set_redirect_error_status unopen_account_error_status
65
+ set_response_error_reason_status(:unverified_account, unopen_account_error_status)
65
66
  set_redirect_error_flash omniauth_login_unverified_account_error_flash
66
67
  redirect omniauth_login_failure_redirect
67
68
  end
@@ -92,6 +93,10 @@ module Rodauth
92
93
  @account = _account_from_omniauth_identity
93
94
  end
94
95
 
96
+ def account_from_omniauth
97
+ @account = _account_from_omniauth
98
+ end
99
+
95
100
  def omniauth_new_account
96
101
  @account = _omniauth_new_account(omniauth_email)
97
102
  end
@@ -112,6 +117,16 @@ module Rodauth
112
117
 
113
118
  private
114
119
 
120
+ def before_confirm_password
121
+ authenticated_by.delete("omniauth")
122
+ super if defined?(super)
123
+ end
124
+
125
+ def after_close_account
126
+ super if defined?(super)
127
+ remove_omniauth_identities
128
+ end
129
+
115
130
  def allow_email_auth?
116
131
  (defined?(super) ? super : true) && omniauth_account_identities_ds.empty?
117
132
  end
@@ -159,13 +174,12 @@ module Rodauth
159
174
  )
160
175
  end
161
176
 
162
- def _account_from_omniauth_identity
163
- account_ds(omniauth_identity_account_id).first
177
+ def _account_from_omniauth
178
+ _account_from_login(omniauth_email)
164
179
  end
165
180
 
166
- def after_close_account
167
- super if defined?(super)
168
- remove_omniauth_identities
181
+ def _account_from_omniauth_identity
182
+ account_ds(omniauth_identity_account_id).first
169
183
  end
170
184
 
171
185
  def omniauth_identity_id
@@ -138,7 +138,7 @@ module Rodauth
138
138
  json_response[omniauth_error_type_key] = omniauth_error_type
139
139
  end
140
140
 
141
- set_redirect_error_status omniauth_failure_error_status
141
+ set_response_error_reason_status(:omniauth_failure, omniauth_failure_error_status)
142
142
  set_redirect_error_flash omniauth_failure_error_flash
143
143
  redirect omniauth_failure_redirect
144
144
  end
@@ -164,9 +164,11 @@ module Rodauth
164
164
  # Makes OmniAuth strategies use the JWT session hash.
165
165
  def set_omniauth_jwt_session
166
166
  rack_session = request.env["rack.session"]
167
+ session.keys.each { |k| session[k.to_s] = session.delete(k) } unless scope.opts[:sessions_convert_symbols]
167
168
  request.env["rack.session"] = session
168
169
  yield
169
170
  ensure
171
+ session.keys.each { |k| session[k.to_sym] = session.delete(k) } unless scope.opts[:sessions_convert_symbols]
170
172
  request.env["rack.session"] = rack_session
171
173
  end
172
174
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "rodauth-omniauth"
3
- spec.version = "0.1.0"
3
+ spec.version = "0.3.0"
4
4
  spec.authors = ["Janko Marohnić"]
5
5
  spec.email = ["janko@hey.com"]
6
6
 
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.files = Dir["README.md", "LICENSE.txt", "*.gemspec", "lib/**/*"]
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency "rodauth", "~> 2.0"
20
+ spec.add_dependency "rodauth", "~> 2.13"
21
21
  spec.add_dependency "omniauth", "~> 2.0"
22
22
 
23
23
  spec.add_development_dependency "minitest"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-omniauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-03 00:00:00.000000000 Z
11
+ date: 2022-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rodauth
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '2.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '2.13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: omniauth
29
29
  requirement: !ruby/object:Gem::Requirement