rodauth-oauth 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/doc/release_notes/0_9_3.md +9 -0
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +1 -0
- data/lib/rodauth/features/oauth_base.rb +42 -53
- data/lib/rodauth/features/oauth_jwt.rb +18 -32
- data/lib/rodauth/features/oidc.rb +3 -3
- data/lib/rodauth/oauth/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79fa7abfcf7ea1c0a2594f92bfd7a59e53769dec5a7221a22f740b37471e6e07
|
4
|
+
data.tar.gz: 9f7e8ce4370d44d985940d46d569e3192c35e53cb6e3841094517e64258b2b1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 520a85416c418f93615a471133201c7ec6dd5c16ff9d697a6ca87b609084b58c7b9799c5d3c7a8d02a4fc68ca9750c9ef41dd07dde17c1e1153f0cec8e58f3c1
|
7
|
+
data.tar.gz: 8e7ebc28ba158f43b5226a2de98fc76de8c81e8a8c87dbaa95a69050eed96793e487666389b60be6694437c298fdf23f183a1df098983eeec5f5549df0321c7c
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Rodauth::Oauth
|
2
2
|
|
3
|
-
[![
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/rodauth-oauth.svg)](http://rubygems.org/gems/rodauth-oauth)
|
4
|
+
[![pipeline status](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/rodauth-oauth/pipelines?page=1&scope=all&ref=master)
|
4
5
|
[![coverage report](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/coverage.svg?job=coverage)](https://honeyryderchuck.gitlab.io/rodauth-oauth/coverage/#_AllFiles)
|
5
6
|
|
6
7
|
This is an extension to the `rodauth` gem which implements the [OAuth 2.0 framework](https://tools.ietf.org/html/rfc6749) for an authorization server.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
### 0.9.2 (30/05/2022)
|
2
|
+
|
3
|
+
#### Bugfixes
|
4
|
+
|
5
|
+
* `oauth_jwt`: new access tokens generated via the `"refresh_token"` grant type are now JWT (it was falling back to non JWT behaviour);
|
6
|
+
* `oidc`: a new `id_token` is now generated via the `"refresh_token"` grant type with "rotation" policy (it was being omitted from the response);
|
7
|
+
* `oidc`: fixing calculation of `"auth_time"` claim, which (as per RFC) needs to stay the same across first authentication and subsequent `"refresh_token"` requests;
|
8
|
+
* it requires a new db column (default: `"auth_time"`, datetime) in the `"oauth_tokens"` database;
|
9
|
+
* hash-column `"refresh_token"` will now expose the refresh token (instead of the hash column version) in the `"refresh_token"` grant type response payload (only happened in "non-rotation" refresh token mode).
|
@@ -426,32 +426,40 @@ module Rodauth
|
|
426
426
|
}.merge(params)
|
427
427
|
|
428
428
|
rescue_from_uniqueness_error do
|
429
|
-
|
429
|
+
access_token = _generate_access_token(create_params)
|
430
|
+
refresh_token = _generate_refresh_token(create_params) if should_generate_refresh_token
|
431
|
+
oauth_token = _store_oauth_token(create_params)
|
432
|
+
oauth_token[oauth_tokens_token_column] = access_token
|
433
|
+
oauth_token[oauth_tokens_refresh_token_column] = refresh_token if refresh_token
|
434
|
+
oauth_token
|
435
|
+
end
|
436
|
+
end
|
430
437
|
|
431
|
-
|
432
|
-
|
433
|
-
else
|
434
|
-
create_params[oauth_tokens_token_column] = token
|
435
|
-
end
|
438
|
+
def _generate_access_token(params = {})
|
439
|
+
token = oauth_unique_id_generator
|
436
440
|
|
437
|
-
|
438
|
-
|
439
|
-
|
441
|
+
if oauth_tokens_token_hash_column
|
442
|
+
params[oauth_tokens_token_hash_column] = generate_token_hash(token)
|
443
|
+
else
|
444
|
+
params[oauth_tokens_token_column] = token
|
445
|
+
end
|
440
446
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
447
|
+
token
|
448
|
+
end
|
449
|
+
|
450
|
+
def _generate_refresh_token(params)
|
451
|
+
token = oauth_unique_id_generator
|
452
|
+
|
453
|
+
if oauth_tokens_refresh_token_hash_column
|
454
|
+
params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(token)
|
455
|
+
else
|
456
|
+
params[oauth_tokens_refresh_token_column] = token
|
451
457
|
end
|
458
|
+
|
459
|
+
token
|
452
460
|
end
|
453
461
|
|
454
|
-
def
|
462
|
+
def _store_oauth_token(params = {})
|
455
463
|
ds = db[oauth_tokens_table]
|
456
464
|
|
457
465
|
if __one_oauth_token_per_account
|
@@ -577,43 +585,24 @@ module Rodauth
|
|
577
585
|
|
578
586
|
rescue_from_uniqueness_error do
|
579
587
|
oauth_tokens_ds = db[oauth_tokens_table]
|
580
|
-
|
588
|
+
access_token = _generate_access_token(update_params)
|
581
589
|
|
582
|
-
if
|
583
|
-
update_params
|
590
|
+
if oauth_refresh_token_protection_policy == "rotation"
|
591
|
+
update_params = {
|
592
|
+
**update_params,
|
593
|
+
oauth_tokens_oauth_token_id_column => oauth_token[oauth_tokens_id_column],
|
594
|
+
oauth_tokens_account_id_column => oauth_token[oauth_tokens_account_id_column],
|
595
|
+
oauth_tokens_scopes_column => oauth_token[oauth_tokens_scopes_column]
|
596
|
+
}
|
597
|
+
|
598
|
+
refresh_token = _generate_refresh_token(update_params)
|
584
599
|
else
|
585
|
-
|
600
|
+
refresh_token = param("refresh_token")
|
586
601
|
end
|
602
|
+
oauth_token = __update_and_return__(oauth_tokens_ds, update_params)
|
587
603
|
|
588
|
-
oauth_token =
|
589
|
-
|
590
|
-
**update_params,
|
591
|
-
oauth_tokens_oauth_token_id_column => oauth_token[oauth_tokens_id_column],
|
592
|
-
oauth_tokens_scopes_column => oauth_token[oauth_tokens_scopes_column]
|
593
|
-
}
|
594
|
-
|
595
|
-
refresh_token = oauth_unique_id_generator
|
596
|
-
|
597
|
-
if oauth_tokens_refresh_token_hash_column
|
598
|
-
insert_params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(refresh_token)
|
599
|
-
else
|
600
|
-
insert_params[oauth_tokens_refresh_token_column] = refresh_token
|
601
|
-
end
|
602
|
-
|
603
|
-
# revoke the refresh token
|
604
|
-
oauth_tokens_ds.where(oauth_tokens_id_column => oauth_token[oauth_tokens_id_column])
|
605
|
-
.update(oauth_tokens_revoked_at_column => Sequel::CURRENT_TIMESTAMP)
|
606
|
-
|
607
|
-
insert_params[oauth_tokens_oauth_token_id_column] = oauth_token[oauth_tokens_id_column]
|
608
|
-
__insert_and_return__(oauth_tokens_ds, oauth_tokens_id_column, insert_params)
|
609
|
-
else
|
610
|
-
# includes none
|
611
|
-
ds = oauth_tokens_ds.where(oauth_tokens_id_column => oauth_token[oauth_tokens_id_column])
|
612
|
-
__update_and_return__(ds, update_params)
|
613
|
-
end
|
614
|
-
|
615
|
-
oauth_token[oauth_tokens_token_column] = token
|
616
|
-
oauth_token[oauth_tokens_refresh_token_column] = refresh_token if refresh_token
|
604
|
+
oauth_token[oauth_tokens_token_column] = access_token
|
605
|
+
oauth_token[oauth_tokens_refresh_token_column] = refresh_token
|
617
606
|
oauth_token
|
618
607
|
end
|
619
608
|
end
|
@@ -66,7 +66,6 @@ module Rodauth
|
|
66
66
|
:jwt_encode,
|
67
67
|
:jwt_decode,
|
68
68
|
:jwks_set,
|
69
|
-
:last_account_login_at,
|
70
69
|
:generate_jti
|
71
70
|
)
|
72
71
|
|
@@ -99,12 +98,6 @@ module Rodauth
|
|
99
98
|
|
100
99
|
private
|
101
100
|
|
102
|
-
unless method_defined?(:last_account_login_at)
|
103
|
-
def last_account_login_at
|
104
|
-
nil
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
101
|
def issuer
|
109
102
|
@issuer ||= oauth_jwt_token_issuer || authorization_server_url
|
110
103
|
end
|
@@ -175,41 +168,38 @@ module Rodauth
|
|
175
168
|
|
176
169
|
# /token
|
177
170
|
|
178
|
-
def
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
if should_generate_refresh_token
|
185
|
-
refresh_token = oauth_unique_id_generator
|
186
|
-
|
187
|
-
if oauth_tokens_refresh_token_hash_column
|
188
|
-
create_params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(refresh_token)
|
189
|
-
else
|
190
|
-
create_params[oauth_tokens_refresh_token_column] = refresh_token
|
191
|
-
end
|
192
|
-
end
|
171
|
+
def create_oauth_token_from_token(oauth_token, update_params)
|
172
|
+
otoken = super
|
173
|
+
access_token = _generate_jwt_access_token(otoken)
|
174
|
+
otoken[oauth_tokens_token_column] = access_token
|
175
|
+
otoken
|
176
|
+
end
|
193
177
|
|
194
|
-
|
195
|
-
|
178
|
+
def generate_oauth_token(params = {}, should_generate_refresh_token = true)
|
179
|
+
oauth_token = super
|
180
|
+
access_token = _generate_jwt_access_token(oauth_token)
|
181
|
+
oauth_token[oauth_tokens_token_column] = access_token
|
182
|
+
oauth_token
|
183
|
+
end
|
196
184
|
|
185
|
+
def _generate_jwt_access_token(oauth_token)
|
197
186
|
claims = jwt_claims(oauth_token)
|
198
187
|
|
199
188
|
# one of the points of using jwt is avoiding database lookups, so we put here all relevant
|
200
189
|
# token data.
|
201
190
|
claims[:scope] = oauth_token[oauth_tokens_scopes_column]
|
202
191
|
|
203
|
-
|
192
|
+
jwt_encode(claims)
|
193
|
+
end
|
204
194
|
|
205
|
-
|
206
|
-
|
195
|
+
def _generate_access_token(*)
|
196
|
+
# no op
|
207
197
|
end
|
208
198
|
|
209
199
|
def jwt_claims(oauth_token)
|
210
200
|
issued_at = Time.now.to_i
|
211
201
|
|
212
|
-
|
202
|
+
{
|
213
203
|
iss: issuer, # issuer
|
214
204
|
iat: issued_at, # issued at
|
215
205
|
#
|
@@ -227,10 +217,6 @@ module Rodauth
|
|
227
217
|
exp: issued_at + oauth_token_expires_in,
|
228
218
|
aud: (oauth_jwt_audience || oauth_application[oauth_applications_client_id_column])
|
229
219
|
}
|
230
|
-
|
231
|
-
claims[:auth_time] = last_account_login_at.to_i if last_account_login_at
|
232
|
-
|
233
|
-
claims
|
234
220
|
end
|
235
221
|
|
236
222
|
def jwt_subject(oauth_token)
|
@@ -74,6 +74,7 @@ module Rodauth
|
|
74
74
|
|
75
75
|
auth_value_method :oauth_grants_nonce_column, :nonce
|
76
76
|
auth_value_method :oauth_tokens_nonce_column, :nonce
|
77
|
+
auth_value_method :oauth_tokens_auth_time_column, :auth_time
|
77
78
|
|
78
79
|
translatable_method :invalid_scope_message, "The Access Token expired"
|
79
80
|
|
@@ -341,8 +342,7 @@ module Rodauth
|
|
341
342
|
|
342
343
|
# Time when the End-User authentication occurred.
|
343
344
|
#
|
344
|
-
|
345
|
-
id_token_claims[:auth_time] = id_token_claims[:iat]
|
345
|
+
id_token_claims[:auth_time] = oauth_token[oauth_tokens_auth_time_column].to_i
|
346
346
|
|
347
347
|
account = db[accounts_table].where(account_id_column => oauth_token[oauth_tokens_account_id_column]).first
|
348
348
|
|
@@ -488,7 +488,7 @@ module Rodauth
|
|
488
488
|
end
|
489
489
|
end
|
490
490
|
|
491
|
-
scope_claims.unshift("auth_time")
|
491
|
+
scope_claims.unshift("auth_time")
|
492
492
|
|
493
493
|
response_types_supported = metadata[:response_types_supported]
|
494
494
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth-oauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rodauth
|
@@ -59,6 +59,7 @@ extra_rdoc_files:
|
|
59
59
|
- doc/release_notes/0_9_0.md
|
60
60
|
- doc/release_notes/0_9_1.md
|
61
61
|
- doc/release_notes/0_9_2.md
|
62
|
+
- doc/release_notes/0_9_3.md
|
62
63
|
files:
|
63
64
|
- CHANGELOG.md
|
64
65
|
- LICENSE.txt
|
@@ -89,6 +90,7 @@ files:
|
|
89
90
|
- doc/release_notes/0_9_0.md
|
90
91
|
- doc/release_notes/0_9_1.md
|
91
92
|
- doc/release_notes/0_9_2.md
|
93
|
+
- doc/release_notes/0_9_3.md
|
92
94
|
- lib/generators/rodauth/oauth/install_generator.rb
|
93
95
|
- lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
|
94
96
|
- lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb
|