Class: KindeSdk::Client

Inherits:
Object
  • Object
show all
Includes:
Entitlements, FeatureFlags, Permissions, Roles
Defined in:
lib/kinde_sdk/client.rb,
lib/kinde_sdk/client/roles.rb,
lib/kinde_sdk/client/permissions.rb,
lib/kinde_sdk/client/entitlements.rb,
lib/kinde_sdk/client/feature_flags.rb

Defined Under Namespace

Modules: Entitlements, FeatureFlags, Permissions, Roles

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Entitlements

#get_entitlements, #has_billing_entitlements?, #has_entitlements?

Methods included from Roles

#getRoles, #get_roles, #has_roles?

Methods included from Permissions

#getAllPermissions, #getPermissions, #get_permission, #get_permissions, #get_permissions_legacy, #permission_granted?

Methods included from FeatureFlags

#getFlags, #get_boolean_flag, #get_flag, #get_flags, #get_integer_flag, #get_string_flag, #has_feature_flags?

Constructor Details

#initialize(sdk_api_client, tokens_hash, auto_refresh_tokens, force_api = false) ⇒ Client

Returns a new instance of Client.



31
32
33
34
35
36
37
38
39
# File 'lib/kinde_sdk/client.rb', line 31

def initialize(sdk_api_client, tokens_hash, auto_refresh_tokens, force_api = false)
  @kinde_api_client = sdk_api_client
  @auto_refresh_tokens = auto_refresh_tokens
  @token_store = TokenManager.create_store(tokens_hash)
  @force_api = force_api

  # refresh the token if it's expired and auto_refresh_tokens is enabled
  refresh_token if auto_refresh_tokens && TokenManager.token_expired?(@token_store)
end

Instance Attribute Details

#auto_refresh_tokensObject

Returns the value of attribute auto_refresh_tokens.



29
30
31
# File 'lib/kinde_sdk/client.rb', line 29

def auto_refresh_tokens
  @auto_refresh_tokens
end

#force_apiObject

Returns the value of attribute force_api.



29
30
31
# File 'lib/kinde_sdk/client.rb', line 29

def force_api
  @force_api
end

#kinde_api_clientObject

Returns the value of attribute kinde_api_client.



29
30
31
# File 'lib/kinde_sdk/client.rb', line 29

def kinde_api_client
  @kinde_api_client
end

#token_storeObject

Returns the value of attribute token_store.



29
30
31
# File 'lib/kinde_sdk/client.rb', line 29

def token_store
  @token_store
end

Instance Method Details

#bearer_tokenString

Returns the bearer token for backwards compatibility

Returns:

  • (String)


43
44
45
# File 'lib/kinde_sdk/client.rb', line 43

def bearer_token
  @token_store.bearer_token
end

#enhanced_user_profileOpenStruct

Get enhanced user profile information.

Returns:

  • (OpenStruct)

    Enhanced user profile data

Raises:



358
359
360
361
362
363
# File 'lib/kinde_sdk/client.rb', line 358

def 
  frontend.
