rodauth-oauth 0.7.4 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -424
  3. data/README.md +26 -389
  4. data/doc/release_notes/0_0_1.md +3 -0
  5. data/doc/release_notes/0_0_2.md +15 -0
  6. data/doc/release_notes/0_0_3.md +31 -0
  7. data/doc/release_notes/0_0_4.md +36 -0
  8. data/doc/release_notes/0_0_5.md +36 -0
  9. data/doc/release_notes/0_0_6.md +21 -0
  10. data/doc/release_notes/0_1_0.md +44 -0
  11. data/doc/release_notes/0_2_0.md +43 -0
  12. data/doc/release_notes/0_3_0.md +28 -0
  13. data/doc/release_notes/0_4_0.md +18 -0
  14. data/doc/release_notes/0_4_1.md +9 -0
  15. data/doc/release_notes/0_4_2.md +5 -0
  16. data/doc/release_notes/0_4_3.md +3 -0
  17. data/doc/release_notes/0_5_0.md +11 -0
  18. data/doc/release_notes/0_5_1.md +13 -0
  19. data/doc/release_notes/0_6_0.md +9 -0
  20. data/doc/release_notes/0_6_1.md +6 -0
  21. data/doc/release_notes/0_7_0.md +20 -0
  22. data/doc/release_notes/0_7_1.md +10 -0
  23. data/doc/release_notes/0_7_2.md +21 -0
  24. data/doc/release_notes/0_7_3.md +10 -0
  25. data/doc/release_notes/0_7_4.md +5 -0
  26. data/doc/release_notes/0_8_0.md +37 -0
  27. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +3 -3
  28. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +11 -0
  29. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +20 -0
  30. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +22 -10
  31. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +11 -5
  32. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +38 -0
  33. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +5 -5
  34. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +11 -15
  35. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +9 -1
  36. data/lib/rodauth/features/oauth.rb +3 -1418
  37. data/lib/rodauth/features/oauth_application_management.rb +209 -0
  38. data/lib/rodauth/features/oauth_assertion_base.rb +96 -0
  39. data/lib/rodauth/features/oauth_authorization_code_grant.rb +249 -0
  40. data/lib/rodauth/features/oauth_authorization_server.rb +0 -0
  41. data/lib/rodauth/features/oauth_base.rb +735 -0
  42. data/lib/rodauth/features/oauth_device_grant.rb +221 -0
  43. data/lib/rodauth/features/oauth_http_mac.rb +3 -21
  44. data/lib/rodauth/features/oauth_implicit_grant.rb +59 -0
  45. data/lib/rodauth/features/oauth_jwt.rb +37 -60
  46. data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +59 -0
  47. data/lib/rodauth/features/oauth_pkce.rb +98 -0
  48. data/lib/rodauth/features/oauth_resource_server.rb +21 -0
  49. data/lib/rodauth/features/oauth_saml_bearer_grant.rb +102 -0
  50. data/lib/rodauth/features/oauth_token_introspection.rb +108 -0
  51. data/lib/rodauth/features/oauth_token_management.rb +77 -0
  52. data/lib/rodauth/features/oauth_token_revocation.rb +109 -0
  53. data/lib/rodauth/features/oidc.rb +4 -3
  54. data/lib/rodauth/oauth/database_extensions.rb +15 -2
  55. data/lib/rodauth/oauth/refinements.rb +48 -0
  56. data/lib/rodauth/oauth/version.rb +1 -1
  57. data/locales/en.yml +28 -12
  58. data/templates/authorize.str +7 -7
  59. data/templates/client_secret_field.str +2 -2
  60. data/templates/description_field.str +1 -1
  61. data/templates/device_search.str +11 -0
  62. data/templates/device_verification.str +24 -0
  63. data/templates/homepage_url_field.str +2 -2
  64. data/templates/jws_jwk_field.str +4 -0
  65. data/templates/jwt_public_key_field.str +4 -0
  66. data/templates/name_field.str +1 -1
  67. data/templates/new_oauth_application.str +9 -0
  68. data/templates/oauth_application.str +7 -3
  69. data/templates/oauth_application_oauth_tokens.str +51 -0
  70. data/templates/oauth_applications.str +2 -2
  71. data/templates/oauth_tokens.str +9 -11
  72. data/templates/redirect_uri_field.str +2 -2
  73. metadata +71 -3
  74. data/lib/rodauth/features/oauth_saml.rb +0 -104
