rodauth-oauth 1.0.0.pre.beta2 → 1.0.0
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/CHANGELOG.md +1 -1
- data/MIGRATION-GUIDE-v1.md +12 -0
- data/README.md +24 -10
- data/doc/release_notes/0_1_0.md +2 -2
- data/doc/release_notes/0_2_0.md +1 -1
- data/doc/release_notes/0_3_0.md +1 -1
- data/doc/release_notes/0_5_0.md +2 -2
- data/doc/release_notes/0_8_0.md +2 -2
- data/doc/release_notes/1_0_0.md +79 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize_error.erb +10 -0
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +0 -1
- data/lib/rodauth/features/oauth_authorize_base.rb +26 -14
- data/lib/rodauth/features/oauth_base.rb +1 -0
- data/lib/rodauth/features/oauth_implicit_grant.rb +10 -0
- data/lib/rodauth/features/oidc.rb +37 -9
- data/lib/rodauth/features/oidc_rp_initiated_logout.rb +29 -26
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +2 -1
- data/locales/pt.yml +2 -1
- data/templates/authorize_error.str +12 -0
- metadata +14 -14
- data/doc/release_notes/1_0_0_beta1.md +0 -38
- data/doc/release_notes/1_0_0_beta2.md +0 -34
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8288f66a0f7dd5400b60d2508a0247aefd37f1aa73322c19bf3b744d3e8b1ace
|
|
4
|
+
data.tar.gz: 6f2c333c4c2c3a4f92544f939ac1112d31c78a56779f22234bb7e7ce95105931
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 612b2651b4c29f98427a5113b403ce214d3d8513cd740977a834f5efdbb4aac46fc83f74f54a2b925836e6860cf008226232956ee5dc975f08cd88215aa198f2
|
|
7
|
+
data.tar.gz: 2940dd71610ea52f3c18ea942f0a2fd4122028f9c1c0bce5986506501c24c5603d981406e997b662e68986411244db442747e453ff65a49431551d56d8da0eac
|
data/CHANGELOG.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
See the Release Notes under https://gitlab.com/
|
|
1
|
+
See the Release Notes under https://gitlab.com/os85/rodauth-oauth/-/tree/master/doc/release_notes
|
data/MIGRATION-GUIDE-v1.md
CHANGED
|
@@ -90,6 +90,10 @@ The client secret is hashed (with bcrypt) before being stored, by default. While
|
|
|
90
90
|
oauth_applications_client_secret_hash_column nil
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
## oauth applications: oauth_applications_homepage_url_column no longer required
|
|
94
|
+
|
|
95
|
+
The homepage url is no longer considered a require prooperty of an OAuth client application.
|
|
96
|
+
|
|
93
97
|
## oauth grants: access token and refresh token hashed by default
|
|
94
98
|
|
|
95
99
|
access token and refresh token columns are now hashed by default, and point to the same column as the main counterpart:
|
|
@@ -205,6 +209,14 @@ JWKs URI endpoint has been moved to its plugin. If you require this functionalit
|
|
|
205
209
|
enable :oauth_jwt, :oauth_jwt_jwks
|
|
206
210
|
```
|
|
207
211
|
|
|
212
|
+
## OIDC RP-initiated logout segregated in its plugin
|
|
213
|
+
|
|
214
|
+
It was previously being loaded in the `:oidc` plugin by default. If you require this funtionality, enable the plugin:
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
enable :oidc_rp_initiated_logout
|
|
218
|
+
```
|
|
219
|
+
|
|
208
220
|
## routing functions renamed
|
|
209
221
|
|
|
210
222
|
Previously, loading well-known routes, the oauth server metadata, or oauth application/tokens (now grants) management dashboard implied calling a function on roda to load those routes. These have been renamed:
|
data/README.md
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
# Rodauth::Oauth
|
|
2
2
|
|
|
3
3
|
[](http://rubygems.org/gems/rodauth-oauth)
|
|
4
|
-
[](https://gitlab.com/os85/rodauth-oauth/pipelines?page=1&scope=all&ref=master)
|
|
5
|
+
[](https://os85.gitlab.io/rodauth-oauth/coverage/#_AllFiles)
|
|
6
6
|
|
|
7
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.
|
|
8
8
|
|
|
9
|
+
## Certification
|
|
10
|
+
[<img width="184" height="96" align="right" src="/openid-certified.jpg" alt="OpenID Certification">](https://openid.net/certification/)
|
|
11
|
+
|
|
12
|
+
`rodauth-oauth` is [certified](https://openid.net/certification/) for the following profiles of the OpenID Connect™ protocol:
|
|
13
|
+
|
|
14
|
+
* Basic OP
|
|
15
|
+
* Implicit OP
|
|
16
|
+
* Hybrid OP
|
|
17
|
+
* Config OP
|
|
18
|
+
* Dynamic OP
|
|
19
|
+
* Form Post OP
|
|
20
|
+
|
|
21
|
+
(it also passes the conformance tests for the RP-Initiated Logout OP).
|
|
22
|
+
|
|
9
23
|
## Features
|
|
10
24
|
|
|
11
25
|
This gem implements the following RFCs and features of OAuth:
|
|
@@ -65,10 +79,10 @@ Or install it yourself as:
|
|
|
65
79
|
## Resources
|
|
66
80
|
| | |
|
|
67
81
|
| ------------- | ----------------------------------------------------------- |
|
|
68
|
-
| Website | https://
|
|
69
|
-
| Documentation | https://
|
|
70
|
-
| Wiki | https://gitlab.com/
|
|
71
|
-
| CI | https://gitlab.com/
|
|
82
|
+
| Website | https://os85.gitlab.io/rodauth-oauth/ |
|
|
83
|
+
| Documentation | https://os85.gitlab.io/rodauth-oauth/rdoc/ |
|
|
84
|
+
| Wiki | https://gitlab.com/os85/rodauth-oauth/wikis/home |
|
|
85
|
+
| CI | https://gitlab.com/os85/rodauth-oauth/pipelines |
|
|
72
86
|
|
|
73
87
|
## Articles
|
|
74
88
|
|
|
@@ -132,12 +146,12 @@ end
|
|
|
132
146
|
|
|
133
147
|
### Example (TL;DR)
|
|
134
148
|
|
|
135
|
-
Just [check our example applications](https://gitlab.com/
|
|
149
|
+
Just [check our example applications](https://gitlab.com/os85/rodauth-oauth/-/tree/master/examples/).
|
|
136
150
|
|
|
137
151
|
|
|
138
152
|
### Database migrations
|
|
139
153
|
|
|
140
|
-
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/
|
|
154
|
+
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/os85/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)).
|
|
141
155
|
|
|
142
156
|
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:
|
|
143
157
|
|
|
@@ -270,7 +284,7 @@ end
|
|
|
270
284
|
|
|
271
285
|
`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.
|
|
272
286
|
|
|
273
|
-
Default translations shipping with `rodauth-oauth` can be found [in this directory](https://gitlab.com/
|
|
287
|
+
Default translations shipping with `rodauth-oauth` can be found [in this directory](https://gitlab.com/os85/rodauth-oauth/-/tree/master/locales). If they're not available for the languages you'd like to support, consider getting them translated from the english text, and contributing them to this repository via a Merge Request.
|
|
274
288
|
|
|
275
289
|
(This feature is available since `v0.7`.)
|
|
276
290
|
|
|
@@ -289,4 +303,4 @@ After checking out the repo, run `bundle install` to install dependencies. Then,
|
|
|
289
303
|
|
|
290
304
|
## Contributing
|
|
291
305
|
|
|
292
|
-
Bug reports and pull requests are welcome on Gitlab at https://gitlab.com/
|
|
306
|
+
Bug reports and pull requests are welcome on Gitlab at https://gitlab.com/os85/rodauth-oauth.
|
data/doc/release_notes/0_1_0.md
CHANGED
|
@@ -12,9 +12,9 @@ plugin :rodauth do
|
|
|
12
12
|
end
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
For more info about integrating it, [check the wiki](https://gitlab.com/
|
|
15
|
+
For more info about integrating it, [check the wiki](https://gitlab.com/os85/rodauth-oauth/-/wikis/home#openid-connect-since-v01).
|
|
16
16
|
|
|
17
|
-
It supports omniauth openID integrations out-of-the-box, [check the OpenID example, which integrates with omniauth_openid_connect](https://gitlab.com/
|
|
17
|
+
It supports omniauth openID integrations out-of-the-box, [check the OpenID example, which integrates with omniauth_openid_connect](https://gitlab.com/os85/rodauth-oauth/-/tree/master/examples).
|
|
18
18
|
|
|
19
19
|
#### Improvements
|
|
20
20
|
|
data/doc/release_notes/0_2_0.md
CHANGED
|
@@ -12,7 +12,7 @@ plugin :rodauth do
|
|
|
12
12
|
end
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
For more info about integrating it, [check the wiki](https://gitlab.com/
|
|
15
|
+
For more info about integrating it, [check the wiki](https://gitlab.com/os85/rodauth-oauth/-/wikis/SAML-Assertion-Access-Tokens).
|
|
16
16
|
|
|
17
17
|
##### Supporting rotating keys
|
|
18
18
|
|
data/doc/release_notes/0_3_0.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
#### Improvements
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
* Support for the OIDC authorize [`prompt` parameter](https://openid.net/specs/openid-connect-core-1_0.html) (sectionn 3.1.2.1). It supports the `none`, `login` and `consent` out-of-the-box, while providing support for `select-account` when paired with [rodauth-select-account, a rodauth feature to handle multiple accounts in the same session](https://gitlab.com/
|
|
10
|
+
* Support for the OIDC authorize [`prompt` parameter](https://openid.net/specs/openid-connect-core-1_0.html) (sectionn 3.1.2.1). It supports the `none`, `login` and `consent` out-of-the-box, while providing support for `select-account` when paired with [rodauth-select-account, a rodauth feature to handle multiple accounts in the same session](https://gitlab.com/os85/rodauth-select-account).
|
|
11
11
|
|
|
12
12
|
* Refresh Tokens are now expirable. The refresh token expiration period is governed by the `oauth_refresh_token_expires_in` option (default: 1 year), and is the period for which a refresh token can be used after its respective access token expired.
|
|
13
13
|
|
data/doc/release_notes/0_5_0.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
#### RP-Initiated Logout
|
|
4
4
|
|
|
5
|
-
The `:oidc` plugin can now do [RP-Initiated Logout](https://gitlab.com/
|
|
5
|
+
The `:oidc` plugin can now do [RP-Initiated Logout](https://gitlab.com/os85/rodauth-oauth/-/wikis/RP-Initiated-Logout). It's disabled by default, so read the docs to learn how to enable it.
|
|
6
6
|
|
|
7
7
|
#### Security
|
|
8
8
|
|
|
9
9
|
The `:oauth_jwt` (and by association, `:oidc`) plugin(s) verifies the claims of used JWT tokens. This is a **very important security fix**, as without it, there is no protection against replay attacks and other types of misuse of the JWT token.
|
|
10
10
|
|
|
11
|
-
A new auth method, `generate_jti(claims)`, was [added to the list of oauth_jwt plugin options](https://gitlab.com/
|
|
11
|
+
A new auth method, `generate_jti(claims)`, was [added to the list of oauth_jwt plugin options](https://gitlab.com/os85/rodauth-oauth/-/wikis/JWT-Access-Tokens#rodauth-options). By default, it'll hash the `aud` and `iat` claims together, but you can overwrite how this is done.
|
data/doc/release_notes/0_8_0.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
* Device code grant
|
|
6
6
|
|
|
7
|
-
`rodauth-oauth` now supports the [Device code grant RFC](https://gitlab.com/
|
|
7
|
+
`rodauth-oauth` now supports the [Device code grant RFC](https://gitlab.com/os85/rodauth-oauth/-/wikis/Device-Grant), via the `oauth_device_grant` feature.
|
|
8
8
|
|
|
9
9
|
* OAuth Tokens Management
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ An OAuth Tokens Management Dashboard is now provided (via `r.oauth_tokens` call
|
|
|
12
12
|
|
|
13
13
|
* Assertion Framework (+ SAML and JWT Bearer Grant)
|
|
14
14
|
|
|
15
|
-
A new plugin, `oauth_assertion_base`, was introduced to provide a baseline for implementing custom Bearer Assertion as per the [OAuth Client Assertion Framework RFC](https://gitlab.com/
|
|
15
|
+
A new plugin, `oauth_assertion_base`, was introduced to provide a baseline for implementing custom Bearer Assertion as per the [OAuth Client Assertion Framework RFC](https://gitlab.com/os85/rodauth-oauth/-/wikis/Client-Assertion-Framework). This in turn was used to refactor and reintroduce the [oauth_saml_bearer_grant](https://gitlab.com/os85/rodauth-oauth/-/wikis/SAML-Bearer-Assertions) and the [oauth_jwt_bearer_grant](https://gitlab.com/os85/rodauth-oauth/-/wikis/JWT-Bearer-Assertions) features, which implement the respective and most recent version of the assertion RFCs.
|
|
16
16
|
|
|
17
17
|
(as a result, `oauth_saml` was removed, which implemented a very old draft version of the SAML Bearer spec).
|
|
18
18
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
## 1.0.0 (15/12/2022)
|
|
2
|
+
|
|
3
|
+
## Highlights
|
|
4
|
+
|
|
5
|
+
rodauth-oauth is now [OpenID certified](https://openid.net/certification/) for the following certification profiles:
|
|
6
|
+
|
|
7
|
+
* Basic OP
|
|
8
|
+
* Implicit OP
|
|
9
|
+
* Hybrid OP
|
|
10
|
+
* Config OP
|
|
11
|
+
* Dynamic OP
|
|
12
|
+
* Form Post OP
|
|
13
|
+
|
|
14
|
+
and passes the conformance tests for RP-Initiated Logout OP.
|
|
15
|
+
|
|
16
|
+
The OIDC server used to run the test can be found [here](https://gitlab.com/os85/rodauth-oauth/-/blob/master/examples/oidc/authentication_server.rb) and deployed [here](https://rodauth-oauth-oidc.onrender.com).
|
|
17
|
+
|
|
18
|
+
### Breaking changes
|
|
19
|
+
|
|
20
|
+
The full description of breaking changes, and suggestions on how to make the migration smoother, can be found in the [migration guide](https://gitlab.com/os85/rodauth-oauth/-/blob/6465b8522a78cf0037a55d3d4b81f68f7811be68/MIGRATION-GUIDE-v1.md).
|
|
21
|
+
|
|
22
|
+
A short list of the main highlights:
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
* Ruby 2.5 or higher is required.
|
|
26
|
+
* `oauth_http_mac` feature removed.
|
|
27
|
+
* `oauth_tokens` table (and resource) were removed (only `oauth_applications` and `oauth_grants`, access and refresh tokens are now properties of the latter).
|
|
28
|
+
* access and refresh tokens hashed by default when stored in the database.
|
|
29
|
+
* default oauth response mode is `"form_post"`.
|
|
30
|
+
* oauth specific features require explicit enablement of respective features (no more `enable :oauth`)
|
|
31
|
+
* refresh token policy is "rotation" by default
|
|
32
|
+
* homepage url is no longer a client application required property.
|
|
33
|
+
* OIDC RP-initiated logout extracted into `oidc_rp_initiated_logout` feature.
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
|
|
37
|
+
The following helpers are exposed in the `rodauth` object:
|
|
38
|
+
|
|
39
|
+
* `current_oauth_account` - returns the dataset row for the `rodauth` account associated to an oauth access token in the "authorization" header.
|
|
40
|
+
* `current_oauth_application` - returns the dataset row for the oauth application associated to an oauth access token in the "authorization" header.
|
|
41
|
+
|
|
42
|
+
When used in `rails` via `rodauth-rails`, both are exposed directly as controller helpers.
|
|
43
|
+
|
|
44
|
+
#### `oauth_resource_server` plugin
|
|
45
|
+
|
|
46
|
+
This plugin can be used as a convenience when configuring resource servers.
|
|
47
|
+
|
|
48
|
+
#### JAR support for request_uri query param
|
|
49
|
+
|
|
50
|
+
The `oauth_jwt_secured_authorization_request` plugin now supports a `request_uri` query param as well.
|
|
51
|
+
|
|
52
|
+
#### OIDC features
|
|
53
|
+
|
|
54
|
+
* The `oidc` plugin supports [essential claims](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter), via the `claims` authorization request query parameter.
|
|
55
|
+
* id token built with `"c_hash"` and `"at_hash"` claims when they should.
|
|
56
|
+
|
|
57
|
+
### Improvements
|
|
58
|
+
|
|
59
|
+
* `:oauth_introspect` plugin: OAuth introspection endpoint exposes the token's `"username"` claim.
|
|
60
|
+
* endpoint client authentication supports "client credentials grant" access tokens.
|
|
61
|
+
* `acr_values_supported` exposed in the openid configuration.
|
|
62
|
+
* `oauth_request_object_signing_alg_allow_none` enables `"none"` as an accepted request object signing alg when `true` (`false` by default).
|
|
63
|
+
* OIDC `offline_access` supported.
|
|
64
|
+
|
|
65
|
+
### Bugfixes
|
|
66
|
+
|
|
67
|
+
* fixed `oidc` calculation of `"auth_time"` claim.
|
|
68
|
+
* JWT: "sub" is now always a string.
|
|
69
|
+
* `response_type` is now an authorization request required parameter (as per the RFC).
|
|
70
|
+
* `state` is now passed along when redirecting from authorization requests with `error`;
|
|
71
|
+
* access token can now be read from POST body or GET query params (as per the RFC).
|
|
72
|
+
* id token no longer shipping with claims with `null` value;
|
|
73
|
+
* id token no longer encoding claims by default (only when `response_type=id_token`, as per the RFC).
|
|
74
|
+
* support "JWT without kid" when doing jwt decoding for JWT tokens not generated in the provider (such as request objects).
|
|
75
|
+
* Set `iss` and `aud` claims in the Userinfo JWT response.
|
|
76
|
+
* Make sure errors are also delivered via form POST, when `response_mode=form_post`.
|
|
77
|
+
* Authorization request now shows an error page when `response_type` or `client_id` are missing, or `redirect_uri` is missing or invalid; a new `"authorize_error"` template is invoked in such cases.
|
|
78
|
+
* oidc: nonce present in id token when using the "id_token token" response type.
|
|
79
|
+
* error parameter delivered in URL fragment when failing an implicit grant autorization request.
|
|
@@ -120,7 +120,6 @@ module Rodauth
|
|
|
120
120
|
return super unless supported_grant_type?(grant_type, "authorization_code")
|
|
121
121
|
|
|
122
122
|
grant_params = {
|
|
123
|
-
oauth_grants_type_column => grant_type,
|
|
124
123
|
oauth_grants_code_column => param("code"),
|
|
125
124
|
oauth_grants_redirect_uri_column => param("redirect_uri"),
|
|
126
125
|
oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column]
|
|
@@ -10,6 +10,7 @@ module Rodauth
|
|
|
10
10
|
after "authorize"
|
|
11
11
|
|
|
12
12
|
view "authorize", "Authorize", "authorize"
|
|
13
|
+
view "authorize_error", "Authorize Error", "authorize_error"
|
|
13
14
|
|
|
14
15
|
button "Authorize", "oauth_authorize"
|
|
15
16
|
button "Back to Client Application", "oauth_authorize_post"
|
|
@@ -24,7 +25,7 @@ module Rodauth
|
|
|
24
25
|
translatable_method :oauth_applications_tos_uri_label, "Terms of service URL"
|
|
25
26
|
translatable_method :oauth_applications_policy_uri_label, "Policy URL"
|
|
26
27
|
translatable_method :oauth_unsupported_response_type_message, "Unsupported response type"
|
|
27
|
-
translatable_method :oauth_authorize_parameter_required, "'%<parameter>s'
|
|
28
|
+
translatable_method :oauth_authorize_parameter_required, "Invalid or missing '%<parameter>s'"
|
|
28
29
|
|
|
29
30
|
# /authorize
|
|
30
31
|
auth_server_route(:authorize) do |r|
|
|
@@ -65,7 +66,15 @@ module Rodauth
|
|
|
65
66
|
private
|
|
66
67
|
|
|
67
68
|
def validate_authorize_params
|
|
68
|
-
|
|
69
|
+
redirect_authorize_error("client_id") unless oauth_application
|
|
70
|
+
|
|
71
|
+
redirect_uris = oauth_application[oauth_applications_redirect_uri_column].split(" ")
|
|
72
|
+
|
|
73
|
+
if (redirect_uri = param_or_nil("redirect_uri"))
|
|
74
|
+
redirect_authorize_error("redirect_uri") unless redirect_uris.include?(redirect_uri)
|
|
75
|
+
elsif redirect_uris.size > 1
|
|
76
|
+
redirect_authorize_error("redirect_uri")
|
|
77
|
+
end
|
|
69
78
|
|
|
70
79
|
redirect_response_error("unsupported_response_type") unless check_valid_response_type?
|
|
71
80
|
|
|
@@ -80,18 +89,6 @@ module Rodauth
|
|
|
80
89
|
false
|
|
81
90
|
end
|
|
82
91
|
|
|
83
|
-
def check_valid_redirect_uri?
|
|
84
|
-
application_redirect_uris = oauth_application[oauth_applications_redirect_uri_column].split(" ")
|
|
85
|
-
|
|
86
|
-
if (redirect_uri = param_or_nil("redirect_uri"))
|
|
87
|
-
application_redirect_uris.include?(redirect_uri)
|
|
88
|
-
else
|
|
89
|
-
set_error_flash(oauth_authorize_parameter_required(parameter: "redirect_uri")) if application_redirect_uris.size > 1
|
|
90
|
-
|
|
91
|
-
true
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
92
|
ACCESS_TYPES = %w[offline online].freeze
|
|
96
93
|
|
|
97
94
|
def check_valid_access_type?
|
|
@@ -127,6 +124,21 @@ module Rodauth
|
|
|
127
124
|
request.env["REQUEST_METHOD"] = "POST"
|
|
128
125
|
end
|
|
129
126
|
|
|
127
|
+
def redirect_authorize_error(parameter, referer = request.referer || default_redirect)
|
|
128
|
+
error_message = oauth_authorize_parameter_required(parameter: parameter)
|
|
129
|
+
|
|
130
|
+
if accepts_json?
|
|
131
|
+
status_code = oauth_invalid_response_status
|
|
132
|
+
|
|
133
|
+
throw_json_response_error(status_code, "invalid_request", error_message)
|
|
134
|
+
else
|
|
135
|
+
scope.instance_variable_set(:@error, error_message)
|
|
136
|
+
scope.instance_variable_set(:@back_url, referer)
|
|
137
|
+
|
|
138
|
+
return_response(authorize_error_view)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
130
142
|
def authorization_required
|
|
131
143
|
if accepts_json?
|
|
132
144
|
throw_json_response_error(oauth_authorization_required_error_status, "invalid_client")
|
|
@@ -48,6 +48,16 @@ module Rodauth
|
|
|
48
48
|
generate_token(grant_params, false)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
def _redirect_response_error(redirect_url, query_params)
|
|
52
|
+
response_types = param("response_type").split(/ +/)
|
|
53
|
+
|
|
54
|
+
return super if response_types.empty? || response_types == %w[code]
|
|
55
|
+
|
|
56
|
+
query_params = query_params.map { |k, v| "#{k}=#{v}" }
|
|
57
|
+
redirect_url.fragment = query_params.join("&")
|
|
58
|
+
redirect(redirect_url.to_s)
|
|
59
|
+
end
|
|
60
|
+
|
|
51
61
|
def authorize_response(params, mode)
|
|
52
62
|
return super unless mode == "fragment"
|
|
53
63
|
|
|
@@ -292,6 +292,11 @@ module Rodauth
|
|
|
292
292
|
end
|
|
293
293
|
|
|
294
294
|
super
|
|
295
|
+
|
|
296
|
+
return unless (response_type = param_or_nil("response_type"))
|
|
297
|
+
return unless response_type.include?("id_token")
|
|
298
|
+
|
|
299
|
+
redirect_response_error("invalid_request") unless param_or_nil("nonce")
|
|
295
300
|
end
|
|
296
301
|
|
|
297
302
|
def require_authorizable_account
|
|
@@ -440,10 +445,7 @@ module Rodauth
|
|
|
440
445
|
_generate_access_token(oauth_grant)
|
|
441
446
|
end
|
|
442
447
|
|
|
443
|
-
|
|
444
|
-
"code" => authorization_code,
|
|
445
|
-
**json_access_token_payload(oauth_grants_token_column => access_token)
|
|
446
|
-
}
|
|
448
|
+
json_access_token_payload(oauth_grants_token_column => access_token).merge("code" => authorization_code)
|
|
447
449
|
end
|
|
448
450
|
|
|
449
451
|
def create_token(*)
|
|
@@ -457,6 +459,9 @@ module Rodauth
|
|
|
457
459
|
|
|
458
460
|
return unless oauth_scopes.include?("openid")
|
|
459
461
|
|
|
462
|
+
signing_algorithm = oauth_application[oauth_applications_id_token_signed_response_alg_column] ||
|
|
463
|
+
oauth_jwt_keys.keys.first
|
|
464
|
+
|
|
460
465
|
id_token_claims = jwt_claims(oauth_grant)
|
|
461
466
|
|
|
462
467
|
id_token_claims[:nonce] = oauth_grant[oauth_grants_nonce_column] if oauth_grant[oauth_grants_nonce_column]
|
|
@@ -466,6 +471,16 @@ module Rodauth
|
|
|
466
471
|
# Time when the End-User authentication occurred.
|
|
467
472
|
id_token_claims[:auth_time] = get_oidc_account_last_login_at(oauth_grant[oauth_grants_account_id_column]).to_i
|
|
468
473
|
|
|
474
|
+
# Access Token hash value.
|
|
475
|
+
if (access_token = oauth_grant[oauth_grants_token_column])
|
|
476
|
+
id_token_claims[:at_hash] = id_token_hash(access_token, signing_algorithm)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# code hash value.
|
|
480
|
+
if (code = oauth_grant[oauth_grants_code_column])
|
|
481
|
+
id_token_claims[:c_hash] = id_token_hash(code, signing_algorithm)
|
|
482
|
+
end
|
|
483
|
+
|
|
469
484
|
account = db[accounts_table].where(account_id_column => oauth_grant[oauth_grants_account_id_column]).first
|
|
470
485
|
|
|
471
486
|
# this should never happen!
|
|
@@ -488,10 +503,7 @@ module Rodauth
|
|
|
488
503
|
|
|
489
504
|
params = {
|
|
490
505
|
jwks: oauth_application_jwks(oauth_application),
|
|
491
|
-
signing_algorithm:
|
|
492
|
-
oauth_application[oauth_applications_id_token_signed_response_alg_column] ||
|
|
493
|
-
oauth_jwt_keys.keys.first
|
|
494
|
-
),
|
|
506
|
+
signing_algorithm: signing_algorithm,
|
|
495
507
|
encryption_algorithm: oauth_application[oauth_applications_id_token_encrypted_response_alg_column],
|
|
496
508
|
encryption_method: oauth_application[oauth_applications_id_token_encrypted_response_enc_column]
|
|
497
509
|
}.compact
|
|
@@ -655,7 +667,8 @@ module Rodauth
|
|
|
655
667
|
when "id_token token"
|
|
656
668
|
redirect_response_error("invalid_request") unless supports_token_response_type?
|
|
657
669
|
|
|
658
|
-
|
|
670
|
+
grant_params = oidc_grant_params.merge(oauth_grants_type_column => "hybrid")
|
|
671
|
+
oauth_grant = _do_authorize_token(grant_params)
|
|
659
672
|
generate_id_token(oauth_grant)
|
|
660
673
|
|
|
661
674
|
response_params.replace(json_access_token_payload(oauth_grant))
|
|
@@ -664,6 +677,7 @@ module Rodauth
|
|
|
664
677
|
|
|
665
678
|
params = create_oauth_grant_with_token
|
|
666
679
|
oauth_grant = valid_oauth_grant_ds.where(oauth_grants_code_column => params["code"]).first
|
|
680
|
+
oauth_grant[oauth_grants_token_column] = params["access_token"]
|
|
667
681
|
generate_id_token(oauth_grant)
|
|
668
682
|
|
|
669
683
|
response_params.replace(params.merge("id_token" => oauth_grant[:id_token]))
|
|
@@ -785,5 +799,19 @@ module Rodauth
|
|
|
785
799
|
end
|
|
786
800
|
return_response(jwt)
|
|
787
801
|
end
|
|
802
|
+
|
|
803
|
+
def id_token_hash(hash, algo)
|
|
804
|
+
digest = case algo
|
|
805
|
+
when /256/ then Digest::SHA256
|
|
806
|
+
when /384/ then Digest::SHA384
|
|
807
|
+
when /512/ then Digest::SHA512
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
return unless digest
|
|
811
|
+
|
|
812
|
+
hash = digest.digest(hash)
|
|
813
|
+
hash = hash[0...hash.size / 2]
|
|
814
|
+
Base64.urlsafe_encode64(hash).tr("=", "")
|
|
815
|
+
end
|
|
788
816
|
end
|
|
789
817
|
end
|
|
@@ -19,31 +19,35 @@ module Rodauth
|
|
|
19
19
|
catch_error do
|
|
20
20
|
validate_oidc_logout_params
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
22
|
+
oauth_application = nil
|
|
23
|
+
|
|
24
|
+
if (id_token_hint = param_or_nil("id_token_hint"))
|
|
25
|
+
#
|
|
26
|
+
# why this is done:
|
|
27
|
+
#
|
|
28
|
+
# we need to decode the id token in order to get the application, because, if the
|
|
29
|
+
# signing key is application-specific, we don't know how to verify the signature
|
|
30
|
+
# beforehand. Hence, we have to do it twice: decode-and-do-not-verify, initialize
|
|
31
|
+
# the @oauth_application, and then decode-and-verify.
|
|
32
|
+
#
|
|
33
|
+
claims = jwt_decode(id_token_hint, verify_claims: false)
|
|
34
|
+
|
|
35
|
+
redirect_logout_with_error(oauth_invalid_client_message) unless claims
|
|
36
|
+
|
|
37
|
+
oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["aud"]).first
|
|
38
|
+
oauth_grant = db[oauth_grants_table]
|
|
39
|
+
.where(
|
|
40
|
+
oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
|
|
41
|
+
oauth_grants_account_id_column => account_id
|
|
42
|
+
).first
|
|
43
|
+
|
|
44
|
+
# check whether ID token belongs to currently logged-in user
|
|
45
|
+
redirect_logout_with_error(oauth_invalid_client_message) unless oauth_grant && claims["sub"] == jwt_subject(oauth_grant,
|
|
46
|
+
oauth_application)
|
|
47
|
+
|
|
48
|
+
# When an id_token_hint parameter is present, the OP MUST validate that it was the issuer of the ID Token.
|
|
49
|
+
redirect_logout_with_error(oauth_invalid_client_message) unless claims && claims["iss"] == oauth_jwt_issuer
|
|
50
|
+
end
|
|
47
51
|
|
|
48
52
|
# now let's logout from IdP
|
|
49
53
|
transaction do
|
|
@@ -92,7 +96,6 @@ module Rodauth
|
|
|
92
96
|
# Logout
|
|
93
97
|
|
|
94
98
|
def validate_oidc_logout_params
|
|
95
|
-
redirect_logout_with_error(oauth_invalid_client_message) unless param_or_nil("id_token_hint")
|
|
96
99
|
# check if valid token hint type
|
|
97
100
|
return unless (redirect_uri = param_or_nil("post_logout_redirect_uri"))
|
|
98
101
|
|
data/locales/en.yml
CHANGED
|
@@ -9,6 +9,7 @@ en:
|
|
|
9
9
|
user_code_not_found_error_flash: "No device to authorize with the given user code"
|
|
10
10
|
authorize_page_title: "Authorize"
|
|
11
11
|
authorize_page_lead: "The application %{name} would like to access your data."
|
|
12
|
+
authorize_error_page_title: "Authorize Error"
|
|
12
13
|
oauth_cancel_button: "Cancel"
|
|
13
14
|
oauth_applications_page_title: "Oauth Applications"
|
|
14
15
|
oauth_application_page_title: "Oauth Application"
|
|
@@ -65,5 +66,5 @@ en:
|
|
|
65
66
|
oauth_unsupported_transform_algorithm_message: "transform algorithm not supported"
|
|
66
67
|
oauth_invalid_request_object_message: "request object is invalid"
|
|
67
68
|
oauth_invalid_scope_message: "The Access Token expired"
|
|
68
|
-
oauth_authorize_parameter_required: "'%{parameter}'
|
|
69
|
+
oauth_authorize_parameter_required: "Invalid or missing '%{parameter}'"
|
|
69
70
|
oauth_invalid_post_logout_redirect_uri_message: "Invalid post logout redirect URI"
|
data/locales/pt.yml
CHANGED
|
@@ -9,6 +9,7 @@ pt:
|
|
|
9
9
|
user_code_not_found_error_flash: "Não existe nenhum dispositivo a ser autorizado com o código de usuário inserido"
|
|
10
10
|
authorize_page_title: "Autorizar"
|
|
11
11
|
authorize_page_lead: "O aplicativo %{name} gostaria de aceder aos seus dados."
|
|
12
|
+
authorize_error_page_title: Erro de autorização
|
|
12
13
|
oauth_cancel_button: "Cancelar"
|
|
13
14
|
oauth_applications_page_title: "Aplicativos OAuth"
|
|
14
15
|
oauth_application_page_title: "Aplicativo Oauth"
|
|
@@ -65,5 +66,5 @@ pt:
|
|
|
65
66
|
oauth_unsupported_transform_algorithm_message: "algoritmo de transformação não suportado"
|
|
66
67
|
oauth_invalid_request_object_message: "request_object é inválido"
|
|
67
68
|
oauth_invalid_scope_message: "O Token de acesso expirou"
|
|
68
|
-
oauth_authorize_parameter_required: "'%{parameter}'
|
|
69
|
+
oauth_authorize_parameter_required: "'%{parameter}' inválido ou em falta"
|
|
69
70
|
oauth_invalid_post_logout_redirect_uri_message: "URI de redireccionamento pós-logout inválido"
|
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: 1.0.0
|
|
4
|
+
version: 1.0.0
|
|
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-
|
|
11
|
+
date: 2022-12-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rodauth
|
|
@@ -66,8 +66,7 @@ extra_rdoc_files:
|
|
|
66
66
|
- doc/release_notes/0_9_1.md
|
|
67
67
|
- doc/release_notes/0_9_2.md
|
|
68
68
|
- doc/release_notes/0_9_3.md
|
|
69
|
-
- doc/release_notes/
|
|
70
|
-
- doc/release_notes/1_0_0_beta2.md
|
|
69
|
+
- doc/release_notes/1_0_0.md
|
|
71
70
|
files:
|
|
72
71
|
- CHANGELOG.md
|
|
73
72
|
- LICENSE.txt
|
|
@@ -105,12 +104,12 @@ files:
|
|
|
105
104
|
- doc/release_notes/0_9_1.md
|
|
106
105
|
- doc/release_notes/0_9_2.md
|
|
107
106
|
- doc/release_notes/0_9_3.md
|
|
108
|
-
- doc/release_notes/
|
|
109
|
-
- doc/release_notes/1_0_0_beta2.md
|
|
107
|
+
- doc/release_notes/1_0_0.md
|
|
110
108
|
- lib/generators/rodauth/oauth/install_generator.rb
|
|
111
109
|
- lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
|
|
112
110
|
- lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb
|
|
113
111
|
- lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb
|
|
112
|
+
- lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize_error.erb
|
|
114
113
|
- lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb
|
|
115
114
|
- lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb
|
|
116
115
|
- lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb
|
|
@@ -155,6 +154,7 @@ files:
|
|
|
155
154
|
- locales/en.yml
|
|
156
155
|
- locales/pt.yml
|
|
157
156
|
- templates/authorize.str
|
|
157
|
+
- templates/authorize_error.str
|
|
158
158
|
- templates/client_secret_field.str
|
|
159
159
|
- templates/description_field.str
|
|
160
160
|
- templates/device_search.str
|
|
@@ -169,15 +169,15 @@ files:
|
|
|
169
169
|
- templates/oauth_grants.str
|
|
170
170
|
- templates/redirect_uri_field.str
|
|
171
171
|
- templates/scope_field.str
|
|
172
|
-
homepage: https://gitlab.com/
|
|
172
|
+
homepage: https://gitlab.com/os85/rodauth-oauth
|
|
173
173
|
licenses:
|
|
174
174
|
- Apache-2.0
|
|
175
175
|
metadata:
|
|
176
|
-
homepage_uri: https://
|
|
177
|
-
documentation_uri: https://
|
|
178
|
-
bug_tracker_uri: https://gitlab.com/
|
|
179
|
-
source_code_uri: https://gitlab.com/
|
|
180
|
-
changelog_uri: https://gitlab.com/
|
|
176
|
+
homepage_uri: https://os85.gitlab.io/rodauth-oauth/
|
|
177
|
+
documentation_uri: https://os85.gitlab.io/rodauth-oauth/rdoc/
|
|
178
|
+
bug_tracker_uri: https://gitlab.com/os85/rodauth-oauth/issues
|
|
179
|
+
source_code_uri: https://gitlab.com/os85/rodauth-oauth
|
|
180
|
+
changelog_uri: https://gitlab.com/os85/rodauth-oauth/-/blob/master/CHANGELOG.md
|
|
181
181
|
rubygems_mfa_required: 'true'
|
|
182
182
|
post_install_message:
|
|
183
183
|
rdoc_options: []
|
|
@@ -190,9 +190,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
190
190
|
version: 2.5.0
|
|
191
191
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
192
|
requirements:
|
|
193
|
-
- - "
|
|
193
|
+
- - ">="
|
|
194
194
|
- !ruby/object:Gem::Version
|
|
195
|
-
version:
|
|
195
|
+
version: '0'
|
|
196
196
|
requirements: []
|
|
197
197
|
rubygems_version: 3.2.32
|
|
198
198
|
signing_key:
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
## 1.0.0-beta1 (21/10/2022)
|
|
2
|
-
|
|
3
|
-
### Breaking changes
|
|
4
|
-
|
|
5
|
-
The full description of breaking changes, and suggestions on how to make the migration smoother, can be found in the [migration guide](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/blob/6465b8522a78cf0037a55d3d4b81f68f7811be68/MIGRATION-GUIDE-v1.md).
|
|
6
|
-
|
|
7
|
-
A short list of the main highlights:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* Ruby 2.5 or higher is required.
|
|
11
|
-
* `oauth_http_mac` feature removed.
|
|
12
|
-
* `oauth_tokens` table (and resource) were removed (only `oauth_applications` and `oauth_grants`, access and refresh tokens are now properties of the latter).
|
|
13
|
-
* access and refresh tokens hashed by default when stored in the database.
|
|
14
|
-
* default oauth response mode is `"form_post"`.
|
|
15
|
-
* oauth specific features require explicit enablement of respective features (no more `enable :oauth`)
|
|
16
|
-
* refresh token policy is "rotation" by default
|
|
17
|
-
|
|
18
|
-
### Features
|
|
19
|
-
|
|
20
|
-
The following helpers are exposed in the `rodauth` object:
|
|
21
|
-
|
|
22
|
-
* `current_oauth_account` - returns the dataset row for the `rodauth` account associated to an oauth access token in the "authorization" header.
|
|
23
|
-
* `current_oauth_application` - returns the dataset row for the oauth application associated to an oauth access token in the "authorization" header.
|
|
24
|
-
|
|
25
|
-
When used in `rails` via `rodauth-rails`, both are exposed directly as controller helpers.
|
|
26
|
-
|
|
27
|
-
#### `oauth_resource_server` plugin
|
|
28
|
-
|
|
29
|
-
This plugin can be used as a convenience when configuring resource servers.
|
|
30
|
-
|
|
31
|
-
### Improvements
|
|
32
|
-
|
|
33
|
-
* `:oauth_introspect` plugin: OAuth introspection endpoint exposes the token's `"username"` claim.
|
|
34
|
-
* endpoint client authentication supports "client credentials grant" access tokens.
|
|
35
|
-
|
|
36
|
-
### Bugfixes
|
|
37
|
-
|
|
38
|
-
* fixed `oidc` calculation of `"auth_time"` claim.
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
This version passes the conformance tests for the following OpenID Connect certification profiles:
|
|
2
|
-
|
|
3
|
-
* Basic certification
|
|
4
|
-
* Form-post basic certification
|
|
5
|
-
* Config certification
|
|
6
|
-
* Dynamic Config certification (`response_type=code`)
|
|
7
|
-
|
|
8
|
-
## Breaking Changes
|
|
9
|
-
|
|
10
|
-
* homepage url is no longer a client application required property.
|
|
11
|
-
* OIDC RP-initiated logout extracted into `oidc_rp_initiated_logout` feature.
|
|
12
|
-
|
|
13
|
-
## Features
|
|
14
|
-
|
|
15
|
-
* `oauth_jwt_secured_authorization_request` now supports a `request_uri` query param as well.
|
|
16
|
-
* `oidc` supports essential claims, via the `claims` authorization request query parameter.
|
|
17
|
-
|
|
18
|
-
## Improvements
|
|
19
|
-
|
|
20
|
-
* exposing `acr_values_supported` in the openid configuration.
|
|
21
|
-
* `oauth_request_object_signing_alg_allow_none` enables `"none"` as an accepted request object signing alg when `true` (`false` by default).
|
|
22
|
-
* OIDC `offline_access` supported.
|
|
23
|
-
|
|
24
|
-
## Bugfixes
|
|
25
|
-
|
|
26
|
-
* JWT: "sub" is now always a string.
|
|
27
|
-
* `response_type` is now an authorization request required parameter (as per the RFC).
|
|
28
|
-
* `state` is now passed along when redirecting from authorization requeests with `error`;
|
|
29
|
-
* access token can now be read from POST body or GET quety params (as per the RFC).
|
|
30
|
-
* id token no longer shipping with claims with `null` value;
|
|
31
|
-
* id token no longer encoding claims by default (only when `response_type=id_token`, as per the RFC).
|
|
32
|
-
* support "JWT without kid" when doing jwt decoding for JWT tokens not generated in the provider (such as request objects).
|
|
33
|
-
* Set `iss` and `aud` claims in the Userinfo JWT response.
|
|
34
|
-
* Make sure errors are also delivered via form POST, when `response_mode=form_post`.
|