rescue StandardError => e
  Rails.logger.error("Failed to fetch enhanced profile: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch enhanced profile: #{e.message}"
end

#entitlement(key) ⇒ OpenStruct?

Get a specific entitlement by key.

Parameters:

  • key (String)

    The entitlement key to retrieve

Returns:

  • (OpenStruct, nil)

    The entitlement response or nil if not found

Raises:



206
207
208
209
210
211
# File 'lib/kinde_sdk/client.rb', line 206

def entitlement(key)
  frontend.get_entitlement(key)
rescue StandardError => e
  Rails.logger.error("Failed to fetch entitlement for #{key}: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch entitlement: #{e.message}"
end

#entitlements(page_size: 10, starting_after: nil) ⇒ OpenStruct

Get entitlements for the authenticated user with pagination support.

Parameters:

  • page_size (Integer) (defaults to: 10)

    Number of results per page (default: 10)

  • starting_after (String) (defaults to: nil)

    The ID to start after for pagination

Returns:

  • (OpenStruct)

    Response containing entitlements data

Raises:



176
177
178
179
180
181
# File 'lib/kinde_sdk/client.rb', line 176

def entitlements(page_size: 10, starting_after: nil)
  frontend.get_entitlements(page_size: page_size, starting_after: starting_after)
rescue StandardError => e
  Rails.logger.error("Failed to fetch entitlements: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch entitlements: #{e.message}"
end

#expires_atInteger

Returns the token expiration time for backwards compatibility

Returns:

  • (Integer)


55
56
57
# File 'lib/kinde_sdk/client.rb', line 55

def expires_at
  @token_store.expires_at
end

#frontendObject

Frontend API access - Internal use only Uses user access tokens instead of M2M tokens



164
165
166
# File 'lib/kinde_sdk/client.rb', line 164

def frontend
  @frontend_client ||= KindeSdk::Internal::FrontendClient.new(@token_store, KindeSdk.config.domain)
end

#generate_portal_url(domain:, return_url:, sub_nav: PortalPage::PROFILE) ⇒ Hash

Generate a URL to the user profile portal

Parameters:

  • domain (String)

    The domain of the Kinde instance

  • return_url (String)

    URL to redirect to after completing the profile flow

  • sub_nav (String) (defaults to: PortalPage::PROFILE)

    Sub-navigation section to display

Returns:

  • (Hash)

    A hash containing the generated URL

Raises:

  • (StandardError)

    If the request fails or returns invalid data



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/kinde_sdk/client.rb', line 95

def generate_portal_url(domain:, return_url:, sub_nav: PortalPage::PROFILE)
  refresh_token if auto_refresh_tokens && token_expired?

  unless return_url.start_with?('http')
    raise StandardError, 'generatePortalUrl: returnUrl must be an absolute URL'
  end

  params = {
    'return_url' => return_url,
    'sub_nav' => sub_nav
  }

  response = Faraday.get("#{domain}/account_api/v1/portal_link", params) do |req|
    req.headers['Authorization'] = "Bearer #{@token_store.bearer_token}"
  end

  unless response.success?
    raise StandardError, "Failed to fetch profile URL: #{response.status} #{response.reason_phrase}"
  end

  result = JSON.parse(response.body)
  unless result['url'].is_a?(String)
    raise StandardError, "Invalid URL received from API"
  end

  begin
    { url: URI.parse(result['url']) }
  rescue URI::InvalidURIError => e
    Rails.logger.error(e)
    raise StandardError, "Invalid URL format received from API: #{result['url']}"
  end
end

#generatePortalUrl(return_url, sub_nav = PortalPage::PROFILE) ⇒ Hash

PHP SDK compatible portal URL generation method.

Parameters:

  • return_url (String)

    URL to redirect to after completing the profile flow

  • sub_nav (String) (defaults to: PortalPage::PROFILE)

    Sub-navigation section to display (defaults to ‘profile’)

Returns:

  • (Hash)

    A hash containing the generated URL

Raises:

  • (KindeSdk::APIError)

    If the user is not authenticated or API request fails

  • (ArgumentError)

    If the return_url is not an absolute URL



339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/kinde_sdk/client.rb', line 339

def generatePortalUrl(return_url, sub_nav = PortalPage::PROFILE)
  unless token_store.bearer_token
    raise KindeSdk::APIError, 'generatePortalUrl: Access Token not found'
  end

  unless return_url.start_with?('http')
    raise ArgumentError, 'generatePortalUrl: returnUrl must be an absolute URL'
  end

  frontend.get_portal_link(subnav: sub_nav, return_url: return_url)
rescue StandardError => e
  Rails.logger.error("Failed to generate portal URL: #{e.message}")
  raise KindeSdk::APIError, "Unable to generate portal URL: #{e.message}"
end

#get_claim(claim, token_type = :access_token) ⇒ Hash

token_type is one of: :access_token, :id_token

Examples:

“scp”, value: [“openid”, “offline”]

Returns:

  • (Hash)


76
77
78
79
80
81
82
83
84
85
86
# File 'lib/kinde_sdk/client.rb', line 76

def get_claim(claim, token_type = :access_token)
  refresh_token if auto_refresh_tokens && token_expired?

  token = @token_store.tokens[token_type.to_sym]
  return unless token

  value = JWT.decode(token, nil, false)[0][claim]
  return unless value

  { name: claim, value: value }
end

#getAllEntitlementsArray Also known as: all_entitlements

Get all entitlements for the authenticated user, handling pagination automatically.

Returns:

  • (Array)

    All entitlements

Raises:



187
188
189
190
191
192
193
194
195
196
# File 'lib/kinde_sdk/client.rb', line 187

def getAllEntitlements
  unless token_store.bearer_token
    raise KindeSdk::APIError, 'User must be authenticated to get entitlements'
  end

  paginate_all_results('entitlements') { |starting_after| entitlements(page_size: 100, starting_after: starting_after) }
rescue StandardError => e
  Rails.logger.error("Failed to fetch all entitlements: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch all entitlements: #{e.message}"
end

#getEntitlement(key) ⇒ OpenStruct?

PHP SDK compatible alias for entitlement

Parameters:

  • key (String)

    The entitlement key to retrieve

Returns:

  • (OpenStruct, nil)

    The entitlement response or nil if not found

Raises:



218
219
220
221
222
223
224
225
226
# File 'lib/kinde_sdk/client.rb', line 218

def getEntitlement(key)
  # Find the specific entitlement from all entitlements (like PHP SDK does)
  entitlements = getAllEntitlements
  
  entitlements.find { |entitlement| entitlement.feature_key == key }
rescue StandardError => e
  Rails.logger.error("Failed to get entitlement for #{key}: #{e.message}")
  raise KindeSdk::APIError, "Unable to get entitlement: #{e.message}"
end

#getEntitlementLimit(key) ⇒ Integer? Also known as: entitlement_limit

Get the maximum limit for a specific entitlement.

Parameters:

  • key (String)

    The entitlement key

Returns:

  • (Integer, nil)

    The maximum limit or nil if not found

Raises:



256
257
258
259
260
261
262
# File 'lib/kinde_sdk/client.rb', line 256

def getEntitlementLimit(key)
  entitlement = getEntitlement(key)
  entitlement ? entitlement.entitlement_limit_max : nil
rescue StandardError => e
  Rails.logger.error("Error getting entitlement limit for #{key}: #{e.message}")
  nil
end

#has_entitlement?(feature_key) ⇒ Boolean

Check if the user has a specific entitlement.

Parameters:

  • feature_key (String)

    The entitlement key to check

Returns:

  • (Boolean)

    True if the user has the entitlement, false otherwise



232
233
234
235
236
237
238
# File 'lib/kinde_sdk/client.rb', line 232

def has_entitlement?(feature_key)
  entitlement_response = entitlement(feature_key)
  entitlement_response&.data&.entitlement.present?
rescue StandardError => e
  Rails.logger.error("Error checking entitlement for #{feature_key}: #{e.message}")
  false
end

#hasEntitlement(key) ⇒ Boolean

PHP SDK compatible alias for has_entitlement?

Parameters:

  • key (String)

    The entitlement key to check

Returns:

  • (Boolean)

    True if the user has the entitlement, false otherwise



244
245
246
247
248
249
# File 'lib/kinde_sdk/client.rb', line 244

def hasEntitlement(key)
  getEntitlement(key) != nil
rescue StandardError => e
  Rails.logger.error("Error checking entitlement for #{key}: #{e.message}")
  false
end

#oauthObject

Custom oauth method that provides backward compatibility



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/kinde_sdk/client.rb', line 137

def oauth
  oauth_api = init_instance_api(KindeApi::OAuthApi)
  
  # Add backward compatibility for get_user method
  unless oauth_api.respond_to?(:get_user)
    oauth_api.define_singleton_method(:get_user) do |opts = {}|
      # Call the new method
       = (opts)
      
      # Convert UserProfileV2 to UserProfile format as a hash
      # This provides backward compatibility without depending on the UserProfile class
      {
        id: .id || .sub,
        preferred_email: .email,
        provided_id: .provided_id,
        last_name: .family_name,
        first_name: .given_name,
        picture: .picture
      }
    end
  end
  
  oauth_api
end

Generate a URL to the user profile portal.

Parameters:

  • return_url (String)

    URL to redirect to after completing the profile flow

  • sub_nav (String)

    Sub-navigation section to display (defaults to ‘profile’)

Returns:

  • (Hash)

    A hash containing the generated URL

Raises:



325
326
327
328
329
330
# File 'lib/kinde_sdk/client.rb', line 325

def portal_link(return_url:, page: PortalPage::PROFILE)
  frontend.get_portal_link(subnav: page, return_url: return_url)
rescue StandardError => e
  Rails.logger.error("Failed to get portal link: #{e.message}")
  raise KindeSdk::APIError, "Unable to get portal link: #{e.message}"
end

#refresh_tokenObject



63
64
65
66
67
68
69
70
# File 'lib/kinde_sdk/client.rb', line 63

def refresh_token
  new_tokens_hash = TokenManager.refresh_tokens(@token_store, Current.session)
  return nil unless new_tokens_hash

  @token_store.set_tokens(new_tokens_hash)
  @kinde_api_client = KindeSdk.api_client(@token_store.bearer_token)
  new_tokens_hash
end

#token_expired?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/kinde_sdk/client.rb', line 59

def token_expired?
  TokenManager.token_expired?(@token_store)
end

#tokens_hashHash

Returns the tokens hash for backwards compatibility

Returns:

  • (Hash)


49
50
51
# File 'lib/kinde_sdk/client.rb', line 49

def tokens_hash
  @token_store.tokens
end

#user_feature_flags(page_size: 10, starting_after: nil) ⇒ OpenStruct

Get user feature flags with pagination support.

Parameters:

  • page_size (Integer) (defaults to: 10)

    Number of results per page (default: 10)

  • starting_after (String) (defaults to: nil)

    The ID to start after for pagination

Returns:

  • (OpenStruct)

    Response containing feature flags data

Raises:



273
274
275
276
277
278
# File 'lib/kinde_sdk/client.rb', line 273

def user_feature_flags(page_size: 10, starting_after: nil)
  frontend.get_feature_flags(page_size: page_size, starting_after: starting_after)
rescue StandardError => e
  Rails.logger.error("Failed to fetch feature flags: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch feature flags: #{e.message}"
end

#user_permissions(page_size: 10, starting_after: nil) ⇒ OpenStruct

Get user permissions with pagination support.

Parameters:

  • page_size (Integer) (defaults to: 10)

    Number of results per page (default: 10)

  • starting_after (String) (defaults to: nil)

    The ID to start after for pagination

Returns:

  • (OpenStruct)

    Response containing permissions data

Raises:



286
287
288
289
290
291
# File 'lib/kinde_sdk/client.rb', line 286

def user_permissions(page_size: 10, starting_after: nil)
  frontend.get_user_permissions(page_size: page_size, starting_after: starting_after)
rescue StandardError => e
  Rails.logger.error("Failed to fetch permissions: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch permissions: #{e.message}"
end

#user_properties(page_size: 10, starting_after: nil) ⇒ OpenStruct

Get user properties with pagination support.

Parameters:

  • page_size (Integer) (defaults to: 10)

    Number of results per page (default: 10)

  • starting_after (String) (defaults to: nil)

    The ID to start after for pagination

Returns:

  • (OpenStruct)

    Response containing properties data

Raises:



299
300
301
302
303
304
# File 'lib/kinde_sdk/client.rb', line 299

def user_properties(page_size: 10, starting_after: nil)
  frontend.get_user_properties(page_size: page_size, starting_after: starting_after)
rescue StandardError => e
  Rails.logger.error("Failed to fetch properties: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch properties: #{e.message}"
end

#user_roles(page_size: 10, starting_after: nil) ⇒ OpenStruct

Get user roles with pagination support.

Parameters:

  • page_size (Integer) (defaults to: 10)

    Number of results per page (default: 10)

  • starting_after (String) (defaults to: nil)

    The ID to start after for pagination

Returns:

  • (OpenStruct)

    Response containing roles data

Raises:



312
313
314
315
316
317
# File 'lib/kinde_sdk/client.rb', line 312

def user_roles(page_size: 10, starting_after: nil)
  frontend.get_user_roles(page_size: page_size, starting_after: starting_after)
rescue StandardError => e
  Rails.logger.error("Failed to fetch roles: #{e.message}")
  raise KindeSdk::APIError, "Unable to fetch roles: #{e.message}"
end