rodauth-oauth 0.10.4 → 1.0.0.pre.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIGRATION-GUIDE-v1.md +286 -0
- data/README.md +28 -35
- data/doc/release_notes/1_0_0_beta1.md +38 -0
- data/doc/release_notes/1_0_0_beta2.md +34 -0
- data/lib/generators/rodauth/oauth/install_generator.rb +0 -1
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +21 -11
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +1 -1
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +2 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +1 -6
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +0 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_grants.html.erb +41 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +2 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_grants.html.erb +37 -0
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +57 -57
- data/lib/rodauth/features/oauth_application_management.rb +61 -74
- data/lib/rodauth/features/oauth_assertion_base.rb +19 -23
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +62 -90
- data/lib/rodauth/features/oauth_authorize_base.rb +115 -22
- data/lib/rodauth/features/oauth_base.rb +397 -315
- data/lib/rodauth/features/oauth_client_credentials_grant.rb +20 -18
- data/lib/rodauth/features/{oauth_device_grant.rb → oauth_device_code_grant.rb} +62 -73
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +52 -31
- data/lib/rodauth/features/oauth_grant_management.rb +70 -0
- data/lib/rodauth/features/oauth_implicit_grant.rb +29 -27
- data/lib/rodauth/features/oauth_jwt.rb +53 -689
- data/lib/rodauth/features/oauth_jwt_base.rb +458 -0
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +48 -17
- data/lib/rodauth/features/oauth_jwt_jwks.rb +47 -0
- data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +116 -0
- data/lib/rodauth/features/oauth_management_base.rb +2 -0
- data/lib/rodauth/features/oauth_pkce.rb +22 -26
- data/lib/rodauth/features/oauth_resource_indicators.rb +33 -25
- data/lib/rodauth/features/oauth_resource_server.rb +59 -0
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +7 -1
- data/lib/rodauth/features/oauth_token_introspection.rb +76 -46
- data/lib/rodauth/features/oauth_token_revocation.rb +46 -33
- data/lib/rodauth/features/oidc.rb +382 -241
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +127 -51
- data/lib/rodauth/features/oidc_rp_initiated_logout.rb +115 -0
- data/lib/rodauth/oauth/database_extensions.rb +8 -6
- data/lib/rodauth/oauth/http_extensions.rb +74 -0
- data/lib/rodauth/oauth/railtie.rb +20 -0
- data/lib/rodauth/oauth/ttl_store.rb +2 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/lib/rodauth/oauth.rb +29 -1
- data/locales/en.yml +34 -22
- data/locales/pt.yml +34 -22
- data/templates/authorize.str +19 -17
- data/templates/device_search.str +1 -1
- data/templates/device_verification.str +2 -2
- data/templates/jwks_field.str +1 -0
- data/templates/new_oauth_application.str +1 -2
- data/templates/oauth_application.str +2 -2
- data/templates/oauth_application_oauth_grants.str +54 -0
- data/templates/oauth_applications.str +2 -2
- data/templates/oauth_grants.str +52 -0
- metadata +23 -16
- data/lib/generators/rodauth/oauth/templates/app/models/oauth_token.rb +0 -4
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +0 -39
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +0 -35
- data/lib/rodauth/features/oauth.rb +0 -9
- data/lib/rodauth/features/oauth_http_mac.rb +0 -86
- data/lib/rodauth/features/oauth_token_management.rb +0 -81
- data/lib/rodauth/oauth/refinements.rb +0 -48
- data/templates/jwt_public_key_field.str +0 -4
- data/templates/oauth_application_oauth_tokens.str +0 -52
- data/templates/oauth_tokens.str +0 -50
@@ -1,86 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rodauth/oauth/refinements"
|
4
|
-
|
5
|
-
module Rodauth
|
6
|
-
Feature.define(:oauth_http_mac, :OauthHttpMac) do
|
7
|
-
using PrefixExtensions
|
8
|
-
|
9
|
-
depends :oauth
|
10
|
-
|
11
|
-
auth_value_method :oauth_token_type, "mac"
|
12
|
-
auth_value_method :oauth_mac_algorithm, "hmac-sha-256" # hmac-sha-256, hmac-sha-1
|
13
|
-
auth_value_method :oauth_tokens_mac_key_column, :mac_key
|
14
|
-
|
15
|
-
def authorization_token
|
16
|
-
return @authorization_token if defined?(@authorization_token)
|
17
|
-
|
18
|
-
@authorization_token = begin
|
19
|
-
value = request.get_header("HTTP_AUTHORIZATION").to_s
|
20
|
-
|
21
|
-
scheme, token = value.split(/ +/, 2)
|
22
|
-
|
23
|
-
return unless scheme == "MAC"
|
24
|
-
|
25
|
-
mac_attributes = parse_mac_authorization_header_props(token)
|
26
|
-
|
27
|
-
oauth_token = oauth_token_by_token(mac_attributes["id"])
|
28
|
-
|
29
|
-
return unless oauth_token && mac_signature_matches?(oauth_token, mac_attributes)
|
30
|
-
|
31
|
-
oauth_token
|
32
|
-
|
33
|
-
# TODO: set new MAC-KEY for the next request
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def generate_oauth_token(params = {}, *args)
|
40
|
-
super({ oauth_tokens_mac_key_column => oauth_unique_id_generator }.merge(params), *args)
|
41
|
-
end
|
42
|
-
|
43
|
-
def json_access_token_payload(oauth_token)
|
44
|
-
payload = super
|
45
|
-
|
46
|
-
payload["mac_key"] = oauth_token[oauth_tokens_mac_key_column]
|
47
|
-
payload["mac_algorithm"] = oauth_mac_algorithm
|
48
|
-
|
49
|
-
payload
|
50
|
-
end
|
51
|
-
|
52
|
-
def mac_signature_matches?(oauth_token, mac_attributes)
|
53
|
-
nonce = mac_attributes["nonce"]
|
54
|
-
uri = URI(request.url)
|
55
|
-
|
56
|
-
request_signature = [
|
57
|
-
nonce,
|
58
|
-
request.request_method,
|
59
|
-
uri.request_uri,
|
60
|
-
uri.host,
|
61
|
-
uri.port
|
62
|
-
].join("\n") + ("\n" * 3)
|
63
|
-
|
64
|
-
mac_algorithm = case oauth_mac_algorithm
|
65
|
-
when "hmac-sha-256"
|
66
|
-
OpenSSL::Digest::SHA256
|
67
|
-
when "hmac-sha-1"
|
68
|
-
OpenSSL::Digest::SHA1
|
69
|
-
else
|
70
|
-
raise ArgumentError, "Unsupported algorithm"
|
71
|
-
end
|
72
|
-
|
73
|
-
mac_signature = Base64.strict_encode64 \
|
74
|
-
OpenSSL::HMAC.digest(mac_algorithm.new, oauth_token[oauth_tokens_mac_key_column], request_signature)
|
75
|
-
|
76
|
-
mac_signature == mac_attributes["mac"]
|
77
|
-
end
|
78
|
-
|
79
|
-
def parse_mac_authorization_header_props(token)
|
80
|
-
@mac_authorization_header_props = token.split(/ *, */).each_with_object({}) do |prop, props|
|
81
|
-
field, value = prop.split(/ *= */, 2)
|
82
|
-
props[field] = value.delete_prefix("\"").delete_suffix("\"")
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rodauth/oauth/refinements"
|
4
|
-
|
5
|
-
module Rodauth
|
6
|
-
Feature.define(:oauth_token_management, :OauthTokenManagement) do
|
7
|
-
using RegexpExtensions
|
8
|
-
|
9
|
-
depends :oauth_management_base, :oauth_token_revocation
|
10
|
-
|
11
|
-
view "oauth_tokens", "My Oauth Tokens", "oauth_tokens"
|
12
|
-
|
13
|
-
button "Revoke", "oauth_token_revoke"
|
14
|
-
|
15
|
-
auth_value_method :oauth_tokens_path, "oauth-tokens"
|
16
|
-
|
17
|
-
%w[token refresh_token expires_in revoked_at].each do |param|
|
18
|
-
translatable_method :"oauth_tokens_#{param}_label", param.gsub("_", " ").capitalize
|
19
|
-
end
|
20
|
-
|
21
|
-
auth_value_method :oauth_tokens_route, "oauth-tokens"
|
22
|
-
auth_value_method :oauth_tokens_id_pattern, Integer
|
23
|
-
auth_value_method :oauth_tokens_per_page, 20
|
24
|
-
|
25
|
-
auth_value_methods(
|
26
|
-
:oauth_token_path
|
27
|
-
)
|
28
|
-
|
29
|
-
def oauth_tokens_path(opts = {})
|
30
|
-
route_path(oauth_tokens_route, opts)
|
31
|
-
end
|
32
|
-
|
33
|
-
def oauth_tokens_url(opts = {})
|
34
|
-
route_url(oauth_tokens_route, opts)
|
35
|
-
end
|
36
|
-
|
37
|
-
def oauth_token_path(id)
|
38
|
-
"#{oauth_tokens_path}/#{id}"
|
39
|
-
end
|
40
|
-
|
41
|
-
def oauth_tokens
|
42
|
-
request.on(oauth_tokens_route) do
|
43
|
-
require_account
|
44
|
-
|
45
|
-
request.get do
|
46
|
-
page = Integer(param_or_nil("page") || 1)
|
47
|
-
per_page = per_page_param(oauth_tokens_per_page)
|
48
|
-
|
49
|
-
scope.instance_variable_set(:@oauth_tokens, db[oauth_tokens_table]
|
50
|
-
.select(Sequel[oauth_tokens_table].*, Sequel[oauth_applications_table][oauth_applications_name_column])
|
51
|
-
.join(oauth_applications_table, Sequel[oauth_tokens_table][oauth_tokens_oauth_application_id_column] =>
|
52
|
-
Sequel[oauth_applications_table][oauth_applications_id_column])
|
53
|
-
.where(Sequel[oauth_tokens_table][oauth_tokens_account_id_column] => account_id)
|
54
|
-
.where(oauth_tokens_revoked_at_column => nil)
|
55
|
-
.order(Sequel.desc(oauth_tokens_id_column))
|
56
|
-
.paginate(page, per_page))
|
57
|
-
oauth_tokens_view
|
58
|
-
end
|
59
|
-
|
60
|
-
request.post(oauth_tokens_id_pattern) do |id|
|
61
|
-
db[oauth_tokens_table]
|
62
|
-
.where(oauth_tokens_id_column => id)
|
63
|
-
.where(oauth_tokens_account_id_column => account_id)
|
64
|
-
.update(oauth_tokens_revoked_at_column => Sequel::CURRENT_TIMESTAMP)
|
65
|
-
|
66
|
-
set_notice_flash revoke_oauth_token_notice_flash
|
67
|
-
redirect oauth_tokens_path || "/"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def check_csrf?
|
73
|
-
case request.path
|
74
|
-
when oauth_tokens_path
|
75
|
-
only_json? ? false : super
|
76
|
-
else
|
77
|
-
super
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rodauth
|
4
|
-
module PrefixExtensions
|
5
|
-
unless String.method_defined?(:delete_prefix)
|
6
|
-
refine(String) do
|
7
|
-
def delete_suffix(suffix)
|
8
|
-
suffix = suffix.to_s
|
9
|
-
len = suffix.length
|
10
|
-
return dup unless len.positive? && index(suffix, -len)
|
11
|
-
|
12
|
-
self[0...-len]
|
13
|
-
end
|
14
|
-
|
15
|
-
def delete_prefix(prefix)
|
16
|
-
prefix = prefix.to_s
|
17
|
-
return dup unless rindex(prefix, 0)
|
18
|
-
|
19
|
-
self[prefix.length..-1]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
unless String.method_defined?(:delete_suffix!)
|
25
|
-
refine(String) do
|
26
|
-
def delete_suffix!(suffix)
|
27
|
-
suffix = suffix.to_s
|
28
|
-
chomp! if frozen?
|
29
|
-
len = suffix.length
|
30
|
-
return unless len.positive? && index(suffix, -len)
|
31
|
-
|
32
|
-
self[-len..-1] = ""
|
33
|
-
self
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
module RegexpExtensions
|
40
|
-
unless Regexp.method_defined?(:match?)
|
41
|
-
refine(Regexp) do
|
42
|
-
def match?(*args)
|
43
|
-
!match(*args).nil?
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,4 +0,0 @@
|
|
1
|
-
<div class="form-group">
|
2
|
-
<label for="name">#{rodauth.oauth_applications_jwt_public_key_label}#{rodauth.input_field_label_suffix}</label>
|
3
|
-
#{rodauth.input_field_string(rodauth.oauth_application_jwt_public_key_param, "jwt_public_key", :type=>"text", :required=>false)}
|
4
|
-
</div>
|
@@ -1,52 +0,0 @@
|
|
1
|
-
<div id="oauth-tokens">
|
2
|
-
#{
|
3
|
-
if @oauth_tokens.count.zero?
|
4
|
-
"<p>No oauth tokens yet!</p>"
|
5
|
-
else
|
6
|
-
<<-HTML
|
7
|
-
<table class="table">
|
8
|
-
<thead>
|
9
|
-
<tr>
|
10
|
-
<th scope="col">#{rodauth.oauth_tokens_token_label}</th>
|
11
|
-
<th scope="col">#{rodauth.oauth_tokens_refresh_token_label}</th>
|
12
|
-
<th scope="col">#{rodauth.oauth_tokens_expires_in_label}</th>
|
13
|
-
<th scope="col">#{rodauth.oauth_tokens_revoked_at_label}</th>
|
14
|
-
<th scope="col">#{rodauth.oauth_tokens_scopes_label}</th>
|
15
|
-
<th scope="col"><span class="badge badge-pill badge-dark">#{@oauth_tokens.count}</span>
|
16
|
-
</tr>
|
17
|
-
</thead>
|
18
|
-
<tbody>
|
19
|
-
#{
|
20
|
-
@oauth_tokens.map do |oauth_token|
|
21
|
-
<<-HTML
|
22
|
-
<tr>
|
23
|
-
<td><code class="token">#{oauth_token[rodauth.oauth_tokens_token_column]}</code></td>
|
24
|
-
<td><code class="token">#{oauth_token[rodauth.oauth_tokens_refresh_token_column]}</code></td>
|
25
|
-
<td>#{oauth_token[rodauth.oauth_tokens_expires_in_column]}</td>
|
26
|
-
<td>#{oauth_token[rodauth.oauth_tokens_revoked_at_column]}</td>
|
27
|
-
<td>#{oauth_token[rodauth.oauth_tokens_scopes_column]}</td>
|
28
|
-
<td>
|
29
|
-
#{
|
30
|
-
if !oauth_token[rodauth.oauth_tokens_revoked_at_column] && !oauth_token[rodauth.oauth_tokens_token_hash_column]
|
31
|
-
<<-HTML
|
32
|
-
<form method="post" action="#{rodauth.revoke_path}" class="form-horizontal" role="form" id="revoke-form">
|
33
|
-
#{csrf_tag(rodauth.revoke_path) if respond_to?(:csrf_tag)}
|
34
|
-
#{rodauth.input_field_string("token_type_hint", "revoke-token-type-hint", :value => "access_token", :type=>"hidden")}
|
35
|
-
#{rodauth.input_field_string("token", "revoke-token", :value => oauth_token[rodauth.oauth_tokens_token_column], :type=>"hidden")}
|
36
|
-
#{rodauth.button(rodauth.oauth_token_revoke_button)}
|
37
|
-
</form>
|
38
|
-
HTML
|
39
|
-
end
|
40
|
-
}
|
41
|
-
</td>
|
42
|
-
</tr>
|
43
|
-
HTML
|
44
|
-
end.join
|
45
|
-
}
|
46
|
-
</tbody>
|
47
|
-
</table>
|
48
|
-
#{rodauth.oauth_management_pagination_links(@oauth_tokens)}
|
49
|
-
HTML
|
50
|
-
end
|
51
|
-
}
|
52
|
-
</div>
|
data/templates/oauth_tokens.str
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
<div id="oauth-tokens">
|
2
|
-
#{
|
3
|
-
if @oauth_tokens.count.zero?
|
4
|
-
"<p>No oauth tokens yet!</p>"
|
5
|
-
else
|
6
|
-
<<-HTML
|
7
|
-
<table class="table">
|
8
|
-
<thead>
|
9
|
-
<tr>
|
10
|
-
<th scope="col">#{rodauth.oauth_applications_name_label}</th>
|
11
|
-
<th scope="col">#{rodauth.oauth_tokens_token_label}</th>
|
12
|
-
<th scope="col">#{rodauth.oauth_tokens_refresh_token_label}</th>
|
13
|
-
<th scope="col">#{rodauth.oauth_tokens_expires_in_label}</th>
|
14
|
-
<th scope="col">#{rodauth.oauth_tokens_scopes_label}</th>
|
15
|
-
<th scope="col"><span class="badge badge-pill badge-dark">#{@oauth_tokens.count}</span>
|
16
|
-
</tr>
|
17
|
-
</thead>
|
18
|
-
<tbody>
|
19
|
-
#{
|
20
|
-
@oauth_tokens.map do |oauth_token|
|
21
|
-
<<-HTML
|
22
|
-
<tr>
|
23
|
-
<td>#{oauth_token[rodauth.oauth_applications_name_column]}</td>
|
24
|
-
<td><code class="token">#{oauth_token[rodauth.oauth_tokens_token_column]}</code></td>
|
25
|
-
<td><code class="token">#{oauth_token[rodauth.oauth_tokens_refresh_token_column]}</code></td>
|
26
|
-
<td>#{oauth_token[rodauth.oauth_tokens_expires_in_column]}</td>
|
27
|
-
<td>#{oauth_token[rodauth.oauth_tokens_scopes_column]}</td>
|
28
|
-
<td>
|
29
|
-
#{
|
30
|
-
if !oauth_token[rodauth.oauth_tokens_token_hash_column]
|
31
|
-
<<-HTML
|
32
|
-
<form method="post" action="#{rodauth.oauth_token_path(oauth_token[rodauth.oauth_tokens_id_column])}" class="form-horizontal" role="form" id="token-revoke-form">
|
33
|
-
#{csrf_tag(rodauth.oauth_token_path(oauth_token[rodauth.oauth_tokens_id_column])) if respond_to?(:csrf_tag)}
|
34
|
-
#{rodauth.button(rodauth.oauth_token_revoke_button)}
|
35
|
-
</form>
|
36
|
-
HTML
|
37
|
-
end
|
38
|
-
}
|
39
|
-
</td>
|
40
|
-
</tr>
|
41
|
-
HTML
|
42
|
-
end.join
|
43
|
-
}
|
44
|
-
</tbody>
|
45
|
-
</table>
|
46
|
-
#{rodauth.oauth_management_pagination_links(@oauth_tokens)}
|
47
|
-
HTML
|
48
|
-
end
|
49
|
-
}
|
50
|
-
</div>
|