clowk 0.3.0 → 0.3.3
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 +42 -24
- data/lib/clowk/configuration.rb +40 -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 +3 -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: 7b02c176f355917aa070a2b183a5844911e769b49ff7906cc40c13bbcd68cbf6
|
|
4
|
+
data.tar.gz: 7ba591ce54755e2ff3d6a586ef7942ae96442efa6ae93caff5c64e0ac313cbd1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f33e323f94c88fc064d12d0caa0cbc87bf9fad301ba0e29fc89d51ec0f40e81658e7dc67f485e437281705b0cdd10c6157b927500d0afd255f151d48485188ad
|
|
7
|
+
data.tar.gz: 9c6e02049ac043a85e293ad4ac8ec060d4f90e2b1231931cb593a6287a73b1400bdb6c4c01e18cf20fe090cf5be43336eeade52bb495c8d24074878b0a4c688f
|
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
|
|
@@ -15,20 +15,28 @@ module Clowk
|
|
|
15
15
|
enforce_session_method = :"#{scope}_enforce_session!"
|
|
16
16
|
|
|
17
17
|
base.class_eval do
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
unless current_method == :clowk_current_resource
|
|
19
|
+
define_method(current_method) do
|
|
20
|
+
clowk_current_resource
|
|
21
|
+
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
unless authenticate_method == :clowk_authenticate!
|
|
25
|
+
define_method(authenticate_method) do
|
|
26
|
+
clowk_authenticate!
|
|
27
|
+
end
|
|
24
28
|
end
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
unless signed_in_method == :clowk_signed_in?
|
|
31
|
+
define_method(signed_in_method) do
|
|
32
|
+
clowk_signed_in?
|
|
33
|
+
end
|
|
28
34
|
end
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
unless enforce_session_method == :clowk_enforce_session!
|
|
37
|
+
define_method(enforce_session_method) do
|
|
38
|
+
clowk_enforce_session!
|
|
39
|
+
end
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
helper_method current_method, authenticate_method, signed_in_method, :current_token if respond_to?(:helper_method)
|
|
@@ -47,7 +55,7 @@ module Clowk
|
|
|
47
55
|
end
|
|
48
56
|
|
|
49
57
|
def current_token
|
|
50
|
-
stored_session&.dig(
|
|
58
|
+
stored_session&.dig("token") || extracted_token
|
|
51
59
|
end
|
|
52
60
|
|
|
53
61
|
def clowk_signed_in?
|
|
@@ -59,7 +67,7 @@ module Clowk
|
|
|
59
67
|
end
|
|
60
68
|
|
|
61
69
|
def clowk_session_active?
|
|
62
|
-
clowk_session_status&.dig(:status) ==
|
|
70
|
+
clowk_session_status&.dig(:status) == "active"
|
|
63
71
|
end
|
|
64
72
|
|
|
65
73
|
def clowk_enforce_session!
|
|
@@ -74,21 +82,13 @@ module Clowk
|
|
|
74
82
|
return
|
|
75
83
|
end
|
|
76
84
|
|
|
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
|
|
85
|
+
clowk_handle_expired_session(session_info)
|
|
82
86
|
end
|
|
83
87
|
|
|
84
88
|
def clowk_authenticate!
|
|
85
89
|
return clowk_current_resource if clowk_signed_in?
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
render json: { error: 'Unauthorized' }, status: :unauthorized
|
|
89
|
-
else
|
|
90
|
-
redirect_to clowk_sign_in_path(return_to: request.fullpath)
|
|
91
|
-
end
|
|
91
|
+
clowk_handle_unauthenticated
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def clowk_sign_out!
|
|
@@ -100,6 +100,22 @@ module Clowk
|
|
|
100
100
|
|
|
101
101
|
private
|
|
102
102
|
|
|
103
|
+
def clowk_handle_unauthenticated
|
|
104
|
+
if request.format.json?
|
|
105
|
+
render json: {error: "Unauthorized"}, status: :unauthorized
|
|
106
|
+
else
|
|
107
|
+
redirect_to clowk_sign_in_path(return_to: request.fullpath)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def clowk_handle_expired_session(_session_info)
|
|
112
|
+
if request.format.json?
|
|
113
|
+
render json: {error: "Session expired or inactive"}, status: :unauthorized
|
|
114
|
+
else
|
|
115
|
+
redirect_to clowk_sign_in_path(return_to: request.fullpath)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
103
119
|
def verified_request_payload
|
|
104
120
|
return unless extracted_token
|
|
105
121
|
|
|
@@ -123,7 +139,7 @@ module Clowk
|
|
|
123
139
|
end
|
|
124
140
|
|
|
125
141
|
def stored_user_payload
|
|
126
|
-
payload = stored_session&.dig(
|
|
142
|
+
payload = stored_session&.dig("user") || stored_session&.dig(:user)
|
|
127
143
|
payload&.deep_symbolize_keys
|
|
128
144
|
end
|
|
129
145
|
|
|
@@ -143,7 +159,7 @@ module Clowk
|
|
|
143
159
|
end
|
|
144
160
|
|
|
145
161
|
def resolve_session_status
|
|
146
|
-
cached = stored_session&.dig(
|
|
162
|
+
cached = stored_session&.dig("session_status") || stored_session&.dig(:session_status)
|
|
147
163
|
|
|
148
164
|
return cached&.deep_symbolize_keys if cached
|
|
149
165
|
|
|
@@ -156,9 +172,11 @@ module Clowk
|
|
|
156
172
|
result = client.tokens.verify_with_session(token: current_token)
|
|
157
173
|
status = result&.dig(:session)
|
|
158
174
|
|
|
159
|
-
session[Clowk.config.session_key] = stored_session.merge(
|
|
175
|
+
session[Clowk.config.session_key] = stored_session.merge("session_status" => status) if status && stored_session
|
|
160
176
|
|
|
161
177
|
status
|
|
178
|
+
rescue Clowk::InvalidTokenError
|
|
179
|
+
nil
|
|
162
180
|
end
|
|
163
181
|
end
|
|
164
182
|
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
|
|
@@ -25,24 +24,56 @@ module Clowk
|
|
|
25
24
|
attr_accessor :on_session_expired
|
|
26
25
|
|
|
27
26
|
def initialize
|
|
28
|
-
@
|
|
29
|
-
@
|
|
30
|
-
@
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@
|
|
27
|
+
@api_base_url = "https://api.clowk.dev/api/v1"
|
|
28
|
+
@app_base_url = "https://app.clowk.in"
|
|
29
|
+
@after_sign_in_path = "/"
|
|
30
|
+
@after_sign_out_path = "/"
|
|
31
|
+
@mount_path = "/clowk"
|
|
32
|
+
@callback_path = "/clowk/oauth/callback"
|
|
33
|
+
@cookie_key = "clowk_token"
|
|
34
34
|
@http_logger = nil
|
|
35
35
|
@http_open_timeout = 5
|
|
36
36
|
@http_read_timeout = 10
|
|
37
37
|
@http_retry_attempts = 2
|
|
38
38
|
@http_retry_interval = 0.05
|
|
39
39
|
@http_write_timeout = 10
|
|
40
|
-
@issuer =
|
|
40
|
+
@issuer = "clowk"
|
|
41
41
|
@session_key = :clowk
|
|
42
42
|
@prefix_by = :clowk
|
|
43
43
|
@token_param = :token
|
|
44
44
|
@enforce_active_session = false
|
|
45
45
|
@on_session_expired = nil
|
|
46
46
|
end
|
|
47
|
+
|
|
48
|
+
def after_sign_in_path
|
|
49
|
+
resolve_path(@after_sign_in_path)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def after_sign_out_path
|
|
53
|
+
resolve_path(@after_sign_out_path)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
attr_writer :after_sign_in_path
|
|
57
|
+
|
|
58
|
+
attr_writer :after_sign_out_path
|
|
59
|
+
|
|
60
|
+
def validate!
|
|
61
|
+
errors = []
|
|
62
|
+
errors << "secret_key must be a String" unless @secret_key.is_a?(String) || @secret_key.nil?
|
|
63
|
+
errors << "http_open_timeout must be Numeric" unless @http_open_timeout.is_a?(Numeric)
|
|
64
|
+
errors << "http_read_timeout must be Numeric" unless @http_read_timeout.is_a?(Numeric)
|
|
65
|
+
errors << "http_write_timeout must be Numeric" unless @http_write_timeout.is_a?(Numeric)
|
|
66
|
+
errors << "http_retry_attempts must be a non-negative Integer" unless @http_retry_attempts.is_a?(Integer) && @http_retry_attempts >= 0
|
|
67
|
+
|
|
68
|
+
raise ConfigurationError, errors.join(", ") if errors.any?
|
|
69
|
+
|
|
70
|
+
true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def resolve_path(path_or_callable)
|
|
76
|
+
path_or_callable.respond_to?(:call) ? path_or_callable.call : path_or_callable
|
|
77
|
+
end
|
|
47
78
|
end
|
|
48
79
|
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
|
|
|
@@ -18,7 +18,7 @@ module Clowk
|
|
|
18
18
|
attr_reader :request, :token_param, :cookie_key
|
|
19
19
|
|
|
20
20
|
def token_from_params
|
|
21
|
-
params = request.respond_to?(:params) && request.params ? request.params : {}
|
|
21
|
+
params = (request.respond_to?(:params) && request.params) ? request.params : {}
|
|
22
22
|
params[token_param.to_s].presence
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -26,8 +26,8 @@ module Clowk
|
|
|
26
26
|
header = request.authorization.to_s
|
|
27
27
|
return if header.empty?
|
|
28
28
|
|
|
29
|
-
scheme, token = header.split(
|
|
30
|
-
return unless scheme.to_s.casecmp(
|
|
29
|
+
scheme, token = header.split(" ", 2)
|
|
30
|
+
return unless scheme.to_s.casecmp("Bearer").zero?
|
|
31
31
|
|
|
32
32
|
token.presence
|
|
33
33
|
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.3.
|
|
4
|
+
version: 0.3.3
|
|
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: []
|