data/README.md CHANGED
@@ -9,23 +9,26 @@ This is an extension to the `rodauth` gem which implements the [OAuth 2.0 framew
9
9
 
10
10
  This gem implements the following RFCs and features of OAuth:
11
11
 
12
- * [The OAuth 2.0 protocol framework](https://tools.ietf.org/html/rfc6749):
13
- * [Authorization grant flow](https://tools.ietf.org/html/rfc6749#section-1.3);
12
+ * `oauth` - [The OAuth 2.0 protocol framework](https://tools.ietf.org/html/rfc6749):
14
13
  * [Access Token generation](https://tools.ietf.org/html/rfc6749#section-1.4);
15
14
  * [Access Token refresh](https://tools.ietf.org/html/rfc6749#section-1.5);
16
- * [Implicit grant (off by default)[https://tools.ietf.org/html/rfc6749#section-4.2];
17
- * [Token revocation](https://tools.ietf.org/html/rfc7009);
18
- * [Token introspection](https://tools.ietf.org/html/rfc7662);
19
- * [Authorization Server Metadata](https://tools.ietf.org/html/rfc8414);
20
- * [PKCE](https://tools.ietf.org/html/rfc7636);
21
- * Access Type (Token refresh online and offline);
22
- * [MAC Authentication Scheme](https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-02);
23
- * [JWT Acess Tokens](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-07);
24
- * [SAML 2.0 Assertion Access Tokens](https://tools.ietf.org/html/draft-ietf-oauth-saml2-bearer-03);
15
+ * `oauth_authorization_code_grant` - [Authorization grant flow](https://tools.ietf.org/html/rfc6749#section-1.3);
16
+ * `oauth_implicit_grant` - [Implicit grant (off by default)](https://tools.ietf.org/html/rfc6749#section-4.2);
17
+ * `oauth_device_grant` - [Device code grant (off by default)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-device-flow-15);
18
+ * `oauth_token_revocation` - [Token revocation](https://tools.ietf.org/html/rfc7009);
19
+ * `oauth_token_introspection` - [Token introspection](https://tools.ietf.org/html/rfc7662);
20
+ * [Authorization Server Metadata](https://tools.ietf.org/html/rfc8414);
21
+ * `oauth_pkce` - [PKCE](https://tools.ietf.org/html/rfc7636);
22
+ * Access Type (Token refresh online and offline);
23
+ * `oauth_jwt` - [JWT Access Tokens](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-07);
24
+ * `oauth_http_mac` - [MAC Authentication Scheme](https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-02);
25
+ * `oauth_assertion_base` - [Assertion Framework](https://datatracker.ietf.org/doc/html/rfc7521);
26
+ * `oauth_saml_bearer_grant` - [SAML 2.0 Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7522);
27
+ * `oauth_jwt_bearer_grant` - [JWT Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7523);
25
28
  * [JWT Secured Authorization Requests](https://tools.ietf.org/html/draft-ietf-oauth-jwsreq-20);
26
29
  * OAuth application and token management dashboards;
27
30
 
28
- It also implements the [OpenID Connect layer](https://openid.net/connect/) on top of the OAuth features it provides, including:
31
+ It also implements the [OpenID Connect layer](https://openid.net/connect/) (via the `openid` feature) on top of the OAuth features it provides, including:
29
32
 
30
33
  * [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html);
31
34
  * [OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0-29.html);
@@ -60,6 +63,11 @@ Or install it yourself as:
60
63
  | Wiki | https://gitlab.com/honeyryderchuck/rodauth-oauth/wikis/home |
61
64
  | CI | https://gitlab.com/honeyryderchuck/rodauth-oauth/pipelines |
62
65
 
66
+ ## Articles
67
+
68
+ * [How to use rodauth-oauth with rails and rodauth](https://honeyryderchuck.gitlab.io/httpx/2021/03/15/oidc-provider-on-rails-using-rodauth-oauth.html)
69
+ * [How to use rodauth-oauth with rails and without rodauth](https://honeyryderchuck.gitlab.io/httpx/2021/09/08/using-rodauth-oauth-in-rails-without-rodauth-based-auth.html)
70
+
63
71
  ## Usage
64
72
 
65
73
  This tutorial assumes you already read the documentation and know how to set up `rodauth`. After that, integrating `roda-auth` will look like:
@@ -118,134 +126,12 @@ end
118
126
 
119
127
  ### Example (TL;DR)
120
128
 
121
- If you're familiar with the technology and want to skip the next paragraphs, just [check our example applications](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/tree/master/examples/).
122
-
123
-
124
- Generating tokens happens mostly server-to-server, so here's an example using:
125
-
126
- #### Access Token Generation
127
-
128
- ##### HTTPX
129
-
130
- ```ruby
131
- require "httpx"
132
- response = HTTPX.post("https://auth_server/token",json: {
133
- client_id: ENV["OAUTH_CLIENT_ID"],
134
- client_secret: ENV["OAUTH_CLIENT_SECRET"],
135
- grant_type: "authorization_code",
136
- code: "oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as"
137
- })
138
- response.raise_for_status
139
- payload = JSON.parse(response.to_s)
140
- puts payload #=> {"access_token" => "awr23f3h8f9d2h89...", "refresh_token" => "23fkop3kr290kc..." ....
141
- ```
142
-
143
- ##### cURL
144
-
145
- ```
146
- > curl --data '{"client_id":"$OAUTH_CLIENT_ID","client_secret":"$OAUTH_CLIENT_SECRET","grant_type":"authorization_code","code":"oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as"}' https://auth_server/token
147
- ```
148
-
149
- #### Refresh Token
150
-
151
- Refreshing expired tokens also happens mostly server-to-server, here's an example:
152
-
153
- ##### HTTPX
154
-
155
- ```ruby
156
- require "httpx"
157
- response = HTTPX.post("https://auth_server/token",json: {
158
- client_id: ENV["OAUTH_CLIENT_ID"],
159
- client_secret: ENV["OAUTH_CLIENT_SECRET"],
160
- grant_type: "refresh_token",
161
- token: "2r89hfef4j9f90d2j2390jf390g"
162
- })
163
- response.raise_for_status
164
- payload = JSON.parse(response.to_s)
165
- puts payload #=> {"access_token" => "awr23f3h8f9d2h89...", "token_type" => "Bearer" ....
166
- ```
167
-
168
- ##### cURL
169
-
170
- ```
171
- > curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","client_secret":"$OAUTH_CLIENT_SECRET","grant_type":"token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/token
172
- ```
173
-
174
- #### Revoking tokens
175
-
176
- Token revocation can be done both by the identity owner or the application owner, and can therefore be done either online (browser-based form) or server-to-server. Here's an example using server-to-server:
177
-
178
- ```ruby
179
- require "httpx"
180
- httpx = HTTPX.plugin(:basic_authorization)
181
- response = httpx.basic_authentication(ENV["CLIENT_ID"], ENV["CLIENT_SECRET"])
182
- .post("https://auth_server/revoke",json: {
183
- token_type_hint: "access_token", # can also be "refresh:tokn"
184
- token: "2r89hfef4j9f90d2j2390jf390g"
185
- })
186
- response.raise_for_status
187
- payload = JSON.parse(response.to_s)
188
- puts payload #=> {"access_token" => "awr23f3h8f9d2h89...", "token_type" => "Bearer" ....
189
- ```
190
-
191
- ##### cURL
192
-
193
- ```
194
- > curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","token_type_hint":"access_token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/revoke
195
- ```
196
-
197
- #### Token introspection
198
-
199
- Token revocation can be used to determine the state of a token (whether active, what's the scope...) . Here's an example using server-to-server:
200
-
201
- ```ruby
202
- require "httpx"
203
- httpx = HTTPX.plugin(:basic_authorization)
204
- response = httpx.basic_authentication(ENV["CLIENT_ID"], ENV["CLIENT_SECRET"])
205
- .post("https://auth_server/introspect",json: {
206
- token_type_hint: "access_token", # can also be "refresh:tokn"
207
- token: "2r89hfef4j9f90d2j2390jf390g"
208
- })
209
- response.raise_for_status
210
- payload = JSON.parse(response.to_s)
211
- puts payload #=> {"active" => true, "scope" => "read write" ....
212
- ```
213
-
214
- ##### cURL
215
-
216
- ```
217
- > curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","token_type_hint":"access_token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/revoke
218
- ```
219
-
220
- ### Authorization Server Metadata
221
-
222
- The Authorization Server Metadata endpoint can be used by clients to obtain the information needed to interact with an
223
- OAuth 2.0 authorization server, i.e. know which endpoint is used to authorize clients.
224
-
225
- Because this endpoint **must be https://AUTHSERVER/.well-known/oauth-authorization-server**, you'll have to define it at the root-level of your app:
226
-
227
- ```ruby
228
- plugin :rodauth do
229
- # enable it in the plugin
230
- enable :login, :oauth
231
- oauth_application_default_scope %w[profile.read]
232
- oauth_application_scopes %w[profile.read profile.write]
233
- end
234
-
235
- # then, inside roda
236
-
237
- route do |r|
238
- r.rodauth
239
- # server metadata endpoint
240
- rodauth.oauth_server_metadata
129
+ Just [check our example applications](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/tree/master/examples/).
241
130
 
242
- # now, your oauth and app code...
243
-
244
- ```
245
131
 
246
132
  ### Database migrations
247
133
 
248
- You have to generate database tables for Oauth applications, grants and tokens. In order for you to hit the ground running, [here's a set of migrations (using `sequel`) to generate the needed tables](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/tree/master/test/migrate) (omit the first 2 if you already have account tables).
134
+ You have to generate database tables for accounts, oauth applications, grants and tokens. In order for you to hit the ground running, [here's a set of migrations (using `sequel`) to generate the needed tables](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/tree/master/test/migrate) (omit the first 2 if you already have account tables, and [follow recommendations from rodauth accordingly](https://github.com/jeremyevans/rodauth)).
249
135
 
250
136
  You can change column names or even use existing tables, however, be aware that you'll have to define new column accessors at the `rodauth` plugin declaration level. Let's say, for instance, you'd like to change the `oauth_grants` table name to `access_grants`, and it's `code` column to `authorization_code`; then, you'd have to do the following:
251
137
 
@@ -267,16 +153,7 @@ You'll have to generate HTML templates for the Oauth Authorization form.
267
153
 
268
154
  The rodauth default setup expects the roda `render` plugin to be activated; by default, it expects a `views` directory to be defined in the project root folder. The Oauth Authorization template must be therefore defined there, and it should be called `oauth_authorize.(erb|str|...)` (read the [roda `render` plugin documentation](http://roda.jeremyevans.net/rdoc/classes/Roda/RodaPlugins/Render.html) for more info about HTML templating).
269
155
 
270
- ### Endpoints
271
-
272
- Once you set it up, by default, the following endpoints will be available:
273
-
274
- * `GET /authorize`: Loads the OAuth authorization HTML form;
275
- * `POST /authorize`: Responds to an OAuth authorization request, as [per the spec](https://tools.ietf.org/html/rfc6749#section-4);
276
- * `POST /token`: Generates OAuth tokens as [per the spec](https://tools.ietf.org/html/rfc6749#section-4.4.2);
277
- * `POST /revoke`: Revokes OAuth tokens as [per the spec](https://tools.ietf.org/html/rfc7009);
278
-
279
- ### OAuth applications
156
+ ### OAuth applications management
280
157
 
281
158
  This feature is **optional**, as not all authorization servers will want a full oauth applications dashboard. However, if you do and you don't want to do the work yourself, you can set it up in your roda app like this:
282
159
 
@@ -290,24 +167,11 @@ route do |r|
290
167
  end
291
168
  ```
292
169
 
293
- This will define the following endpoints:
294
-
295
- * `GET /oauth-applications`: returns the OAuth applications HTML dashboard;
296
- * `GET /oauth-applications/{application_id}`: returns an OAuth application HTML page;
297
- * `GET /oauth-applications/{application_id}/oauth-tokens`: returns the OAuth tokens from an OAuth application HTML page;
298
- * `GET /oauth-applications/new`: returns a new OAuth application form;
299
- * `POST /oauth-applications`: processes a new OAuth application request;
300
-
301
- As in the OAuth authorization form example, you'll have to define the following HTML templates in order to use this feature:
302
-
303
- * `oauth_applications.(erb|str|...)`: the list of OAuth applications;
304
- * `oauth_application.(erb|str|...)`: the OAuth application page;
305
- * `new_oauth_application.(erb|str|...)`: the new OAuth application form;
306
- * `oauth_tokens.(erb|str|...)`: the list of OAuth tokens from an application;
170
+ Navigate to `"http://your-app/oauth-applications"` and click around.
307
171
 
308
172
  ## Rails
309
173
 
310
- This library provides a thin integration layer on top of [rodauth-rails](https://github.com/janko/rodauth-rails). Therefore, the first step you'll have to take is to integrate it in your project. Fortunately, it's very straightforward.
174
+ Support for `rails` is achieved thanks to [rodauth-rails](https://github.com/janko/rodauth-rails). Therefore, the first step you'll have to take is to add it to your dependencies.
311
175
 
312
176
  You'll have to run the generator task to create the necessary migrations and views:
313
177
 
@@ -316,7 +180,7 @@ You'll have to run the generator task to create the necessary migrations and vie
316
180
  # create a migration file, db/migrate(*_create_rodauth_oauth.rb);
317
181
  # Oauth Application, Grant and Token models into app/models;
318
182
  > bundle exec rails generate rodauth:oauth:views
319
- # creates view files under app/views/rodauth
183
+ # copies default view files into app/views/rodauth
320
184
  ```
321
185
 
322
186
  You are encouraged to check the output and adapt it to your needs.
@@ -403,230 +267,6 @@ plugin :rodauth do
403
267
  end
404
268
  ```
405
269
 
406
- ### Access Type (default: "offline")
407
-
408
- The "access_type" feature allows the authorization server to emit access tokens with no associated refresh token. This means that users with expired access tokens will have to go through the OAuth flow everytime they need a new one.
409
-
410
- In order to enable this option, add "access_type=online" to the query params section of the authorization url.
411
-
412
- #### Approval Prompt
413
-
414
- When using "online grants", one can use an extra query param in the URL, "approval_prompt", which when set to "auto", will skip the authorization form (on the other hand, if one wants to force the authorization form for all grants, then you can set it to "force", or don't set it at all, as it's the default).
415
-
416
- This will only work **if there was a previous successful online grant** for the same application, scopes and redirect URI.
417
-
418
- #### DB schema
419
-
420
- the "oauth_grants" table will have to include the "access_type" row:
421
-
422
- ```ruby
423
- # in migration
424
- String :access_type, null: false, default: "offline"
425
- ```
426
-
427
- If you want to disable this flow altogether, you can:
428
-
429
- ```ruby
430
- enable :oauth
431
- use_oauth_access_type? false
432
- ```
433
-
434
-
435
- ### Implicit Grant (default: disabled)
436
-
437
- The implicit grant flow is part of the original OAuth 2.0 RFC, however, if you care about security, you are **strongly recommended** not to enable it.
438
-
439
- However, if you really need it, just pass the option when enabling the `rodauth` plugin:
440
-
441
- ```ruby
442
- plugin :rodauth do
443
- enable :oauth
444
- use_oauth_implicit_grant_type true
445
- end
446
- ```
447
-
448
- And add "response_type=token" to the query params section of the authorization url.
449
-
450
- ### PKCE
451
-
452
- The "Proof Key for Code Exchange by OAuth Public Clients" (aka PKCE) flow, which is **particularly recommended for OAuth integration in mobile apps**, is transparently supported by `rodauth-oauth`, by adding the `code_challenge_method=S256&code_challenge=$YOUR_CODE_CHALLENGE` query params to the authorization url. Once you do that, you'll have to pass the `code_verifier` when generating a token:
453
-
454
- ```ruby
455
- # with httpx
456
- require "httpx"
457
- response = HTTPX.post("https://auth_server/token",json: {
458
- client_id: ENV["OAUTH_CLIENT_ID"],
459
- grant_type: "authorization_code",
460
- code: "oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as",
461
- code_verifier: your_code_verifier_here
462
- })
463
- response.raise_for_status
464
- payload = JSON.parse(response.to_s)
465
- puts payload #=> {"access_token" => ....
466
- ```
467
-
468
- By default, the pkce integration sets "S256" as the default challenge method. If you value security, you **should not use plain**. However, if you really need to, you can set it in the `rodauth` plugin:
469
-
470
- ```ruby
471
- plugin :rodauth do
472
- enable :oauth
473
- oauth_pkce_challenge_method "plain"
474
- end
475
- ```
476
-
477
- Although PKCE flow is supported out-of-the-box, it's not enforced by default. If you want to, you can force it, thereby forcing clients to generate a challenge:
478
-
479
- ```ruby
480
- plugin :rodauth do
481
- enable :oauth
482
- oauth_require_pkce true
483
- end
484
- ```
485
-
486
- If you want, on the other hand. to disable this flow altogether, you can:
487
-
488
- ```ruby
489
- enable :oauth
490
- use_oauth_pkce? false
491
- ```
492
-
493
- ### HTTP Mac Authentication
494
-
495
- You can enable HTTP MAC authentication like this:
496
-
497
- ```ruby
498
- plugin :rodauth do
499
- enable :oauth_http_mac
500
- end
501
- ```
502
-
503
- Generating an access token will deliver the following fields:
504
-
505
- ```ruby
506
- # with httpx
507
- require "httpx"
508
- response = httpx.post("https://auth_server/token",json: {
509
- client_id: env["oauth_client_id"],
510
- client_secret: env["oauth_client_secret"],
511
- grant_type: "authorization_code",
512
- code: "oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as"
513
- })
514
- response.raise_for_status
515
- payload = json.parse(response.to_s)
516
- puts payload #=> {
517
- # "access_token" => ....
518
- # "mac_key" => ....
519
- # "mac_algorithm" =>
520
- ```
521
-
522
- which you'll be able to use to generate the mac signature to send in the "Authorization" header.
523
-
524
- #### DB schema
525
-
526
- the "oauth_tokens" table will have to include a column for the mac key:
527
-
528
- ```ruby
529
- # in migration
530
- String :mac_key, token: true
531
- ```
532
-
533
-
534
- ### JWT Access Tokens
535
-
536
- JWT Acess Tokens are great to avoid DB lookups when validation the authorization token. Quoting the RFC, *The approach is particularly common in topologies where the authorization server and resource server are not co-located, are not run by the same entity, or are otherwise separated by some boundary.*
537
-
538
- You can enable JWT Access tokens by doing:
539
-
540
- ```ruby
541
- plugin :rodauth do
542
- enable :oauth_jwt
543
- end
544
- ```
545
-
546
- This will, by default, use the OAuth application as HMAC signature and "HS256" as the algorithm to sign the resulting JWT access tokens. You can tweak those features by editing the following options:
547
-
548
- ```ruby
549
- enable :oauth_jwt
550
- oauth_jwt_secret "SECRET"
551
- oauth_jwt_algorithm "HS512"
552
- ```
553
-
554
- You can look for other options in [the jwt gem documentation](https://github.com/jwt/ruby-jwt), as this is used under the hood.
555
-
556
- #### Pub/Priv key
557
-
558
- You can decide to keep a private key to encode the JWT token, while other clients hace the public key to decode it. You can then do it like:
559
-
560
- ```ruby
561
- rsa_private = OpenSSL::PKey::RSA.generate 2048
562
- rsa_public = rsa_private.public_key
563
-
564
- plugin :rodauth do
565
- enable :oauth_jwt
566
- oauth_jwt_key rsa_private
567
- oauth_jwt_public_key rsa_public
568
- oauth_jwt_algorithm "RS256"
569
- end
570
- ```
571
-
572
- #### JWK
573
-
574
- One can further encode the JWT token using JSON Web Keys. Here's how you could enable the feature:
575
-
576
- ```ruby
577
- rsa_private = OpenSSL::PKey::RSA.generate 2048
578
- rsa_public = rsa_private.public_key
579
-
580
- plugin :rodauth do
581
- enable :oauth_jwt
582
- oauth_jwt_jwk_key rsa_private
583
- oauth_jwt_jwk_public_key rsa_public
584
- oauth_jwt_jwk_algorithm "RS256"
585
- end
586
- ```
587
-
588
- #### JWE
589
-
590
- You can further instruct the jwt feature to encrypt the encoded token using JSON Web Encryption standard:
591
-
592
- ```ruby
593
- jwe_key = OpenSSL::PKey::RSA.new(2048)
594
-
595
- plugin :rodauth do
596
- oauth_jwt_secret "SECRET"
597
- oauth_jwt_algorithm "HS256"
598
- oauth_jwt_jwe_key jwe_key
599
- oauth_jwt_jwe_encryption_method "A192GCM"
600
- end
601
- ```
602
-
603
- which adds an extra layer of protection.
604
-
605
- #### JWKS URI
606
-
607
- A route is defined for getting the JWK Set in a JSON format; this is typically used by client applications, who need the JWK set to decode the JWT token. This URL is typically `https://oauth-server/jwks`.
608
-
609
- #### JWT Bearer as authorization grant
610
-
611
- One can emit a new access token by using the bearer access token as grant. This can be done emitting a request similar to this:
612
-
613
- ```ruby
614
- # with httpx
615
- require "httpx"
616
- response = httpx.post("https://auth_server/token",json: {
617
- grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
618
- assertion: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6IkV4YW1wbGUiLCJpYXQiOjE1OTIwMDk1MDEsImNsaWVudF9pZCI6IkNMSUVOVF9JRCIsImV4cCI6MTU5MjAxMzEwMSwiYXVkIjpudWxsLCJzY29wZSI6InVzZXIucmVhZCB1c2VyLndyaXRlIiwianRpIjoiOGM1NTVjMjdiOWRjNDdmOTcyNWRkYzBhMjk0NzA1ZTA4NzFkY2JlN2Q5ZTNlMmVkNGE1ZTBiOGZlNTZlYzcxMSJ9.AlxKRtE3ec0mtyBSDx4VseND4eC6cH5ubtv8gfYxxsc"
619
- })
620
- response.raise_for_status
621
- payload = json.parse(response.to_s)
622
- puts payload #=> {
623
- # "access_token" => "ey....
624
- ```
625
-
626
- #### DB Schema
627
-
628
- You'll still need the "oauth_tokens" table, however you can remove the "token" column.
629
-
630
270
  #### Internationalization (i18n)
631
271
 
632
272
  `rodauth-oauth` supports translating all user-facing text found in all pages and forms, by integrating with [rodauth-i18n](https://github.com/janko/rodauth-i18n). Just set it up in your application and `rodauth` configuration.
@@ -635,9 +275,6 @@ Default translations shipping with `rodauth-oauth` can be found [in this directo
635
275
 
636
276
  (This feature is available since `v0.7`.)
637
277
 
638
- #### Caveats
639
-
640
- Although very handy for the mentioned use case, one can't revoke a JWT token on demand (it must expire first).
641
278
 
642
279
  ## Ruby support policy
643
280
 
@@ -0,0 +1,3 @@
1
+ ## 0.0.1 (14/5/2020)
2
+
3
+ Initial implementation of the Oauth 2.0 framework, with an example app done using roda.
@@ -0,0 +1,15 @@
1
+ ## 0.0.2 (29/5/2020)
2
+
3
+ ### Features
4
+
5
+ * Implementation of PKCE by OAuth Public Clients (https://tools.ietf.org/html/rfc7636);
6
+ * Implementation of grants using "access_type" and "approval_prompt" ([similar to what Google OAuth 2.0 API does](https://wiki.scn.sap.com/wiki/display/Security/Access+Google+APIs+using+the+OAuth+2.0+Client+API));
7
+
8
+ ### Improvements
9
+
10
+ * Store token/refresh token hashes in the database, instead of the "plain" tokens;
11
+ * Client secret hashed by default, and provided by the application owner;
12
+
13
+ ### Fix
14
+
15
+ * usage of client secret for authorizing the generation of tokens, as the spec mandates (and refraining from them when doing PKCE).
@@ -0,0 +1,31 @@
1
+ ## 0.0.3 (5/6/2020)
2
+
3
+ ### Features
4
+
5
+ #### `:oauth_http_mac`
6
+
7
+ A new feature builds on top of `:oauth` to allow MAC authorization.
8
+
9
+ ```ruby
10
+ plugin :rodauth do
11
+ enable :oauth_http_mac
12
+ # options here...
13
+ end
14
+ ```
15
+
16
+ #### `:oauth_jwt`
17
+
18
+ Another new feature, this time supporting the generation of JWT access tokens.
19
+
20
+ ```ruby
21
+ plugin :rodauth do
22
+ enable :oauth_jwt
23
+ # options here...
24
+ end
25
+ ```
26
+
27
+ ### Improvements
28
+
29
+ * added options for disabling pkce and access type (respectively, `use_oauth_pkce?` and `use_oauth_access_type?`);
30
+ * renamed the existing `use_oauth_implicit_grant_type` to `use_oauth_implicit_grant_type?`;
31
+ * It's now usable as JSON API (small caveat: POST authorize will still redirect on success...);
@@ -0,0 +1,36 @@
1
+ ## 0.0.4 (13/6/2020)
2
+
3
+ ### Features
4
+
5
+ #### Token introspection
6
+
7
+ `rodauth-oauth` now ships with an introspection endpoint (`/oauth-introspect`).
8
+
9
+ #### Authorization Server Metadata
10
+
11
+ `rodauth-oauth` now allows to define an authorization metadata endpoint, which has to be defined at the route of the router:
12
+
13
+ ```ruby
14
+ route do |r|
15
+ r.rodauth
16
+ rodauth.oauth_server_metadata
17
+ ...
18
+ ```
19
+
20
+ #### JWKs URI
21
+
22
+ the `oauth_jwt` feature now ships with an endpoint, `/oauth-jwks`, where client applications can retrieve the JWK set to verify generated tokens.
23
+
24
+ #### JWT access tokens as authorization grants
25
+
26
+ The `oauth_jwt` feature now allows the usage of access tokens to authorize the generation of new tokens, [as per the RFC](https://tools.ietf.org/html/rfc7523#section-4);
27
+
28
+ ### Improvements
29
+
30
+ * using `client_secret_basic` authorization where client id/secret params were allowed (i.e. in the token and revoke endpoints, for example);
31
+ * improved JWK usage for both supported jwt libraries;
32
+ * marked `fetch_access_token` as auth_value_method, thereby allowing users to fetch the access token from other sources than the "Authorization" header (i.e. form body, query params, etc...)
33
+
34
+ ### Bugfixes
35
+
36
+ * Fixed scope claim of JWT ("scopes" -> "scope");
@@ -0,0 +1,36 @@
1
+ ### 0.0.5 (26/6/2020)
2
+
3
+ #### Features
4
+
5
+ * new option: `oauth_scope_separator` (default: `" "`), to define how scopes are stored;
6
+
7
+ ##### Resource Server mode
8
+
9
+ `rodauth-oauth` can now be used in a resource server, i.e. only for authorizing access to resources:
10
+
11
+
12
+ ```ruby
13
+ plugin :rodauth do
14
+ enable :oauth
15
+
16
+ is_authorization_server? false
17
+ authorization_server_url "https://auth-server"
18
+ end
19
+ ```
20
+
21
+ It **requires** the authorization to implement the server metadata endpoint (`/.well-known/oauth-authorization-server`), and if using JWS, the JWKs URI endpoint (unless `oauth_jwt_public_key` is defined).
22
+
23
+ #### Improvements
24
+
25
+ * Multiple Redirect URIs are now allowed for client applications out-of-the-box. In order to use it in API mode, you can pass the `redirect_uri` with an array of strings (the URLs) as values; in the new client application form, you can add several input fields with name field as `redirect_uri[]`. **ATTENTION!!** When using multiple redirect URIs, passing the desired redirect URI to the authorize form becomes mandatory.
26
+ * store scopes with whitespace instead of comma; set separator as `oauth_scope_separator` option, to keep backwards-compatibility;
27
+ * client application can now store multiple redirect uris; the POST API parameters can accept the redirect_uri param value both as a string or an array of string; internally, they'll be stored in a whitespace-separated string;
28
+
29
+ #### Bugfixes
30
+
31
+ * Fixed `RETURNING` support in the databases supporting it (such as postgres).
32
+
33
+ #### Chore
34
+
35
+ * option `scopes_param` renamed to `scope_param`;
36
+ *
@@ -0,0 +1,21 @@
1
+ ### 0.0.6 (6/7/2020)
2
+
3
+ #### Features
4
+
5
+ The `oauth_jwt` feature now supports JWT Secured Authorization Request (JAR) (see https://tools.ietf.org/html/draft-ietf-oauth-jwsreq-20). This means that client applications can send the authorization parameters inside a signed JWT. The client applications keeps the private key, while the authorization server **must** store a public key for the client application. For encrypted JWTs, the client application should use one of the public encryption keys exposed in the JWKs URI, to encrypt the JWT. Remember, **tokens must be signed then encrypted** (or just signed).
6
+
7
+ ###### Options:
8
+
9
+ * `:oauth_application_jws_jwk_column`: db column where the public key is stored; since it's stored in the JWS format, it can be stored either as a String (JSON-encoded), or as an hstore (if you're using postgresql);
10
+ * `:oauth_jwt_jwe_key`: key used to decrypt the request JWT;
11
+ * `:oauth_jwt_jwe_public_key`: key used to encrypt the request JWT, and which will be exposed in the JWKs URI in the JWK format;
12
+
13
+
14
+ #### Improvements
15
+
16
+ * Removing all `_param` options; these defined the URL params, however we're using protocol-defined params, so it's unlikely (and undesired) that these'll change.
17
+ * Hitting the revoke endpoint with a JWT access token returns a 400 error;
18
+
19
+ #### Chore
20
+
21
+ Removed React Javascript from example applications.
@@ -0,0 +1,44 @@
1
+ ### 0.1.0 (31/7/2020)
2
+
3
+ #### Features
4
+
5
+ ##### OpenID
6
+
7
+ `rodauth-oauth` now ships with support for [OpenID Connect](https://openid.net/connect/). In order to enable, you have to:
8
+
9
+ ```ruby
10
+ plugin :rodauth do
11
+ enable :oidc
12
+ end
13
+ ```
14
+
15
+ For more info about integrating it, [check the wiki](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/wikis/home#openid-connect-since-v01).
16
+
17
+ It supports omniauth openID integrations out-of-the-box, [check the OpenID example, which integrates with omniauth_openid_connect](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/tree/master/examples).
18
+
19
+ #### Improvements
20
+
21
+ * JWT: `sub` claim now also handles "pairwise" subjects. For that, you have to set the `oauth_jwt_subject_type` option (`"public"` or `"pairwise"`) and `oauth_jwt_subject_secret` (will be used for salting the `sub` when the type is `"pairwise"`).
22
+ * JWT: `auth_time` claim is now supported; if your application uses the `rodauth` feature `:account_expiration`, it'll use the `last_account_login_at` method, otherwise you can set the `last_account_login_at` option:
23
+
24
+ ```ruby
25
+ last_account_login_at do
26
+ convert_timestamp(db[accounts_table].where(account_id_column => account_id).get(:that_column_where_you_keep_the_data))
27
+ end
28
+ ```
29
+ * JWT: `iss` claim now defaults to `authorization_server_url` when not defined;
30
+ * JWT: `aud` claim now defaults to the token application's client ID (`client_id` claim was removed as a result);
31
+
32
+
33
+
34
+ #### Breaking Changes
35
+
36
+ `rodauth-oauth` URLs no longer have the `oauth-` prefix, so make sure you update your integrations accordingly, i.e. where you used to rely on `/oauth-authorize`, you'll have to use `/authorize`.
37
+
38
+ URI schemes for client applications redirect URIs have to be `https`. In order to override this, set the `oauth_valid_uri_schemes` to an array of your expected URI schemes.
39
+
40
+
41
+ #### Bugfixes
42
+
43
+ * Authorization request submission can receive the `scope` as an array of values now, instead of only dealing with receiving a white-space separated list.
44
+ * fixed trailing "/" in the "issuer" value in server metadata (`https://server.com/` -> `https://server.com`).