clowk 0.1.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56e3503c335ec716e89e49428b66a0c4157be9cb6ff19c46095117062f4c7848
4
- data.tar.gz: 1db10f568e025bc52ea97e255f9e4cdc8064507ace6adf83e74d78d20d5b5d9e
3
+ metadata.gz: f3298d33abdf85d08387832031c8bbdb6efa36785b73c59ece84a3dec4deb064
4
+ data.tar.gz: b533eb6930465ea11188d0f2f8e23758575f113972d9aea81d5bdcb87a21af32
5
5
  SHA512:
6
- metadata.gz: 3ed1c6bb69c0644f5476374f8004ce865c1661aeec3729b42a59442ee453e6e431c23583d603ccf49b9e045ae2c0d7e9733f2482a040ef3418c069e6f88ef531
7
- data.tar.gz: 393ed6db1f3777cc402731fe16e0874d64c842ab3b5118aa49c9a79e5b208f38a660b4bbcca45ee349eccf1f1be030ac35ff6e235310edbc9e14d57cd730a24b
6
+ metadata.gz: 486ff44e3a9e781cad6c87584d40079ca43724c113432a76513078525b8df9a8d64a82c593a4be9bb7db87dc9c3bce7a6ec611c14fbefe2d7074904d8901b758
7
+ data.tar.gz: e06afce3a59cbb24d8c388837b51582c30d2597dec2e9e08337343040ca9cbe1041b2442c573436faa2c958adf3bd6b15ac3170d82447189ae235386a16a5fd6
@@ -12,6 +12,8 @@ module Clowk
12
12
  authenticate_method = :"authenticate_#{scope}!"
13
13
  signed_in_method = :"#{scope}_signed_in?"
14
14
 
15
+ enforce_session_method = :"#{scope}_enforce_session!"
16
+
15
17
  base.class_eval do
16
18
  define_method(current_method) do
17
19
  clowk_current_resource
@@ -25,6 +27,10 @@ module Clowk
25
27
  clowk_current_resource.present?
26
28
  end
27
29
 
30
+ define_method(enforce_session_method) do
31
+ clowk_enforce_session!
32
+ end
33
+
28
34
  helper_method current_method, authenticate_method, signed_in_method, :current_token if respond_to?(:helper_method)
29
35
  end
30
36
  end
@@ -48,6 +54,33 @@ module Clowk
48
54
  clowk_current_resource.present?
49
55
  end
50
56
 
57
+ def clowk_session_status
58
+ @clowk_session_status ||= resolve_session_status
59
+ end
60
+
61
+ def clowk_session_active?
62
+ clowk_session_status&.dig(:status) == 'active'
63
+ end
64
+
65
+ def clowk_enforce_session!
66
+ return if clowk_session_active?
67
+
68
+ session_info = clowk_session_status
69
+ callback = Clowk.config.on_session_expired
70
+
71
+ if callback.respond_to?(:call)
72
+ callback.call(self, session_info)
73
+
74
+ return
75
+ end
76
+
77
+ if request.format.json?
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
82
+ end
83
+
51
84
  def clowk_authenticate!
52
85
  return clowk_current_resource if clowk_signed_in?
53
86
 
@@ -96,7 +129,7 @@ module Clowk
96
129
 
97
130
  def persist_clowk_session(token, payload)
98
131
  session[Clowk.config.session_key] = {
99
- token: token,
132
+ token:,
100
133
  user: payload,
101
134
  signed_in_at: Time.now.to_i
102
135
  }
@@ -108,5 +141,24 @@ module Clowk
108
141
  secure: request.ssl?
109
142
  }
110
143
  end
144
+
145
+ def resolve_session_status
146
+ cached = stored_session&.dig('session_status') || stored_session&.dig(:session_status)
147
+
148
+ return cached&.deep_symbolize_keys if cached
149
+
150
+ resource = clowk_current_resource
151
+
152
+ return unless resource&.session_id
153
+ return unless Clowk.config.secret_key.present?
154
+
155
+ client = Clowk::SDK::Client.new(secret_key: Clowk.config.secret_key)
156
+ result = client.tokens.verify_with_session(token: current_token)
157
+ status = result&.dig(:session)
158
+
159
+ session[Clowk.config.session_key] = stored_session.merge('session_status' => status) if status && stored_session
160
+
161
+ status
162
+ end
111
163
  end
112
164
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Clowk
4
4
  class Configuration
5
- attr_accessor :api_base_url
6
5
  attr_accessor :app_base_url
7
6
  attr_accessor :after_sign_in_path
8
7
  attr_accessor :after_sign_out_path
@@ -22,9 +21,10 @@ module Clowk
22
21
  attr_accessor :session_key
23
22
  attr_accessor :subdomain_url
24
23
  attr_accessor :token_param
24
+ attr_accessor :enforce_active_session
25
+ attr_accessor :on_session_expired
25
26
 
26
27
  def initialize
27
- @api_base_url = 'https://api.clowk.dev/client/v1'
28
28
  @app_base_url = 'https://app.clowk.in'
29
29
  @after_sign_in_path = '/'
30
30
  @after_sign_out_path = '/'
@@ -41,6 +41,8 @@ module Clowk
41
41
  @session_key = :clowk
42
42
  @prefix_by = :clowk
43
43
  @token_param = :token
44
+ @enforce_active_session = false
45
+ @on_session_expired = nil
44
46
  end
45
47
  end
46
48
  end
data/lib/clowk/current.rb CHANGED
@@ -36,6 +36,10 @@ module Clowk
36
36
  attributes[:app_id]
