clowk 0.3.0 → 0.4.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/README.md +2 -2
- data/clowk.gemspec +17 -16
- data/config/routes.rb +4 -4
- data/lib/clowk/authenticable.rb +83 -27
- data/lib/clowk/configuration.rb +47 -9
- data/lib/clowk/controllers/base_controller.rb +10 -10
- data/lib/clowk/controllers/callbacks_controller.rb +5 -4
- data/lib/clowk/current.rb +15 -1
- data/lib/clowk/engine.rb +1 -1
- data/lib/clowk/helpers/url_helpers.rb +7 -9
- data/lib/clowk/http/client.rb +9 -9
- data/lib/clowk/http/logger_middleware.rb +3 -2
- data/lib/clowk/http/response.rb +5 -5
- data/lib/clowk/http/retry_middleware.rb +2 -2
- data/lib/clowk/http/timeout_middleware.rb +1 -1
- data/lib/clowk/jwt_verifier.rb +4 -4
- data/lib/clowk/middleware/token_extractor.rb +9 -3
- data/lib/clowk/sdk/client.rb +5 -12
- data/lib/clowk/sdk/resource.rb +6 -6
- data/lib/clowk/sdk/session.rb +7 -3
- data/lib/clowk/sdk/session_config.rb +1 -1
- data/lib/clowk/sdk/subdomain.rb +1 -1
- data/lib/clowk/sdk/token.rb +8 -3
- data/lib/clowk/sdk/user.rb +1 -1
- data/lib/clowk/subdomain.rb +35 -30
- data/lib/clowk/version.rb +1 -1
- data/lib/clowk.rb +32 -31
- metadata +18 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23e593a98742fb2d99e89bcec77d30c250300f1ce40789ebadd82277548419d8
|
|
4
|
+
data.tar.gz: 0acb397f16bca42b0599cf2ab1e8c223010ea2ae1bb7f7031700e2cdb568f293
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b9cccf22eb2a0760dc0b949ac2021d6f509456424be2d93286af8fe47727a5853eee1dc06a8d5b934725288bc682ff34b74fe85e15c547877f9a78705d520f5
|
|
7
|
+
data.tar.gz: 515c0d900a8afada712f6aa2252fba6415afe3975db242c053131ae2749a127575adba4c801ca9d44ff27161cc4de660ec15fab89eba350cb61844efe55d54fa
|
data/README.md
CHANGED
|
@@ -86,7 +86,7 @@ Clowk.configure do |config|
|
|
|
86
86
|
config.after_sign_in_path = '/'
|
|
87
87
|
config.after_sign_out_path = '/'
|
|
88
88
|
|
|
89
|
-
config.api_base_url = 'https://api.clowk.dev/
|
|
89
|
+
config.api_base_url = 'https://api.clowk.dev/api/v1'
|
|
90
90
|
config.callback_path = '/clowk/oauth/callback'
|
|
91
91
|
config.mount_path = '/clowk'
|
|
92
92
|
|
|
@@ -362,4 +362,4 @@ Its job is to make the Rails side of Clowk integration predictable:
|
|
|
362
362
|
|
|
363
363
|
## License
|
|
364
364
|
|
|
365
|
-
|
|
365
|
+
AGPL-3.0. See `LICENSE`.
|
data/clowk.gemspec
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
3
|
+
require_relative "lib/clowk/version"
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name =
|
|
6
|
+
spec.name = "clowk"
|
|
7
7
|
spec.version = Clowk::VERSION
|
|
8
|
-
spec.authors = [
|
|
9
|
-
spec.email = [
|
|
8
|
+
spec.authors = ["Clowk"]
|
|
9
|
+
spec.email = ["support@clowk.in"]
|
|
10
10
|
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.description =
|
|
13
|
-
spec.homepage =
|
|
14
|
-
spec.license =
|
|
15
|
-
spec.required_ruby_version =
|
|
11
|
+
spec.summary = "Rails SDK for Clowk authentication"
|
|
12
|
+
spec.description = "Clowk Authentication, JWT verification, and future API access"
|
|
13
|
+
spec.homepage = "https://clowk.in"
|
|
14
|
+
spec.license = "AGPL-3.0-only"
|
|
15
|
+
spec.required_ruby_version = ">= 3.3"
|
|
16
16
|
spec.metadata = {
|
|
17
|
-
|
|
17
|
+
"rubygems_mfa_required" => "true"
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
spec.files = Dir.chdir(__dir__) do
|
|
21
|
-
Dir[
|
|
21
|
+
Dir["README.md", "clowk.gemspec", "config/routes.rb", "lib/**/*.rb"]
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
spec.require_paths = [
|
|
24
|
+
spec.require_paths = ["lib"]
|
|
25
25
|
|
|
26
|
-
spec.add_dependency
|
|
27
|
-
spec.add_dependency
|
|
28
|
-
spec.add_dependency
|
|
26
|
+
spec.add_dependency "activesupport", ">= 7.0"
|
|
27
|
+
spec.add_dependency "jwt", ">= 2.7", "< 3.0"
|
|
28
|
+
spec.add_dependency "railties", ">= 7.0"
|
|
29
29
|
|
|
30
|
-
spec.add_development_dependency
|
|
30
|
+
spec.add_development_dependency "rspec", ">= 3.13", "< 4.0"
|
|
31
|
+
spec.add_development_dependency "standard", ">= 1.0"
|
|
31
32
|
end
|
data/config/routes.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
Clowk::Engine.routes.draw do
|
|
4
|
-
get
|
|
5
|
-
get
|
|
6
|
-
match
|
|
7
|
-
get
|
|
4
|
+
get "/sign_in", to: "sessions#new", as: :sign_in
|
|
5
|
+
get "/sign_up", to: "sessions#sign_up", as: :sign_up
|
|
6
|
+
match "/sign_out", to: "sessions#destroy", via: %i[get delete], as: :sign_out
|
|
7
|
+
get "/oauth/callback", to: "callbacks#show", as: :auth_callback
|
|
8
8
|
end
|
data/lib/clowk/authenticable.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "active_support/concern"
|
|
4
4
|
|
|
5
5
|
module Clowk
|
|
6
6
|
module Authenticable
|
|
@@ -13,22 +13,37 @@ module Clowk
|
|
|
13
13
|
signed_in_method = :"#{scope}_signed_in?"
|
|
14
14
|
|
|
15
15
|
enforce_session_method = :"#{scope}_enforce_session!"
|
|
16
|
+
sign_out_method = :"#{scope}_sign_out!"
|
|
16
17
|
|
|
17
18
|
base.class_eval do
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
unless current_method == :clowk_current_resource
|
|
20
|
+
define_method(current_method) do
|
|
21
|
+
clowk_current_resource
|
|
22
|
+
end
|
|
20
23
|
end
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
unless authenticate_method == :clowk_authenticate!
|
|
26
|
+
define_method(authenticate_method) do
|
|
27
|
+
clowk_authenticate!
|
|
28
|
+
end
|
|
24
29
|
end
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
unless signed_in_method == :clowk_signed_in?
|
|
32
|
+
define_method(signed_in_method) do
|
|
33
|
+
clowk_signed_in?
|
|
34
|
+
end
|
|
28
35
|
end
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
unless enforce_session_method == :clowk_enforce_session!
|
|
38
|
+
define_method(enforce_session_method) do
|
|
39
|
+
clowk_enforce_session!
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
unless sign_out_method == :clowk_sign_out!
|
|
44
|
+
define_method(sign_out_method) do
|
|
45
|
+
clowk_sign_out!
|
|
46
|
+
end
|
|
32
47
|
end
|
|
33
48
|
|
|
34
49
|
helper_method current_method, authenticate_method, signed_in_method, :current_token if respond_to?(:helper_method)
|
|
@@ -47,7 +62,7 @@ module Clowk
|
|
|
47
62
|
end
|
|
48
63
|
|
|
49
64
|
def current_token
|
|
50
|
-
stored_session&.dig(
|
|
65
|
+
stored_session&.dig("token") || extracted_token
|
|
51
66
|
end
|
|
52
67
|
|
|
53
68
|
def clowk_signed_in?
|
|
@@ -59,7 +74,7 @@ module Clowk
|
|
|
59
74
|
end
|
|
60
75
|
|
|
61
76
|
def clowk_session_active?
|
|
62
|
-
clowk_session_status&.dig(:status) ==
|
|
77
|
+
clowk_session_status&.dig(:status) == "active"
|
|
63
78
|
end
|
|
64
79
|
|
|
65
80
|
def clowk_enforce_session!
|
|
@@ -74,21 +89,21 @@ module Clowk
|
|
|
74
89
|
return
|
|
75
90
|
end
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
render json: { error: 'Session expired or inactive' }, status: :unauthorized
|
|
79
|
-
else
|
|
80
|
-
redirect_to clowk_sign_in_path(return_to: request.fullpath)
|
|
81
|
-
end
|
|
92
|
+
clowk_handle_expired_session(session_info)
|
|
82
93
|
end
|
|
83
94
|
|
|
84
95
|
def clowk_authenticate!
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
return clowk_handle_unauthenticated unless clowk_signed_in?
|
|
97
|
+
|
|
98
|
+
# A valid token proves who signed in, not that the session still stands —
|
|
99
|
+
# revocation lives server-side. Opt in with config.enforce_active_session
|
|
100
|
+
# to pay a lookup (cached for session_status_ttl) on every authentication.
|
|
101
|
+
if Clowk.config.enforce_active_session
|
|
102
|
+
clowk_enforce_session!
|
|
103
|
+
return if respond_to?(:performed?) && performed?
|
|
91
104
|
end
|
|
105
|
+
|
|
106
|
+
clowk_current_resource
|
|
92
107
|
end
|
|
93
108
|
|
|
94
109
|
def clowk_sign_out!
|
|
@@ -100,6 +115,22 @@ module Clowk
|
|
|
100
115
|
|
|
101
116
|
private
|
|
102
117
|
|
|
118
|
+
def clowk_handle_unauthenticated
|
|
119
|
+
if request.format.json?
|
|
120
|
+
render json: {error: "Unauthorized"}, status: :unauthorized
|
|
121
|
+
else
|
|
122
|
+
redirect_to clowk_sign_in_path(return_to: request.fullpath)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def clowk_handle_expired_session(_session_info)
|
|
127
|
+
if request.format.json?
|
|
128
|
+
render json: {error: "Session expired or inactive"}, status: :unauthorized
|
|
129
|
+
else
|
|
130
|
+
redirect_to clowk_sign_in_path(return_to: request.fullpath)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
103
134
|
def verified_request_payload
|
|
104
135
|
return unless extracted_token
|
|
105
136
|
|
|
@@ -111,8 +142,13 @@ module Clowk
|
|
|
111
142
|
nil
|
|
112
143
|
end
|
|
113
144
|
|
|
145
|
+
# Bearer header or cookie only — never the query string. A valid token in a
|
|
146
|
+
# URL would otherwise sign the visitor in on ANY path, bypassing the state
|
|
147
|
+
# check that makes the OAuth callback safe, and would stay replayable for the
|
|
148
|
+
# token's full lifetime anywhere the URL was logged. CallbacksController
|
|
149
|
+
# reads params directly, after validating state.
|
|
114
150
|
def extracted_token
|
|
115
|
-
@extracted_token ||= Clowk::Middleware::TokenExtractor.new(request).call
|
|
151
|
+
@extracted_token ||= Clowk::Middleware::TokenExtractor.new(request, token_param: nil).call
|
|
116
152
|
end
|
|
117
153
|
|
|
118
154
|
def stored_session
|
|
@@ -123,7 +159,7 @@ module Clowk
|
|
|
123
159
|
end
|
|
124
160
|
|
|
125
161
|
def stored_user_payload
|
|
126
|
-
payload = stored_session&.dig(
|
|
162
|
+
payload = stored_session&.dig("user") || stored_session&.dig(:user)
|
|
127
163
|
payload&.deep_symbolize_keys
|
|
128
164
|
end
|
|
129
165
|
|
|
@@ -143,9 +179,9 @@ module Clowk
|
|
|
143
179
|
end
|
|
144
180
|
|
|
145
181
|
def resolve_session_status
|
|
146
|
-
cached = stored_session&.dig(
|
|
182
|
+
cached = stored_session&.dig("session_status") || stored_session&.dig(:session_status)
|
|
147
183
|
|
|
148
|
-
return cached&.deep_symbolize_keys if cached
|
|
184
|
+
return cached&.deep_symbolize_keys if cached && clowk_session_status_fresh?
|
|
149
185
|
|
|
150
186
|
resource = clowk_current_resource
|
|
151
187
|
|
|
@@ -156,9 +192,29 @@ module Clowk
|
|
|
156
192
|
result = client.tokens.verify_with_session(token: current_token)
|
|
157
193
|
status = result&.dig(:session)
|
|
158
194
|
|
|
159
|
-
|
|
195
|
+
if status && stored_session
|
|
196
|
+
session[Clowk.config.session_key] = stored_session.merge(
|
|
197
|
+
"session_status" => status,
|
|
198
|
+
"session_status_checked_at" => Time.now.to_i
|
|
199
|
+
)
|
|
200
|
+
end
|
|
160
201
|
|
|
161
202
|
status
|
|
203
|
+
rescue Clowk::InvalidTokenError
|
|
204
|
+
nil
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Whether the cached status may still be trusted. Timestamped alongside the
|
|
208
|
+
# payload rather than inside it, so what we hand back stays exactly what the
|
|
209
|
+
# API returned.
|
|
210
|
+
def clowk_session_status_fresh?
|
|
211
|
+
ttl = Clowk.config.session_status_ttl.to_i
|
|
212
|
+
return false unless ttl.positive?
|
|
213
|
+
|
|
214
|
+
checked_at = (stored_session&.dig("session_status_checked_at") ||
|
|
215
|
+
stored_session&.dig(:session_status_checked_at)).to_i
|
|
216
|
+
|
|
217
|
+
checked_at.positive? && (Time.now.to_i - checked_at) < ttl
|
|
162
218
|
end
|
|
163
219
|
end
|
|
164
220
|
end
|
data/lib/clowk/configuration.rb
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Clowk
|
|
4
4
|
class Configuration
|
|
5
|
+
attr_accessor :api_base_url
|
|
5
6
|
attr_accessor :app_base_url
|
|
6
|
-
attr_accessor :after_sign_in_path
|
|
7
|
-
attr_accessor :after_sign_out_path
|
|
8
7
|
attr_accessor :callback_path
|
|
9
8
|
attr_accessor :cookie_key
|
|
10
9
|
attr_accessor :http_logger
|
|
@@ -23,26 +22,65 @@ module Clowk
|
|
|
23
22
|
attr_accessor :token_param
|
|
24
23
|
attr_accessor :enforce_active_session
|
|
25
24
|
attr_accessor :on_session_expired
|
|
25
|
+
attr_accessor :session_status_ttl
|
|
26
26
|
|
|
27
27
|
def initialize
|
|
28
|
-
@
|
|
29
|
-
@
|
|
30
|
-
@
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@
|
|
28
|
+
@api_base_url = "https://api.clowk.dev/api/v1"
|
|
29
|
+
@app_base_url = "https://app.clowk.in"
|
|
30
|
+
@after_sign_in_path = "/"
|
|
31
|
+
@after_sign_out_path = "/"
|
|
32
|
+
@mount_path = "/clowk"
|
|
33
|
+
@callback_path = "/clowk/oauth/callback"
|
|
34
|
+
@cookie_key = "clowk_token"
|
|
34
35
|
@http_logger = nil
|
|
35
36
|
@http_open_timeout = 5
|
|
36
37
|
@http_read_timeout = 10
|
|
37
38
|
@http_retry_attempts = 2
|
|
38
39
|
@http_retry_interval = 0.05
|
|
39
40
|
@http_write_timeout = 10
|
|
40
|
-
@issuer =
|
|
41
|
+
@issuer = "clowk"
|
|
41
42
|
@session_key = :clowk
|
|
42
43
|
@prefix_by = :clowk
|
|
43
44
|
@token_param = :token
|
|
44
45
|
@enforce_active_session = false
|
|
45
46
|
@on_session_expired = nil
|
|
47
|
+
# How long a fetched session status stays trusted, in seconds. Without a
|
|
48
|
+
# TTL the first lookup would be cached for the life of the Rails session,
|
|
49
|
+
# which silently turns every later enforcement call into a no-op. Set 0 to
|
|
50
|
+
# check on every call.
|
|
51
|
+
@session_status_ttl = 300
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def after_sign_in_path
|
|
55
|
+
resolve_path(@after_sign_in_path)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def after_sign_out_path
|
|
59
|
+
resolve_path(@after_sign_out_path)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
attr_writer :after_sign_in_path
|
|
63
|
+
|
|
64
|
+
attr_writer :after_sign_out_path
|
|
65
|
+
|
|
66
|
+
def validate!
|
|
67
|
+
errors = []
|
|
68
|
+
errors << "secret_key must be a String" unless @secret_key.is_a?(String) || @secret_key.nil?
|
|
69
|
+
errors << "http_open_timeout must be Numeric" unless @http_open_timeout.is_a?(Numeric)
|
|
70
|
+
errors << "http_read_timeout must be Numeric" unless @http_read_timeout.is_a?(Numeric)
|
|
71
|
+
errors << "http_write_timeout must be Numeric" unless @http_write_timeout.is_a?(Numeric)
|
|
72
|
+
errors << "http_retry_attempts must be a non-negative Integer" unless @http_retry_attempts.is_a?(Integer) && @http_retry_attempts >= 0
|
|
73
|
+
errors << "session_status_ttl must be a non-negative Integer" unless @session_status_ttl.is_a?(Integer) && @session_status_ttl >= 0
|
|
74
|
+
|
|
75
|
+
raise ConfigurationError, errors.join(", ") if errors.any?
|
|
76
|
+
|
|
77
|
+
true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def resolve_path(path_or_callable)
|
|
83
|
+
path_or_callable.respond_to?(:call) ? path_or_callable.call : path_or_callable
|
|
46
84
|
end
|
|
47
85
|
end
|
|
48
86
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "uri"
|
|
5
5
|
|
|
6
6
|
module Clowk
|
|
7
7
|
class BaseController < ActionController::Base
|
|
@@ -13,18 +13,18 @@ module Clowk
|
|
|
13
13
|
private
|
|
14
14
|
|
|
15
15
|
def redirect_back_or(default, return_to: params[:return_to])
|
|
16
|
-
redirect_target = safe_redirect_path(return_to) || safe_redirect_path(default) ||
|
|
16
|
+
redirect_target = safe_redirect_path(return_to) || safe_redirect_path(default) || "/"
|
|
17
17
|
|
|
18
18
|
redirect_to redirect_target
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def start_clowk_auth_flow!(return_to: nil)
|
|
22
|
-
sanitized_return_to = safe_redirect_path(return_to) || safe_redirect_path(Clowk.config.after_sign_in_path) ||
|
|
22
|
+
sanitized_return_to = safe_redirect_path(return_to) || safe_redirect_path(Clowk.config.after_sign_in_path) || "/"
|
|
23
23
|
state = SecureRandom.hex(32)
|
|
24
24
|
|
|
25
25
|
session[:clowk_auth_flow] = {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
"state" => state,
|
|
27
|
+
"return_to" => sanitized_return_to
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
state
|
|
@@ -38,9 +38,9 @@ module Clowk
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def validate_clowk_state!(expected_state, actual_state)
|
|
41
|
-
raise Clowk::InvalidStateError,
|
|
42
|
-
raise Clowk::InvalidStateError,
|
|
43
|
-
raise Clowk::InvalidStateError,
|
|
41
|
+
raise Clowk::InvalidStateError, "missing state" if actual_state.blank?
|
|
42
|
+
raise Clowk::InvalidStateError, "missing state" if expected_state.blank?
|
|
43
|
+
raise Clowk::InvalidStateError, "invalid state" unless state_matches?(expected_state, actual_state)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def state_matches?(expected_state, actual_state)
|
|
@@ -53,7 +53,7 @@ module Clowk
|
|
|
53
53
|
value = candidate.to_s
|
|
54
54
|
return if value.empty?
|
|
55
55
|
|
|
56
|
-
return value if value.start_with?(
|
|
56
|
+
return value if value.start_with?("/") && !value.start_with?("//")
|
|
57
57
|
|
|
58
58
|
uri = URI.parse(value)
|
|
59
59
|
return unless uri.host == request.host && uri.scheme == request.scheme
|
|
@@ -4,20 +4,21 @@ module Clowk
|
|
|
4
4
|
class CallbacksController < BaseController
|
|
5
5
|
def show
|
|
6
6
|
flow = consume_clowk_auth_flow!
|
|
7
|
-
validate_clowk_state!(flow[
|
|
7
|
+
validate_clowk_state!(flow["state"], params[:state])
|
|
8
8
|
|
|
9
9
|
token = params[Clowk.config.token_param]
|
|
10
|
-
raise Clowk::InvalidTokenError,
|
|
10
|
+
raise Clowk::InvalidTokenError, "missing token" if token.blank?
|
|
11
11
|
|
|
12
12
|
payload = Clowk::JwtVerifier.new.verify(token)
|
|
13
|
-
return_to = flow[
|
|
13
|
+
return_to = flow["return_to"]
|
|
14
14
|
|
|
15
15
|
reset_clowk_session!
|
|
16
16
|
persist_clowk_session(token, payload)
|
|
17
17
|
|
|
18
18
|
redirect_back_or(Clowk.config.after_sign_in_path, return_to:)
|
|
19
19
|
rescue Clowk::InvalidTokenError, Clowk::InvalidStateError => e
|
|
20
|
-
|
|
20
|
+
Rails.logger.error("[Clowk] Authentication failed: #{e.class} - #{e.message}")
|
|
21
|
+
flash[:alert] = "Authentication failed. Please try again."
|
|
21
22
|
|
|
22
23
|
redirect_back_or(Clowk.config.after_sign_out_path, return_to: nil)
|
|
23
24
|
end
|
data/lib/clowk/current.rb
CHANGED
|
@@ -47,5 +47,19 @@ module Clowk
|
|
|
47
47
|
def to_h
|
|
48
48
|
attributes.merge(id: id)
|
|
49
49
|
end
|
|
50
|
+
|
|
51
|
+
def ==(other)
|
|
52
|
+
return false unless other.is_a?(Current)
|
|
53
|
+
|
|
54
|
+
to_h == other.to_h
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def eql?(other)
|
|
58
|
+
self == other
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def hash
|
|
62
|
+
to_h.hash
|
|
63
|
+
end
|
|
50
64
|
end
|
|
51
|
-
end
|
|
65
|
+
end
|
data/lib/clowk/engine.rb
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'cgi'
|
|
4
|
-
|
|
5
3
|
module Clowk
|
|
6
4
|
module Helpers
|
|
7
5
|
module UrlHelpers
|
|
8
6
|
def clowk_sign_in_path(return_to: nil)
|
|
9
|
-
append_query(clowk_local_path(
|
|
7
|
+
append_query(clowk_local_path("/sign_in"), return_to:)
|
|
10
8
|
end
|
|
11
9
|
|
|
12
10
|
def clowk_sign_up_path(return_to: nil)
|
|
13
|
-
append_query(clowk_local_path(
|
|
11
|
+
append_query(clowk_local_path("/sign_up"), return_to:)
|
|
14
12
|
end
|
|
15
13
|
|
|
16
14
|
def clowk_sign_out_path(return_to: nil)
|
|
17
|
-
append_query(clowk_local_path(
|
|
15
|
+
append_query(clowk_local_path("/sign_out"), return_to:)
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
def clowk_callback_url(return_to: nil, state: nil)
|
|
@@ -22,18 +20,18 @@ module Clowk
|
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
def clowk_sign_in_url(redirect_to: nil, return_to: nil, state: nil)
|
|
25
|
-
clowk_remote_auth_url(
|
|
23
|
+
clowk_remote_auth_url("sign-in", redirect_to:, return_to:, state:)
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
def clowk_sign_up_url(redirect_to: nil, return_to: nil, state: nil)
|
|
29
|
-
clowk_remote_auth_url(
|
|
27
|
+
clowk_remote_auth_url("sign-up", redirect_to:, return_to:, state:)
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
private
|
|
33
31
|
|
|
34
32
|
def clowk_remote_auth_url(action, redirect_to:, return_to:, state:)
|
|
35
33
|
callback_url = clowk_callback_url(return_to: redirect_to || return_to, state:)
|
|
36
|
-
query = {
|
|
34
|
+
query = {redirect_uri: callback_url}
|
|
37
35
|
|
|
38
36
|
append_query("#{clowk_instance_base_url}/#{action}", query)
|
|
39
37
|
end
|
|
@@ -50,7 +48,7 @@ module Clowk
|
|
|
50
48
|
filtered = params.compact.reject { |_key, value| value.respond_to?(:empty?) && value.empty? }
|
|
51
49
|
return url if filtered.empty?
|
|
52
50
|
|
|
53
|
-
separator = url.include?(
|
|
51
|
+
separator = url.include?("?") ? "&" : "?"
|
|
54
52
|
"#{url}#{separator}#{Rack::Utils.build_query(filtered)}"
|
|
55
53
|
end
|
|
56
54
|
end
|
data/lib/clowk/http/client.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "uri"
|
|
6
6
|
|
|
7
7
|
module Clowk
|
|
8
8
|
class Http
|
|
@@ -121,7 +121,7 @@ module Clowk
|
|
|
121
121
|
|
|
122
122
|
request.body = JSON.generate(env[:body]) unless env[:body].nil?
|
|
123
123
|
|
|
124
|
-
raw_response = Net::HTTP.start(env[:uri].host, env[:uri].port, use_ssl: env[:uri].scheme ==
|
|
124
|
+
raw_response = Net::HTTP.start(env[:uri].host, env[:uri].port, use_ssl: env[:uri].scheme == "https") do |http|
|
|
125
125
|
apply_timeouts(http, env[:timeouts])
|
|
126
126
|
http.request(request)
|
|
127
127
|
end
|
|
@@ -144,12 +144,12 @@ module Clowk
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def normalize_path(path)
|
|
147
|
-
path.to_s.start_with?(
|
|
147
|
+
path.to_s.start_with?("/") ? path : "/#{path}"
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
def join_paths(base_path, extra_path)
|
|
151
|
-
segments = [base_path.to_s, extra_path.to_s].map { |segment| segment.gsub(%r{^/+|/+$},
|
|
152
|
-
"/#{segments.join(
|
|
151
|
+
segments = [base_path.to_s, extra_path.to_s].map { |segment| segment.gsub(%r{^/+|/+$}, "") }.reject(&:empty?)
|
|
152
|
+
"/#{segments.join("/")}"
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
def apply_headers(request, request_headers)
|
|
@@ -167,8 +167,8 @@ module Clowk
|
|
|
167
167
|
|
|
168
168
|
def default_headers
|
|
169
169
|
{
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
"Accept" => "application/json",
|
|
171
|
+
"Content-Type" => "application/json"
|
|
172
172
|
}
|
|
173
173
|
end
|
|
174
174
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "logger"
|
|
4
4
|
|
|
5
5
|
module Clowk
|
|
6
6
|
class Http
|
|
@@ -22,7 +22,8 @@ module Clowk
|
|
|
22
22
|
attr_reader :app, :logger
|
|
23
23
|
|
|
24
24
|
class NullLogger
|
|
25
|
-
def info(*)
|
|
25
|
+
def info(*)
|
|
26
|
+
end
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
end
|
data/lib/clowk/http/response.rb
CHANGED
|
@@ -27,17 +27,17 @@ module Clowk
|
|
|
27
27
|
body: body,
|
|
28
28
|
body_parsed: body_parsed,
|
|
29
29
|
headers: headers,
|
|
30
|
-
success
|
|
30
|
+
success: success?
|
|
31
31
|
}
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def ==(other)
|
|
35
|
-
if other.respond_to?(:to_h)
|
|
36
|
-
|
|
35
|
+
to_h == if other.respond_to?(:to_h)
|
|
36
|
+
other.to_h
|
|
37
37
|
else
|
|
38
|
-
|
|
38
|
+
other
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
|
-
end
|
|
43
|
+
end
|
data/lib/clowk/jwt_verifier.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "jwt"
|
|
4
4
|
|
|
5
5
|
module Clowk
|
|
6
6
|
class JwtVerifier
|
|
7
|
-
ALGORITHM =
|
|
7
|
+
ALGORITHM = "HS256"
|
|
8
8
|
|
|
9
9
|
def initialize(secret_key: Clowk.config.secret_key, issuer: Clowk.config.issuer)
|
|
10
10
|
@secret_key = secret_key
|
|
@@ -12,9 +12,9 @@ module Clowk
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def verify(token)
|
|
15
|
-
raise ConfigurationError,
|
|
15
|
+
raise ConfigurationError, "missing Clowk secret_key" if @secret_key.to_s.empty?
|
|
16
16
|
|
|
17
|
-
options = {
|
|
17
|
+
options = {algorithm: ALGORITHM}
|
|
18
18
|
options[:iss] = @issuer if @issuer
|
|
19
19
|
options[:verify_iss] = @issuer.present?
|
|
20
20
|
|
|
@@ -17,8 +17,14 @@ module Clowk
|
|
|
17
17
|
|
|
18
18
|
attr_reader :request, :token_param, :cookie_key
|
|
19
19
|
|
|
20
|
+
# Opt-in only. Reading a token from the query string on every request lets
|
|
21
|
+
# a link like /anything?token=<jwt> establish a session without ever
|
|
22
|
+
# passing the callback's `state` check, so callers that establish sessions
|
|
23
|
+
# pass token_param: nil. The callback reads params itself, after state.
|
|
20
24
|
def token_from_params
|
|
21
|
-
|
|
25
|
+
return if token_param.nil?
|
|
26
|
+
|
|
27
|
+
params = (request.respond_to?(:params) && request.params) ? request.params : {}
|
|
22
28
|
params[token_param.to_s].presence
|
|
23
29
|
end
|
|
24
30
|
|
|
@@ -26,8 +32,8 @@ module Clowk
|
|
|
26
32
|
header = request.authorization.to_s
|
|
27
33
|
return if header.empty?
|
|
28
34
|
|
|
29
|
-
scheme, token = header.split(
|
|
30
|
-
return unless scheme.to_s.casecmp(
|
|
35
|
+
scheme, token = header.split(" ", 2)
|
|
36
|
+
return unless scheme.to_s.casecmp("Bearer").zero?
|
|
31
37
|
|
|
32
38
|
token.presence
|
|
33
39
|
end
|
data/lib/clowk/sdk/client.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "active_support/inflector"
|
|
4
4
|
|
|
5
5
|
module Clowk
|
|
6
6
|
module SDK
|
|
7
7
|
class Client
|
|
8
8
|
def initialize(options = {})
|
|
9
|
-
@api_base_url = options.fetch(:api_base_url, nil).presence ||
|
|
9
|
+
@api_base_url = options.fetch(:api_base_url, nil).presence || Clowk.config.api_base_url
|
|
10
10
|
@secret_key = options.fetch(:secret_key, Clowk.config.secret_key)
|
|
11
11
|
@publishable_key = options.fetch(:publishable_key, Clowk.config.publishable_key)
|
|
12
12
|
end
|
|
@@ -18,7 +18,7 @@ module Clowk
|
|
|
18
18
|
|
|
19
19
|
return super unless Clowk::SDK.const_defined?(resource_class_name)
|
|
20
20
|
|
|
21
|
-
resource_ivar = "@#{
|
|
21
|
+
resource_ivar = :"@#{resource_class_name}"
|
|
22
22
|
return instance_variable_get(resource_ivar) if instance_variable_defined?(resource_ivar)
|
|
23
23
|
|
|
24
24
|
resource_class = Clowk::SDK.const_get(resource_class_name)
|
|
@@ -65,13 +65,6 @@ module Clowk
|
|
|
65
65
|
|
|
66
66
|
attr_reader :api_base_url, :publishable_key, :secret_key
|
|
67
67
|
|
|
68
|
-
def derive_api_base_url
|
|
69
|
-
base = Clowk.config.subdomain_url.to_s.strip
|
|
70
|
-
return if base.empty?
|
|
71
|
-
|
|
72
|
-
"#{base.sub(%r{/$}, '')}/api/v1"
|
|
73
|
-
end
|
|
74
|
-
|
|
75
68
|
def http
|
|
76
69
|
@http ||= Clowk::Http.new(
|
|
77
70
|
base_url: api_base_url,
|
|
@@ -87,8 +80,8 @@ module Clowk
|
|
|
87
80
|
|
|
88
81
|
def default_headers
|
|
89
82
|
{}.tap do |headers|
|
|
90
|
-
headers[
|
|
91
|
-
headers[
|
|
83
|
+
headers["X-Clowk-Secret-Key"] = secret_key if secret_key.present?
|
|
84
|
+
headers["X-Clowk-Publishable-Key"] = publishable_key if publishable_key.present?
|
|
92
85
|
end
|
|
93
86
|
end
|
|
94
87
|
end
|
data/lib/clowk/sdk/resource.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "erb"
|
|
4
4
|
|
|
5
5
|
module Clowk
|
|
6
6
|
module SDK
|
|
7
7
|
class Resource
|
|
8
8
|
def self.resource_path
|
|
9
|
-
raise NotImplementedError,
|
|
9
|
+
raise NotImplementedError, "resource_path must be implemented"
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def initialize(client)
|
|
@@ -31,10 +31,10 @@ module Clowk
|
|
|
31
31
|
# @return [Clowk::Http::Response]
|
|
32
32
|
def search(raw_query = nil, **filters)
|
|
33
33
|
query = if raw_query.is_a?(String)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
raw_query
|
|
35
|
+
else
|
|
36
|
+
filters.map { |k, v| "#{k}:#{v}" }.join(" ")
|
|
37
|
+
end
|
|
38
38
|
|
|
39
39
|
client.get("#{self.class.resource_path}/search?query=#{ERB::Util.url_encode(query)}")
|
|
40
40
|
end
|
data/lib/clowk/sdk/session.rb
CHANGED
|
@@ -4,12 +4,16 @@ module Clowk
|
|
|
4
4
|
module SDK
|
|
5
5
|
class Session < Resource
|
|
6
6
|
def self.resource_path
|
|
7
|
-
|
|
7
|
+
"sessions"
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
# @param
|
|
10
|
+
# @param raw_query [String, nil] Raw query string (forwarded to the base search)
|
|
11
|
+
# @param email [String, nil] Email to search for via the legacy ILIKE endpoint
|
|
12
|
+
# @param filters [Hash] Keyword filters (forwarded to the base search)
|
|
11
13
|
# @return [Clowk::Http::Response]
|
|
12
|
-
def search(email:)
|
|
14
|
+
def search(raw_query = nil, email: nil, **filters)
|
|
15
|
+
return super(raw_query, **filters) unless email
|
|
16
|
+
|
|
13
17
|
client.get("#{self.class.resource_path}/search?email=#{ERB::Util.url_encode(email)}")
|
|
14
18
|
end
|
|
15
19
|
|
data/lib/clowk/sdk/subdomain.rb
CHANGED
data/lib/clowk/sdk/token.rb
CHANGED
|
@@ -4,16 +4,21 @@ module Clowk
|
|
|
4
4
|
module SDK
|
|
5
5
|
class Token < Resource
|
|
6
6
|
def self.resource_path
|
|
7
|
-
|
|
7
|
+
"tokens"
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def verify(token:)
|
|
11
|
-
client.post("#{self.class.resource_path}/verify", {
|
|
11
|
+
client.post("#{self.class.resource_path}/verify", {token: token})
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def verify_with_session(token:)
|
|
15
15
|
response = verify(token:)
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
unless response.success?
|
|
18
|
+
raise Clowk::InvalidTokenError, response.body_parsed&.dig("error") || "token verification failed"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
data = response.body_parsed&.dig("data") || response.body_parsed
|
|
17
22
|
|
|
18
23
|
data&.deep_symbolize_keys
|
|
19
24
|
end
|
data/lib/clowk/sdk/user.rb
CHANGED
data/lib/clowk/subdomain.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "uri"
|
|
4
4
|
|
|
5
5
|
module Clowk
|
|
6
6
|
class Subdomain
|
|
7
|
-
API_BASE_URL = 'https://api.clowk.dev/api/v1'
|
|
8
7
|
CACHE_TTL = 60
|
|
9
|
-
DEFAULT_SUBDOMAIN_BASE =
|
|
8
|
+
DEFAULT_SUBDOMAIN_BASE = "clowk.dev"
|
|
9
|
+
|
|
10
|
+
@cache_mutex = Mutex.new
|
|
10
11
|
|
|
11
12
|
class << self
|
|
12
13
|
def resolve_url!(...)
|
|
@@ -14,24 +15,28 @@ module Clowk
|
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def clear_cache!
|
|
17
|
-
@cache = {}
|
|
18
|
+
@cache_mutex.synchronize { @cache = {} }
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def read_cache(key)
|
|
21
|
-
|
|
22
|
+
@cache_mutex.synchronize do
|
|
23
|
+
entry = cache[key]
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
return unless entry
|
|
26
|
+
return entry[:value] if entry[:expires_at] > Time.now
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
cache.delete(key)
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
def write_cache(key, value, ttl:)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
@cache_mutex.synchronize do
|
|
35
|
+
cache[key] = {
|
|
36
|
+
value: value,
|
|
37
|
+
expires_at: Time.now + ttl
|
|
38
|
+
}
|
|
39
|
+
end
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
private
|
|
@@ -50,7 +55,7 @@ module Clowk
|
|
|
50
55
|
return resolve_from_key if publishable_key.present?
|
|
51
56
|
return normalize_url(subdomain_url) if subdomain_url.present?
|
|
52
57
|
|
|
53
|
-
raise ConfigurationError,
|
|
58
|
+
raise ConfigurationError, "set publishable_key or subdomain_url to build Clowk URLs"
|
|
54
59
|
end
|
|
55
60
|
|
|
56
61
|
private
|
|
@@ -64,7 +69,7 @@ module Clowk
|
|
|
64
69
|
response = client.subdomains.find_by_pk(publishable_key)
|
|
65
70
|
resolved = extract_url_from_instance(response.body_parsed)
|
|
66
71
|
|
|
67
|
-
raise ConfigurationError,
|
|
72
|
+
raise ConfigurationError, "could not resolve subdomain_url from publishable_key" if resolved.blank?
|
|
68
73
|
|
|
69
74
|
self.class.write_cache(cache_key, resolved, ttl: CACHE_TTL)
|
|
70
75
|
resolved
|
|
@@ -78,27 +83,27 @@ module Clowk
|
|
|
78
83
|
return if payload.blank?
|
|
79
84
|
|
|
80
85
|
root = payload.is_a?(Hash) ? payload : {}
|
|
81
|
-
instance_data = if root[
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
explicit_url = instance_data[
|
|
86
|
+
instance_data = if root["instance"].is_a?(Hash)
|
|
87
|
+
root["instance"]
|
|
88
|
+
elsif root["data"].is_a?(Hash)
|
|
89
|
+
root["data"]
|
|
90
|
+
else
|
|
91
|
+
root
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
explicit_url = instance_data["url"] || instance_data["subdomain_url"] || instance_data["instance_url"]
|
|
90
95
|
return normalize_url(explicit_url) if explicit_url.present?
|
|
91
96
|
|
|
92
|
-
host = instance_data[
|
|
97
|
+
host = instance_data["host"] || instance_data["domain"] || instance_data["hostname"]
|
|
93
98
|
return normalize_url(host_to_url(host)) if host.present?
|
|
94
99
|
|
|
95
|
-
subdomain = instance_data[
|
|
100
|
+
subdomain = instance_data["subdomain"]
|
|
96
101
|
normalize_url("https://#{subdomain}.#{default_subdomain_base}") if subdomain.present?
|
|
97
102
|
end
|
|
98
103
|
|
|
99
104
|
def host_to_url(host)
|
|
100
105
|
value = host.to_s
|
|
101
|
-
return value if value.start_with?(
|
|
106
|
+
return value if value.start_with?("http://", "https://")
|
|
102
107
|
|
|
103
108
|
"https://#{value}"
|
|
104
109
|
end
|
|
@@ -110,17 +115,17 @@ module Clowk
|
|
|
110
115
|
uri = URI.parse(configured)
|
|
111
116
|
return DEFAULT_SUBDOMAIN_BASE if uri.host.blank?
|
|
112
117
|
|
|
113
|
-
uri.host.split(
|
|
118
|
+
uri.host.split(".").drop(1).join(".")
|
|
114
119
|
rescue URI::InvalidURIError
|
|
115
120
|
DEFAULT_SUBDOMAIN_BASE
|
|
116
121
|
end
|
|
117
122
|
|
|
118
123
|
def normalize_url(value)
|
|
119
|
-
value.to_s.sub(%r{/$},
|
|
124
|
+
value.to_s.sub(%r{/$}, "")
|
|
120
125
|
end
|
|
121
126
|
|
|
122
127
|
def client
|
|
123
|
-
@client ||= Clowk::SDK::Client.new(api_base_url:
|
|
128
|
+
@client ||= Clowk::SDK::Client.new(api_base_url: Clowk.config.api_base_url)
|
|
124
129
|
end
|
|
125
130
|
end
|
|
126
131
|
end
|
data/lib/clowk/version.rb
CHANGED
data/lib/clowk.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
3
|
+
require "rails"
|
|
4
|
+
require "rails/engine"
|
|
5
|
+
require "action_controller/railtie"
|
|
6
|
+
require "active_support"
|
|
7
|
+
require "active_support/core_ext/hash"
|
|
8
|
+
require "active_support/core_ext/object/blank"
|
|
9
|
+
require "rack"
|
|
10
10
|
|
|
11
|
-
require_relative
|
|
12
|
-
require_relative
|
|
11
|
+
require_relative "clowk/version"
|
|
12
|
+
require_relative "clowk/configuration"
|
|
13
13
|
|
|
14
14
|
module Clowk
|
|
15
15
|
class Error < StandardError; end
|
|
@@ -24,6 +24,7 @@ module Clowk
|
|
|
24
24
|
|
|
25
25
|
def configure
|
|
26
26
|
yield(config)
|
|
27
|
+
config.validate!
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def reset!
|
|
@@ -33,25 +34,25 @@ module Clowk
|
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
|
|
36
|
-
require_relative
|
|
37
|
-
require_relative
|
|
38
|
-
require_relative
|
|
39
|
-
require_relative
|
|
40
|
-
require_relative
|
|
41
|
-
require_relative
|
|
42
|
-
require_relative
|
|
43
|
-
require_relative
|
|
44
|
-
require_relative
|
|
45
|
-
require_relative
|
|
46
|
-
require_relative
|
|
47
|
-
require_relative
|
|
48
|
-
require_relative
|
|
49
|
-
require_relative
|
|
50
|
-
require_relative
|
|
51
|
-
require_relative
|
|
52
|
-
require_relative
|
|
53
|
-
require_relative
|
|
54
|
-
require_relative
|
|
55
|
-
require_relative
|
|
56
|
-
require_relative
|
|
57
|
-
require_relative
|
|
37
|
+
require_relative "clowk/current"
|
|
38
|
+
require_relative "clowk/http/response"
|
|
39
|
+
require_relative "clowk/http/logger_middleware"
|
|
40
|
+
require_relative "clowk/http/retry_middleware"
|
|
41
|
+
require_relative "clowk/http/timeout_middleware"
|
|
42
|
+
require_relative "clowk/http/client"
|
|
43
|
+
require_relative "clowk/sdk/resource"
|
|
44
|
+
require_relative "clowk/sdk/user"
|
|
45
|
+
require_relative "clowk/sdk/session"
|
|
46
|
+
require_relative "clowk/sdk/subdomain"
|
|
47
|
+
require_relative "clowk/sdk/token"
|
|
48
|
+
require_relative "clowk/sdk/session_config"
|
|
49
|
+
require_relative "clowk/sdk/client"
|
|
50
|
+
require_relative "clowk/subdomain"
|
|
51
|
+
require_relative "clowk/jwt_verifier"
|
|
52
|
+
require_relative "clowk/helpers/url_helpers"
|
|
53
|
+
require_relative "clowk/middleware/token_extractor"
|
|
54
|
+
require_relative "clowk/authenticable"
|
|
55
|
+
require_relative "clowk/controllers/base_controller"
|
|
56
|
+
require_relative "clowk/controllers/callbacks_controller"
|
|
57
|
+
require_relative "clowk/controllers/sessions_controller"
|
|
58
|
+
require_relative "clowk/engine"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clowk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Clowk
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -77,6 +77,20 @@ dependencies:
|
|
|
77
77
|
- - "<"
|
|
78
78
|
- !ruby/object:Gem::Version
|
|
79
79
|
version: '4.0'
|
|
80
|
+
- !ruby/object:Gem::Dependency
|
|
81
|
+
name: standard
|
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '1.0'
|
|
87
|
+
type: :development
|
|
88
|
+
prerelease: false
|
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '1.0'
|
|
80
94
|
description: Clowk Authentication, JWT verification, and future API access
|
|
81
95
|
email:
|
|
82
96
|
- support@clowk.in
|
|
@@ -114,7 +128,7 @@ files:
|
|
|
114
128
|
- lib/clowk/version.rb
|
|
115
129
|
homepage: https://clowk.in
|
|
116
130
|
licenses:
|
|
117
|
-
- AGPL-3.0
|
|
131
|
+
- AGPL-3.0-only
|
|
118
132
|
metadata:
|
|
119
133
|
rubygems_mfa_required: 'true'
|
|
120
134
|
rdoc_options: []
|
|
@@ -131,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
131
145
|
- !ruby/object:Gem::Version
|
|
132
146
|
version: '0'
|
|
133
147
|
requirements: []
|
|
134
|
-
rubygems_version: 3.6.
|
|
148
|
+
rubygems_version: 3.6.9
|
|
135
149
|
specification_version: 4
|
|
136
150
|
summary: Rails SDK for Clowk authentication
|
|
137
151
|
test_files: []
|