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 +4 -4
- data/README.md +102 -38
- data/lib/rodauth/features/omniauth.rb +21 -7
- data/lib/rodauth/features/omniauth_base.rb +3 -1
- data/rodauth-omniauth.gemspec +2 -2
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b983cad17d20d291428c75e8efd5d59b313e83ac68f7998d7d1ec6c508db0ac4
|
4
|
+
data.tar.gz: e5db5f29f5d2897312d9dc4eca1cc34718ff56bd024bf2f79473a230a9e3cfbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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]
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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/
|
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
|
-
|
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
|
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
|
-
|
61
|
+
account_from_omniauth
|
61
62
|
end
|
62
63
|
|
63
64
|
if account && !open_account?
|
64
|
-
|
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
|
163
|
-
|
177
|
+
def _account_from_omniauth
|
178
|
+
_account_from_login(omniauth_email)
|
164
179
|
end
|
165
180
|
|
166
|
-
def
|
167
|
-
|
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
|
-
|
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
|
|
data/rodauth-omniauth.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "rodauth-omniauth"
|
3
|
-
spec.version = "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.
|
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.
|
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
|
+
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.
|
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.
|
26
|
+
version: '2.13'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: omniauth
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|