kinde_sdk 1.6.6 → 1.7.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/app/controllers/kinde_sdk/auth_controller.rb +96 -13
- data/kinde_api/lib/kinde_api/api/frontend/billing_api.rb +148 -0
- data/kinde_api/lib/kinde_api/api/frontend/feature_flags_api.rb +85 -0
- data/kinde_api/lib/kinde_api/api/frontend/o_auth_api.rb +241 -0
- data/kinde_api/lib/kinde_api/api/frontend/permissions_api.rb +85 -0
- data/kinde_api/lib/kinde_api/api/frontend/properties_api.rb +85 -0
- data/kinde_api/lib/kinde_api/api/frontend/roles_api.rb +85 -0
- data/kinde_api/lib/kinde_api/api/frontend/self_serve_portal_api.rb +89 -0
- data/kinde_api/lib/kinde_api/models/frontend/error.rb +230 -0
- data/kinde_api/lib/kinde_api/models/frontend/error_response.rb +221 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlement_response.rb +228 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlement_response_data.rb +229 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlement_response_data_entitlement.rb +295 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlements_response.rb +228 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlements_response_data.rb +244 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlements_response_data_entitlements_inner.rb +294 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlements_response_data_plans_inner.rb +240 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_entitlements_response_metadata.rb +230 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_feature_flags_response.rb +219 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_feature_flags_response_data.rb +222 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_feature_flags_response_data_feature_flags_inner.rb +259 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_feature_flags_response_data_feature_flags_inner_value.rb +108 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_permissions_response.rb +228 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_permissions_response_data.rb +232 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_permissions_response_data_permissions_inner.rb +240 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_permissions_response_metadata.rb +230 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_properties_response.rb +228 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_properties_response_data.rb +222 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_properties_response_data_properties_inner.rb +249 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_properties_response_data_properties_inner_value.rb +107 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_properties_response_metadata.rb +230 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_roles_response.rb +228 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_roles_response_data.rb +232 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_roles_response_data_roles_inner.rb +240 -0
- data/kinde_api/lib/kinde_api/models/frontend/get_user_roles_response_metadata.rb +230 -0
- data/kinde_api/lib/kinde_api/models/frontend/portal_link.rb +220 -0
- data/kinde_api/lib/kinde_api/models/frontend/token_error_response.rb +230 -0
- data/kinde_api/lib/kinde_api/models/frontend/token_introspect.rb +262 -0
- data/kinde_api/lib/kinde_api/models/frontend/user_profile_v2.rb +323 -0
- data/kinde_api/lib/kinde_api.rb +28 -0
- data/lib/kinde_sdk/client/entitlements.rb +86 -0
- data/lib/kinde_sdk/client/feature_flags.rb +246 -10
- data/lib/kinde_sdk/client/permissions.rb +197 -6
- data/lib/kinde_sdk/client/roles.rb +218 -0
- data/lib/kinde_sdk/client.rb +242 -3
- data/lib/kinde_sdk/configuration.rb +2 -0
- data/lib/kinde_sdk/errors.rb +7 -0
- data/lib/kinde_sdk/internal/frontend_client.rb +111 -0
- data/lib/kinde_sdk/version.rb +1 -1
- data/lib/kinde_sdk.rb +9 -2
- metadata +54 -12
@@ -1,7 +1,168 @@
|
|
1
1
|
module KindeSdk
|
2
2
|
class Client
|
3
3
|
module FeatureFlags
|
4
|
-
|
4
|
+
# Get all feature flags for the authenticated user
|
5
|
+
# Matches the JavaScript SDK API: getFlags(options?)
|
6
|
+
#
|
7
|
+
# @param options [Hash] Options for retrieving feature flags
|
8
|
+
# @option options [Boolean] :force_api (false) If true, calls the API to get fresh flags,
|
9
|
+
# otherwise extracts from token claims. Useful for ensuring latest flags but may incur additional API calls
|
10
|
+
# @option options [Symbol] :token_type (:access_token) The token type to use for soft check (:access_token or :id_token)
|
11
|
+
# @return [Array] Array of flag objects with key, value, and type
|
12
|
+
# @example
|
13
|
+
# # Soft check (from token)
|
14
|
+
# client.get_flags
|
15
|
+
# # => [{ key: "feature_a", value: true, type: "boolean" }]
|
16
|
+
#
|
17
|
+
# # Hard check (from API)
|
18
|
+
# client.get_flags(force_api: true)
|
19
|
+
# # => [{ key: "feature_a", value: true, type: "boolean" }]
|
20
|
+
def get_flags(options = {})
|
21
|
+
# Handle legacy positional argument for backward compatibility
|
22
|
+
if options.is_a?(Symbol)
|
23
|
+
options = { token_type: options }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Extract options with defaults - use member variable if not overridden
|
27
|
+
force_api = options[:force_api] || @force_api || false
|
28
|
+
token_type = options[:token_type] || :access_token
|
29
|
+
|
30
|
+
if force_api
|
31
|
+
# Hard check - call API for fresh flags
|
32
|
+
get_flags_from_api
|
33
|
+
else
|
34
|
+
# Soft check - extract from token claims
|
35
|
+
get_flags_from_token(token_type)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get a specific feature flag
|
40
|
+
# Supports both new API-style calls and legacy 3-parameter calls
|
41
|
+
#
|
42
|
+
# @param name [String] The flag name/key to retrieve
|
43
|
+
# @param options_or_opts [Hash] Options for retrieving flags OR legacy opts hash
|
44
|
+
# @param flag_type [String, nil] Legacy flag type parameter (for 3-param calls)
|
45
|
+
# @return [Hash, nil] Flag object or nil if not found
|
46
|
+
def get_flag(name, options_or_opts = {}, flag_type = nil)
|
47
|
+
# Handle legacy 3-parameter signature: get_flag(name, opts, flag_type)
|
48
|
+
if flag_type || (options_or_opts.is_a?(Hash) && options_or_opts.key?(:default_value) && !options_or_opts.key?(:force_api))
|
49
|
+
return get_flag_legacy(name, options_or_opts, flag_type)
|
50
|
+
end
|
51
|
+
|
52
|
+
get_flag_new_api(name, options_or_opts)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check if user has specific feature flags
|
56
|
+
#
|
57
|
+
# @param flag_conditions [Array] Array of flag keys or flag condition objects
|
58
|
+
# @param options [Hash] Options for retrieving flags (same as get_flags)
|
59
|
+
# @return [Boolean] True if user has all specified flags, false otherwise
|
60
|
+
def has_feature_flags?(flag_conditions, options = {})
|
61
|
+
return true if flag_conditions.nil? || flag_conditions.empty?
|
62
|
+
|
63
|
+
flags = get_flags(options)
|
64
|
+
flag_conditions_array = Array(flag_conditions)
|
65
|
+
|
66
|
+
flag_conditions_array.all? { |condition| check_flag_condition(condition, flags) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Legacy methods for backward compatibility
|
70
|
+
def get_boolean_flag(name, default_value = nil)
|
71
|
+
flag_getter_wrapper(name, "b", default_value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_string_flag(name, default_value = nil)
|
75
|
+
flag_getter_wrapper(name, "s", default_value)
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_integer_flag(name, default_value = nil)
|
79
|
+
flag_getter_wrapper(name, "i", default_value)
|
80
|
+
end
|
81
|
+
|
82
|
+
# PHP SDK compatible alias
|
83
|
+
def getFlags
|
84
|
+
# Use client's force_api setting, default to true for PHP SDK compatibility
|
85
|
+
force_api_setting = @force_api.nil? ? true : @force_api
|
86
|
+
get_flags(force_api: force_api_setting)
|
87
|
+
end
|
88
|
+
|
89
|
+
# JavaScript SDK compatible alias
|
90
|
+
alias_method :hasFeatureFlags, :has_feature_flags?
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# New API-style get_flag implementation
|
95
|
+
# Handles the modern get_flag(name, options) signature
|
96
|
+
#
|
97
|
+
# @param name [String] The flag name/key to retrieve
|
98
|
+
# @param options [Hash] Options for retrieving flags
|
99
|
+
# @return [Hash, nil] Flag object or nil if not found
|
100
|
+
def get_flag_new_api(name, options)
|
101
|
+
flags = get_flags(options)
|
102
|
+
flag = find_flag_by_key(flags, name)
|
103
|
+
|
104
|
+
return nil unless flag
|
105
|
+
|
106
|
+
{
|
107
|
+
code: get_flag_key(flag),
|
108
|
+
type: get_flag_type(flag),
|
109
|
+
value: get_flag_value(flag),
|
110
|
+
is_default: false
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
# Check a single flag condition against available flags
|
115
|
+
#
|
116
|
+
# @param condition [Hash, String] Flag condition to check
|
117
|
+
# @param flags [Array] Available flags to check against
|
118
|
+
# @return [Boolean] True if condition is met, false otherwise
|
119
|
+
def check_flag_condition(condition, flags)
|
120
|
+
if condition.is_a?(Hash) && condition.key?(:flag) && condition.key?(:value)
|
121
|
+
# Custom condition with specific value
|
122
|
+
flag = find_flag_by_key(flags, condition[:flag])
|
123
|
+
flag && get_flag_value(flag) == condition[:value]
|
124
|
+
else
|
125
|
+
# Simple existence check
|
126
|
+
!!find_flag_by_key(flags, condition.to_s)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Find a flag by its key in the flags array
|
131
|
+
#
|
132
|
+
# @param flags [Array] Array of flag objects
|
133
|
+
# @param key [String] The key to search for
|
134
|
+
# @return [Hash, nil] Flag object if found, nil otherwise
|
135
|
+
def find_flag_by_key(flags, key)
|
136
|
+
flags.find { |f| get_flag_key(f) == key.to_s }
|
137
|
+
end
|
138
|
+
|
139
|
+
# Extract the key from a flag object (handles both symbol and string keys)
|
140
|
+
#
|
141
|
+
# @param flag [Hash] Flag object
|
142
|
+
# @return [String] The flag key
|
143
|
+
def get_flag_key(flag)
|
144
|
+
flag[:key] || flag['key']
|
145
|
+
end
|
146
|
+
|
147
|
+
# Extract the value from a flag object (handles both symbol and string keys)
|
148
|
+
#
|
149
|
+
# @param flag [Hash] Flag object
|
150
|
+
# @return [Object] The flag value
|
151
|
+
def get_flag_value(flag)
|
152
|
+
flag[:value] || flag['value']
|
153
|
+
end
|
154
|
+
|
155
|
+
# Extract the type from a flag object (handles both symbol and string keys)
|
156
|
+
#
|
157
|
+
# @param flag [Hash] Flag object
|
158
|
+
# @return [String] The flag type
|
159
|
+
def get_flag_type(flag)
|
160
|
+
flag[:type] || flag['type']
|
161
|
+
end
|
162
|
+
|
163
|
+
# Legacy get_flag implementation for backward compatibility
|
164
|
+
# Handles the 3-parameter signature: get_flag(name, opts, flag_type)
|
165
|
+
def get_flag_legacy(name, opts = {}, flag_type = nil)
|
5
166
|
res = get_claim("feature_flags")&.dig(:value, name)
|
6
167
|
return try_default_flag(flag_type, name, opts) unless res
|
7
168
|
|
@@ -20,22 +181,84 @@ module KindeSdk
|
|
20
181
|
}
|
21
182
|
end
|
22
183
|
|
23
|
-
|
24
|
-
|
25
|
-
|
184
|
+
# Get feature flags from token claims (soft check)
|
185
|
+
# Matches JavaScript logic: token.feature_flags || token["x-hasura-feature-flags"] || null
|
186
|
+
#
|
187
|
+
# @param token_type [Symbol] The token type to use
|
188
|
+
# @return [Array] Array of flag objects
|
189
|
+
def get_flags_from_token(token_type = :access_token)
|
190
|
+
# First try standard feature_flags claim
|
191
|
+
flags = get_claim("feature_flags", token_type)&.dig(:value)
|
192
|
+
|
193
|
+
# Fallback to Hasura-specific flags (matches JS SDK)
|
194
|
+
if flags.nil? || flags.empty?
|
195
|
+
flags = get_claim("x-hasura-feature-flags", token_type)&.dig(:value)
|
196
|
+
end
|
197
|
+
|
198
|
+
return [] if flags.nil? || flags.empty?
|
26
199
|
|
27
|
-
|
28
|
-
|
200
|
+
# Convert from token format to consistent array format
|
201
|
+
if flags.is_a?(Hash)
|
202
|
+
flags.map do |key, value|
|
203
|
+
{
|
204
|
+
key: key.to_s,
|
205
|
+
value: value.is_a?(Hash) ? value['v'] : value,
|
206
|
+
type: value.is_a?(Hash) ? map_flag_type(value['t']) : 'string'
|
207
|
+
}
|
208
|
+
end
|
209
|
+
else
|
210
|
+
[]
|
211
|
+
end
|
29
212
|
end
|
30
213
|
|
31
|
-
|
32
|
-
|
214
|
+
# Get feature flags from API (hard check)
|
215
|
+
# Matches JavaScript API endpoint and data extraction exactly
|
216
|
+
#
|
217
|
+
# @return [Array] Array of flag objects
|
218
|
+
def get_flags_from_api
|
219
|
+
unless token_store.bearer_token
|
220
|
+
return []
|
221
|
+
end
|
222
|
+
|
223
|
+
begin
|
224
|
+
# Use the same pagination pattern as getAllEntitlements
|
225
|
+
all_flags = paginate_all_results('feature_flags') do |starting_after|
|
226
|
+
user_feature_flags(page_size: 100, starting_after: starting_after)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Extract flag data (matches JS: feature_flags?.map((flag) => ({ key, value, type })))
|
230
|
+
all_flags.map do |flag|
|
231
|
+
{
|
232
|
+
key: flag.respond_to?(:key) ? flag.key : flag['key'],
|
233
|
+
value: flag.respond_to?(:value) ? flag.value : flag['value'],
|
234
|
+
type: flag.respond_to?(:type) ? flag.type : flag['type']
|
235
|
+
}
|
236
|
+
end.compact
|
237
|
+
rescue KindeSdk::APIError => e
|
238
|
+
log_error("API Error getting feature flags: #{e.message}")
|
239
|
+
# Graceful fallback to token-based flags (matches JS behavior)
|
240
|
+
get_flags_from_token
|
241
|
+
rescue StandardError => e
|
242
|
+
log_error("Unexpected error getting feature flags from API: #{e.message}")
|
243
|
+
# Graceful fallback to token-based flags
|
244
|
+
get_flags_from_token
|
245
|
+
end
|
33
246
|
end
|
34
247
|
|
35
|
-
|
248
|
+
# Map token flag type codes to full names
|
249
|
+
def map_flag_type(type_code)
|
250
|
+
case type_code
|
251
|
+
when "b" then "boolean"
|
252
|
+
when "s" then "string"
|
253
|
+
when "i" then "integer"
|
254
|
+
when "o" then "object"
|
255
|
+
else "string"
|
256
|
+
end
|
257
|
+
end
|
36
258
|
|
259
|
+
# Legacy helper methods
|
37
260
|
def flag_getter_wrapper(name, type, default_value = nil)
|
38
|
-
v =
|
261
|
+
v = get_flag_legacy(name, { default_value: default_value }, type)[:value]
|
39
262
|
raise ArgumentError, "Flag #{name} value type is different from requested type" unless check_type(v, type)
|
40
263
|
|
41
264
|
v
|
@@ -59,6 +282,19 @@ module KindeSdk
|
|
59
282
|
def check_type(value, type)
|
60
283
|
type == "s" && value.is_a?(String) || type == "b" && (value == false || value == true) || type == "i" && value.is_a?(Integer)
|
61
284
|
end
|
285
|
+
|
286
|
+
# Configurable logging that works with or without Rails
|
287
|
+
def log_error(message)
|
288
|
+
if defined?(Rails) && Rails.logger
|
289
|
+
Rails.logger.error(message)
|
290
|
+
elsif @logger
|
291
|
+
@logger.error(message)
|
292
|
+
elsif respond_to?(:logger) && logger
|
293
|
+
logger.error(message)
|
294
|
+
else
|
295
|
+
$stderr.puts "[KindeSdk] ERROR: #{message}"
|
296
|
+
end
|
297
|
+
end
|
62
298
|
end
|
63
299
|
end
|
64
300
|
end
|
@@ -1,19 +1,210 @@
|
|
1
1
|
module KindeSdk
|
2
2
|
class Client
|
3
3
|
module Permissions
|
4
|
-
|
4
|
+
# Get all permissions for the authenticated user
|
5
|
+
# Matches the JavaScript SDK API: getPermissions(options?)
|
6
|
+
#
|
7
|
+
# @param options [Hash, Symbol] Options for retrieving permissions, or legacy token_type symbol
|
8
|
+
# @option options [Boolean] :force_api (false) If true, calls the API to get fresh permissions,
|
9
|
+
# otherwise extracts from token claims. Useful for ensuring latest permissions but may incur additional API calls
|
10
|
+
# @option options [Symbol] :token_type (:access_token) The token type to use for soft check (:access_token or :id_token)
|
11
|
+
# @return [Hash] Hash containing org_code and permissions array
|
12
|
+
# @example
|
13
|
+
# # Soft check (from token)
|
14
|
+
# client.get_permissions
|
15
|
+
# # => { org_code: "org_123", permissions: ["read:users", "write:posts"] }
|
16
|
+
#
|
17
|
+
# # Hard check (from API)
|
18
|
+
# client.get_permissions(force_api: true)
|
19
|
+
# # => { org_code: "org_123", permissions: ["read:users", "write:posts", "admin:all"] }
|
20
|
+
#
|
21
|
+
# # Legacy backward compatibility
|
22
|
+
# client.get_permissions(:id_token)
|
23
|
+
# # => { org_code: "org_123", permissions: ["read:users", "write:posts"] }
|
24
|
+
def get_permissions(options = {})
|
25
|
+
# Handle legacy positional argument for backward compatibility
|
26
|
+
if options.is_a?(Symbol)
|
27
|
+
options = { token_type: options }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Extract options with defaults - use member variable if not overridden
|
31
|
+
force_api = options[:force_api] || @force_api || false
|
32
|
+
token_type = options[:token_type] || :access_token
|
33
|
+
|
34
|
+
if force_api
|
35
|
+
# Hard check - call API for fresh permissions
|
36
|
+
get_permissions_from_api
|
37
|
+
else
|
38
|
+
# Soft check - extract from token claims
|
39
|
+
get_permissions_from_token(token_type)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get a specific permission status
|
44
|
+
#
|
45
|
+
# @param permission [String] The permission key to check
|
46
|
+
# @param options [Hash] Options for retrieving permissions (same as get_permissions)
|
47
|
+
# @return [Hash] Hash containing org_code and is_granted status
|
48
|
+
def get_permission(permission, options = {})
|
49
|
+
permissions_data = get_permissions(options)
|
50
|
+
|
51
|
+
{
|
52
|
+
org_code: permissions_data[:org_code],
|
53
|
+
is_granted: permissions_data[:permissions]&.include?(permission) || false
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Check if a permission is granted
|
58
|
+
#
|
59
|
+
# @param permission [String] The permission key to check
|
60
|
+
# @param options [Hash] Options for retrieving permissions
|
61
|
+
# @return [Boolean] True if permission is granted, false otherwise
|
62
|
+
def permission_granted?(permission, options = {})
|
63
|
+
get_permission(permission, options)[:is_granted]
|
64
|
+
end
|
65
|
+
|
66
|
+
# PHP SDK compatible alias for get_permissions with hard check
|
67
|
+
# Matches PHP: $client->getPermissions()
|
68
|
+
#
|
69
|
+
# @return [Hash] Hash containing org_code and permissions array
|
70
|
+
def getPermissions
|
71
|
+
# Use client's force_api setting, default to true for PHP SDK compatibility
|
72
|
+
force_api_setting = @force_api.nil? ? true : @force_api
|
73
|
+
get_permissions(force_api: force_api_setting)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get all permissions with automatic pagination (hard check)
|
77
|
+
# Matches PHP: $client->getAllPermissions()
|
78
|
+
#
|
79
|
+
# @return [Array] Array of permission keys
|
80
|
+
def getAllPermissions
|
81
|
+
# Use client's force_api setting, default to true for PHP SDK compatibility
|
82
|
+
force_api_setting = @force_api.nil? ? true : @force_api
|
83
|
+
permissions_data = get_permissions(force_api: force_api_setting)
|
84
|
+
permissions_data[:permissions] || []
|
85
|
+
end
|
86
|
+
|
87
|
+
# JavaScript SDK compatible alias
|
88
|
+
alias_method :all_permissions, :getAllPermissions
|
89
|
+
|
90
|
+
# Backward compatibility method - matches existing Ruby SDK API
|
91
|
+
def get_permissions_legacy(token_type = :access_token)
|
5
92
|
get_claim("permissions", token_type)&.dig(:value)
|
6
93
|
end
|
7
94
|
|
8
|
-
|
95
|
+
private
|
96
|
+
|
97
|
+
# Get permissions from token claims (soft check)
|
98
|
+
# Matches JavaScript logic exactly: token.permissions || token["x-hasura-permissions"] || []
|
99
|
+
#
|
100
|
+
# @param token_type [Symbol] The token type to use
|
101
|
+
# @return [Hash] Hash containing org_code and permissions array
|
102
|
+
def get_permissions_from_token(token_type = :access_token)
|
103
|
+
# First try standard permissions claim
|
104
|
+
permissions = get_claim("permissions", token_type)&.dig(:value)
|
105
|
+
|
106
|
+
# Fallback to Hasura-specific permissions (matches JS SDK)
|
107
|
+
if permissions.nil? || permissions.empty?
|
108
|
+
permissions = get_claim("x-hasura-permissions", token_type)&.dig(:value)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Final fallback to empty array
|
112
|
+
permissions ||= []
|
113
|
+
|
114
|
+
# Get org_code with same fallback pattern
|
115
|
+
org_code = get_claim("org_code", token_type)&.dig(:value)
|
116
|
+
if org_code.nil?
|
117
|
+
org_code = get_claim("x-hasura-org-code", token_type)&.dig(:value)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Log warning if no permissions found (helpful for debugging)
|
121
|
+
if permissions.empty?
|
122
|
+
log_warning("No permissions found in token. This may be expected if user has no permissions assigned.")
|
123
|
+
end
|
124
|
+
|
9
125
|
{
|
10
|
-
org_code:
|
11
|
-
|
126
|
+
org_code: org_code,
|
127
|
+
permissions: permissions
|
12
128
|
}
|
13
129
|
end
|
14
130
|
|
15
|
-
|
16
|
-
|
131
|
+
# Get permissions from API (hard check)
|
132
|
+
# Matches JavaScript API endpoint and data extraction exactly
|
133
|
+
#
|
134
|
+
# @return [Hash] Hash containing org_code and permissions array
|
135
|
+
def get_permissions_from_api
|
136
|
+
unless token_store.bearer_token
|
137
|
+
return {
|
138
|
+
org_code: nil,
|
139
|
+
permissions: []
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
begin
|
144
|
+
# Use the same pagination pattern as getAllEntitlements
|
145
|
+
all_permissions = paginate_all_results('permissions') do |starting_after|
|
146
|
+
user_permissions(page_size: 100, starting_after: starting_after)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Extract permission keys (matches JS: data.permissions?.map((permission) => permission.key))
|
150
|
+
permission_keys = all_permissions.map do |permission|
|
151
|
+
# Handle both OpenStruct and Hash responses
|
152
|
+
permission.respond_to?(:key) ? permission.key : permission['key']
|
153
|
+
end.compact
|
154
|
+
|
155
|
+
# Extract org_code from API response or fallback to token
|
156
|
+
org_code = nil
|
157
|
+
if all_permissions.any?
|
158
|
+
first_permission = all_permissions.first
|
159
|
+
org_code = first_permission.respond_to?(:org_code) ?
|
160
|
+
first_permission.org_code :
|
161
|
+
first_permission['org_code']
|
162
|
+
end
|
163
|
+
|
164
|
+
# Fallback to token if API doesn't provide org_code
|
165
|
+
org_code ||= get_claim("org_code", :access_token)&.dig(:value)
|
166
|
+
|
167
|
+
{
|
168
|
+
org_code: org_code,
|
169
|
+
permissions: permission_keys
|
170
|
+
}
|
171
|
+
rescue KindeSdk::APIError => e
|
172
|
+
log_error("API Error getting permissions: #{e.message}")
|
173
|
+
# Graceful fallback to token-based permissions (matches JS behavior)
|
174
|
+
get_permissions_from_token
|
175
|
+
rescue StandardError => e
|
176
|
+
log_error("Unexpected error getting permissions from API: #{e.message}")
|
177
|
+
# Graceful fallback to token-based permissions
|
178
|
+
get_permissions_from_token
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Configurable logging that works with or without Rails
|
183
|
+
#
|
184
|
+
# @param message [String] The error message to log
|
185
|
+
def log_error(message)
|
186
|
+
if defined?(Rails) && Rails.logger
|
187
|
+
Rails.logger.error(message)
|
188
|
+
elsif @logger
|
189
|
+
@logger.error(message)
|
190
|
+
elsif respond_to?(:logger) && logger
|
191
|
+
logger.error(message)
|
192
|
+
else
|
193
|
+
# Fallback to STDERR if no logger available
|
194
|
+
$stderr.puts "[KindeSdk] ERROR: #{message}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def log_warning(message)
|
199
|
+
if defined?(Rails) && Rails.logger
|
200
|
+
Rails.logger.warn(message)
|
201
|
+
elsif @logger
|
202
|
+
@logger.warn(message)
|
203
|
+
elsif respond_to?(:logger) && logger
|
204
|
+
logger.warn(message)
|
205
|
+
else
|
206
|
+
$stderr.puts "[KindeSdk] WARNING: #{message}"
|
207
|
+
end
|
17
208
|
end
|
18
209
|
end
|
19
210
|
end
|