37
37
  end
38
38
 
39
+ def session_id
40
+ attributes[:session_id]
41
+ end
42
+
39
43
  def [](key)
40
44
  attributes[key.to_sym]
41
45
  end
@@ -6,7 +6,7 @@ module Clowk
6
6
  module SDK
7
7
  class Client
8
8
  def initialize(options = {})
9
- @api_base_url = options.fetch(:api_base_url, Clowk.config.api_base_url)
9
+ @api_base_url = options.fetch(:api_base_url, nil).presence || derive_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
@@ -65,6 +65,13 @@ 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
+
68
75
  def http
69
76
  @http ||= Clowk::Http.new(
70
77
  base_url: api_base_url,
@@ -6,6 +6,19 @@ module Clowk
6
6
  def self.resource_path
7
7
  'sessions'
8
8
  end
9
+
10
+ # @param email [String] Email to search for (ILIKE match)
11
+ # @return [Clowk::Http::Response]
12
+ def search(email:)
13
+ client.get("#{self.class.resource_path}/search?email=#{ERB::Util.url_encode(email)}")
14
+ end
15
+
16
+ # Revokes a session by its session_id (clk_session_UUID)
17
+ # @param session_id [String]
18
+ # @return [Clowk::Http::Response]
19
+ def revoke(session_id)
20
+ destroy(session_id)
21
+ end
9
22
  end
10
23
  end
11
24
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Clowk
4
+ module SDK
5
+ class SessionConfig < Resource
6
+ def self.resource_path
7
+ 'session_config'
8
+ end
9
+
10
+ def fetch
11
+ response = client.get(self.class.resource_path)
12
+
13
+ response.body_parsed&.deep_symbolize_keys
14
+ end
15
+ end
16
+ end
17
+ end
@@ -10,6 +10,13 @@ module Clowk
10
10
  def verify(token:)
11
11
  client.post("#{self.class.resource_path}/verify", { token: token })
12
12
  end
13
+
14
+ def verify_with_session(token:)
15
+ response = verify(token:)
16
+ data = response.body_parsed&.dig('data') || response.body_parsed
17
+
18
+ data&.deep_symbolize_keys
19
+ end
13
20
  end
14
21
  end
15
22
  end
@@ -4,6 +4,7 @@ require 'uri'
4
4
 
5
5
  module Clowk
6
6
  class Subdomain
7
+ API_BASE_URL = 'https://api.clowk.dev/api/v1'
7
8
  CACHE_TTL = 60
8
9
  DEFAULT_SUBDOMAIN_BASE = 'clowk.dev'
9
10
 
@@ -42,7 +43,6 @@ module Clowk
42
43
 
43
44
  def initialize(options = {})
44
45
  @publishable_key = options.fetch(:publishable_key, Clowk.config.publishable_key)
45
- @api_base_url = options.fetch(:api_base_url, Clowk.config.api_base_url)
46
46
  @subdomain_url = options.fetch(:subdomain_url, Clowk.config.subdomain_url)
47
47
  end
48
48
 
@@ -55,7 +55,7 @@ module Clowk
55
55
 
56
56
  private
57
57
 
58
- attr_reader :api_base_url, :publishable_key, :subdomain_url
58
+ attr_reader :publishable_key, :subdomain_url
59
59
 
60
60
  def resolve_from_key
61
61
  cached = self.class.read_cache(cache_key)
@@ -77,8 +77,14 @@ module Clowk
77
77
  def extract_url_from_instance(payload)
78
78
  return if payload.blank?
79
79
 
80
- instance = payload.is_a?(Hash) ? payload : {}
81
- instance_data = instance['instance'].is_a?(Hash) ? instance['instance'] : instance
80
+ root = payload.is_a?(Hash) ? payload : {}
81
+ instance_data = if root['instance'].is_a?(Hash)
82
+ root['instance']
83
+ elsif root['data'].is_a?(Hash)
84
+ root['data']
85
+ else
86
+ root
87
+ end
82
88
 
83
89
  explicit_url = instance_data['url'] || instance_data['subdomain_url'] || instance_data['instance_url']
84
90
  return normalize_url(explicit_url) if explicit_url.present?
@@ -114,7 +120,7 @@ module Clowk
114
120
  end
115
121
 
116
122
  def client
117
- @client ||= Clowk::SDK::Client.new
123
+ @client ||= Clowk::SDK::Client.new(api_base_url: API_BASE_URL)
118
124
  end
119
125
  end
120
126
  end
data/lib/clowk/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clowk
4
- VERSION = '0.1.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/clowk.rb CHANGED
@@ -44,6 +44,7 @@ require_relative 'clowk/sdk/user'
44
44
  require_relative 'clowk/sdk/session'
45
45
  require_relative 'clowk/sdk/subdomain'
46
46
  require_relative 'clowk/sdk/token'
47
+ require_relative 'clowk/sdk/session_config'
47
48
  require_relative 'clowk/sdk/client'
48
49
  require_relative 'clowk/subdomain'
49
50
  require_relative 'clowk/jwt_verifier'
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.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clowk
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-03-23 00:00:00.000000000 Z
10
+ date: 2026-03-27 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -106,6 +106,7 @@ files:
106
106
  - lib/clowk/sdk/client.rb
107
107
  - lib/clowk/sdk/resource.rb
108
108
  - lib/clowk/sdk/session.rb
109
+ - lib/clowk/sdk/session_config.rb
109
110
  - lib/clowk/sdk/subdomain.rb
110
111
  - lib/clowk/sdk/token.rb
111
112
  - lib/clowk/sdk/user.rb