kinde_sdk 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +117 -25
  3. data/kinde_api/README.md +58 -23
  4. data/kinde_api/docs/AddOrganizationUsersRequest.md +1 -1
  5. data/kinde_api/docs/AddOrganizationUsersRequestUsersInner.md +22 -0
  6. data/kinde_api/docs/{AddOrganizationUsers200Response.md → AddOrganizationUsersResponse.md} +2 -2
  7. data/kinde_api/docs/Application.md +20 -0
  8. data/kinde_api/docs/CallbacksApi.md +219 -0
  9. data/kinde_api/docs/ConnectedAppsApi.md +9 -9
  10. data/kinde_api/docs/CreateOrganizationRequest.md +11 -1
  11. data/kinde_api/docs/CreateOrganizationResponse.md +22 -0
  12. data/kinde_api/docs/CreateOrganizationResponseOrganization.md +18 -0
  13. data/kinde_api/docs/CreateOrganizationUserRoleRequest.md +18 -0
  14. data/kinde_api/docs/CreatePermissionRequest.md +22 -0
  15. data/kinde_api/docs/CreateRoleRequest.md +24 -0
  16. data/kinde_api/docs/CreateSubscriberSuccessResponse.md +18 -0
  17. data/kinde_api/docs/CreateSubscriberSuccessResponseSubscriber.md +18 -0
  18. data/kinde_api/docs/{CreateUser200Response.md → CreateUserResponse.md} +2 -2
  19. data/kinde_api/docs/EnvironmentsApi.md +71 -4
  20. data/kinde_api/docs/FeatureFlagsApi.md +9 -11
  21. data/kinde_api/docs/GetApplicationsResponse.md +24 -0
  22. data/kinde_api/docs/GetEnvironmentFeatureFlagsResponse.md +24 -0
  23. data/kinde_api/docs/GetOrganizationFeatureFlagsResponse.md +22 -0
  24. data/kinde_api/docs/GetOrganizationFeatureFlagsResponseFeatureFlagsValue.md +20 -0
  25. data/kinde_api/docs/{GetOrganizations200Response.md → GetOrganizationsResponse.md} +2 -2
  26. data/kinde_api/docs/GetOrganizationsUserRolesResponse.md +24 -0
  27. data/kinde_api/docs/{GetOrganizationUsers200Response.md → GetOrganizationsUsersResponse.md} +2 -2
  28. data/kinde_api/docs/GetRedirectCallbackUrlsResponse.md +18 -0
  29. data/kinde_api/docs/OrganizationUser.md +3 -1
  30. data/kinde_api/docs/OrganizationUserRole.md +22 -0
  31. data/kinde_api/docs/OrganizationsApi.md +494 -59
  32. data/kinde_api/docs/Permissions.md +22 -0
  33. data/kinde_api/docs/PermissionsApi.md +229 -0
  34. data/kinde_api/docs/RedirectCallbackUrls.md +18 -0
  35. data/kinde_api/docs/Roles.md +22 -0
  36. data/kinde_api/docs/RolesApi.md +229 -0
  37. data/kinde_api/docs/SubscribersApi.md +229 -0
  38. data/kinde_api/docs/UpdateOrganizationRequest.md +28 -0
  39. data/kinde_api/docs/UpdateOrganizationUsersRequest.md +18 -0
  40. data/kinde_api/docs/UpdateOrganizationUsersRequestUsersInner.md +24 -0
  41. data/kinde_api/docs/UpdateOrganizationUsersResponse.md +24 -0
  42. data/kinde_api/docs/UpdateUserRequest.md +3 -1
  43. data/kinde_api/docs/User.md +15 -1
  44. data/kinde_api/docs/UserIdentityResult.md +1 -3
  45. data/kinde_api/docs/UserProfile.md +3 -1
  46. data/kinde_api/docs/UserProfileV2.md +6 -2
  47. data/kinde_api/docs/UsersApi.md +31 -259
  48. data/kinde_api/docs/{GetUsers200Response.md → UsersResponse.md} +2 -2
  49. data/kinde_api/lib/kinde_api/api/callbacks_api.rb +221 -0
  50. data/kinde_api/lib/kinde_api/api/connected_apps_api.rb +8 -8
  51. data/kinde_api/lib/kinde_api/api/environments_api.rb +61 -4
  52. data/kinde_api/lib/kinde_api/api/feature_flags_api.rb +8 -15
  53. data/kinde_api/lib/kinde_api/api/organizations_api.rb +492 -57
  54. data/kinde_api/lib/kinde_api/api/permissions_api.rb +226 -0
  55. data/kinde_api/lib/kinde_api/api/roles_api.rb +226 -0
  56. data/kinde_api/lib/kinde_api/api/subscribers_api.rb +228 -0
  57. data/kinde_api/lib/kinde_api/api/users_api.rb +30 -233
  58. data/kinde_api/lib/kinde_api/api_client.rb +1 -13
  59. data/kinde_api/lib/kinde_api/configuration.rb +3 -77
  60. data/kinde_api/lib/kinde_api/models/add_organization_users_request.rb +2 -2
  61. data/kinde_api/lib/kinde_api/models/add_organization_users_request_users_inner.rb +242 -0
  62. data/kinde_api/lib/kinde_api/models/{add_organization_users200_response.rb → add_organization_users_response.rb} +3 -3
  63. data/kinde_api/lib/kinde_api/models/application.rb +226 -0
  64. data/kinde_api/lib/kinde_api/models/create_organization_request.rb +54 -4
  65. data/kinde_api/lib/kinde_api/models/{remove_organization_users200_response.rb → create_organization_response.rb} +19 -12
  66. data/kinde_api/lib/kinde_api/models/{create_organization201_response.rb → create_organization_response_organization.rb} +8 -18
  67. data/kinde_api/lib/kinde_api/models/create_organization_user_role_request.rb +218 -0
  68. data/kinde_api/lib/kinde_api/models/create_permission_request.rb +238 -0
  69. data/kinde_api/lib/kinde_api/models/create_role_request.rb +248 -0
  70. data/kinde_api/lib/kinde_api/models/create_subscriber_success_response.rb +217 -0
  71. data/kinde_api/lib/kinde_api/models/create_subscriber_success_response_subscriber.rb +218 -0
  72. data/kinde_api/lib/kinde_api/models/create_user_request_identities_inner.rb +34 -0
  73. data/kinde_api/lib/kinde_api/models/{create_user200_response.rb → create_user_response.rb} +3 -3
  74. data/kinde_api/lib/kinde_api/models/get_applications_response.rb +249 -0
  75. data/kinde_api/lib/kinde_api/models/get_environment_feature_flags_response.rb +250 -0
  76. data/kinde_api/lib/kinde_api/models/get_organization_feature_flags_response.rb +240 -0
  77. data/kinde_api/lib/kinde_api/models/get_organization_feature_flags_response_feature_flags_value.rb +260 -0
  78. data/kinde_api/lib/kinde_api/models/{get_organizations200_response.rb → get_organizations_response.rb} +3 -3
  79. data/kinde_api/lib/kinde_api/models/get_organizations_user_roles_response.rb +249 -0
  80. data/kinde_api/lib/kinde_api/models/{get_organization_users200_response.rb → get_organizations_users_response.rb} +3 -3
  81. data/kinde_api/lib/kinde_api/models/get_redirect_callback_urls_response.rb +220 -0
  82. data/kinde_api/lib/kinde_api/models/organization_user.rb +15 -4
  83. data/kinde_api/lib/kinde_api/models/organization_user_role.rb +235 -0
  84. data/kinde_api/lib/kinde_api/models/permissions.rb +238 -0
  85. data/kinde_api/lib/kinde_api/models/redirect_callback_urls.rb +220 -0
  86. data/kinde_api/lib/kinde_api/models/roles.rb +238 -0
  87. data/kinde_api/lib/kinde_api/models/update_organization_request.rb +268 -0
  88. data/kinde_api/lib/kinde_api/models/{remove_organization_users_request.rb → update_organization_users_request.rb} +5 -5
  89. data/kinde_api/lib/kinde_api/models/update_organization_users_request_users_inner.rb +252 -0
  90. data/kinde_api/lib/kinde_api/models/update_organization_users_response.rb +250 -0
  91. data/kinde_api/lib/kinde_api/models/update_user_request.rb +14 -4
  92. data/kinde_api/lib/kinde_api/models/user.rb +79 -5
  93. data/kinde_api/lib/kinde_api/models/user_identity_result.rb +4 -14
  94. data/kinde_api/lib/kinde_api/models/user_profile.rb +14 -4
  95. data/kinde_api/lib/kinde_api/models/user_profile_v2.rb +25 -5
  96. data/kinde_api/lib/kinde_api/models/{get_users200_response.rb → users_response.rb} +3 -3
  97. data/kinde_api/lib/kinde_api.rb +32 -8
  98. data/kinde_api/spec/api/callbacks_api_spec.rb +73 -0
  99. data/kinde_api/spec/api/connected_apps_api_spec.rb +1 -1
  100. data/kinde_api/spec/api/environments_api_spec.rb +11 -0
  101. data/kinde_api/spec/api/feature_flags_api_spec.rb +1 -2
  102. data/kinde_api/spec/api/organizations_api_spec.rb +93 -14
  103. data/kinde_api/spec/api/permissions_api_spec.rb +74 -0
  104. data/kinde_api/spec/api/roles_api_spec.rb +74 -0
  105. data/kinde_api/spec/api/subscribers_api_spec.rb +76 -0
  106. data/kinde_api/spec/api/users_api_spec.rb +6 -47
  107. data/kinde_api/spec/api_client_spec.rb +0 -12
  108. data/kinde_api/spec/models/add_organization_users_request_users_inner_spec.rb +46 -0
  109. data/kinde_api/spec/models/{add_organization_users200_response_spec.rb → add_organization_users_response_spec.rb} +6 -6
  110. data/kinde_api/spec/models/{remove_organization_users200_response_spec.rb → application_spec.rb} +8 -8
  111. data/kinde_api/spec/models/create_organization_request_spec.rb +30 -0
  112. data/kinde_api/spec/models/create_organization_response_organization_spec.rb +34 -0
  113. data/kinde_api/spec/models/{create_organization201_response_spec.rb → create_organization_response_spec.rb} +13 -7
  114. data/kinde_api/spec/models/create_organization_user_role_request_spec.rb +34 -0
  115. data/kinde_api/spec/models/create_permission_request_spec.rb +46 -0
  116. data/kinde_api/spec/models/create_role_request_spec.rb +52 -0
  117. data/kinde_api/spec/models/create_subscriber_success_response_spec.rb +34 -0
  118. data/kinde_api/spec/models/create_subscriber_success_response_subscriber_spec.rb +34 -0
  119. data/kinde_api/spec/models/create_user_request_identities_inner_spec.rb +4 -0
  120. data/kinde_api/spec/models/{create_user200_response_spec.rb → create_user_response_spec.rb} +6 -6
  121. data/kinde_api/spec/models/get_applications_response_spec.rb +52 -0
  122. data/kinde_api/spec/models/get_environment_feature_flags_response_spec.rb +52 -0
  123. data/kinde_api/spec/models/get_organization_feature_flags_response_feature_flags_value_spec.rb +44 -0
  124. data/kinde_api/spec/models/get_organization_feature_flags_response_spec.rb +46 -0
  125. data/kinde_api/spec/models/{get_organizations200_response_spec.rb → get_organizations_response_spec.rb} +6 -6
  126. data/kinde_api/spec/models/get_organizations_user_roles_response_spec.rb +52 -0
  127. data/kinde_api/spec/models/{get_organization_users200_response_spec.rb → get_organizations_users_response_spec.rb} +6 -6
  128. data/kinde_api/spec/models/get_redirect_callback_urls_response_spec.rb +34 -0
  129. data/kinde_api/spec/models/organization_user_role_spec.rb +46 -0
  130. data/kinde_api/spec/models/organization_user_spec.rb +6 -0
  131. data/kinde_api/spec/models/permissions_spec.rb +46 -0
  132. data/kinde_api/spec/models/redirect_callback_urls_spec.rb +34 -0
  133. data/kinde_api/spec/models/roles_spec.rb +46 -0
  134. data/kinde_api/spec/models/update_organization_request_spec.rb +64 -0
  135. data/kinde_api/spec/models/{remove_organization_users_request_spec.rb → update_organization_users_request_spec.rb} +6 -6
  136. data/kinde_api/spec/models/update_organization_users_request_users_inner_spec.rb +52 -0
  137. data/kinde_api/spec/models/update_organization_users_response_spec.rb +52 -0
  138. data/kinde_api/spec/models/update_user_request_spec.rb +6 -0
  139. data/kinde_api/spec/models/user_identity_result_spec.rb +0 -6
  140. data/kinde_api/spec/models/user_profile_spec.rb +6 -0
  141. data/kinde_api/spec/models/user_profile_v2_spec.rb +12 -0
  142. data/kinde_api/spec/models/user_spec.rb +42 -0
  143. data/kinde_api/spec/models/{get_users200_response_spec.rb → users_response_spec.rb} +6 -6
  144. data/kinde_sdk.gemspec +1 -1
  145. data/lib/kinde_sdk/client/feature_flags.rb +64 -0
  146. data/lib/kinde_sdk/client/permissions.rb +20 -0
  147. data/lib/kinde_sdk/client.rb +32 -21
  148. data/lib/kinde_sdk/configuration.rb +0 -2
  149. data/lib/kinde_sdk/version.rb +1 -1
  150. data/lib/kinde_sdk.rb +31 -19
  151. data/spec/kinde_sdk_spec.rb +156 -14
  152. data/spec/spec_helper.rb +2 -0
  153. metadata +134 -36
  154. data/kinde_api/docs/CreateOrganization201Response.md +0 -20
  155. data/kinde_api/docs/RemoveOrganizationUsers200Response.md +0 -20
  156. data/kinde_api/docs/RemoveOrganizationUsersRequest.md +0 -18
@@ -2,46 +2,57 @@ require "kinde_api"
2
2
 
3
3
  module KindeSdk
4
4
  class Client
5
- attr_accessor :kinde_api_client
6
- attr_accessor :bearer_token
5
+ include FeatureFlags
6
+ include Permissions
7
7
 
8
- def initialize(sdk_api_client, bearer_token)
8
+ attr_accessor :kinde_api_client, :bearer_token, :tokens_hash, :expires_at
9
+
10
+ def initialize(sdk_api_client, tokens_hash)
9
11
  @kinde_api_client = sdk_api_client
10
- @bearer_token = bearer_token
11
- @decoded_token = JWT.decode(bearer_token, nil, false)
12
+ set_hash_related_data(tokens_hash)
12
13
  end
13
14
 
14
- def get_claim(*args)
15
- @decoded_token[0].dig(*args)
15
+ def token_expired?
16
+ expires_at.to_i > 0 && (expires_at <= Time.now.to_i)
16
17
  end
17
18
 
18
- def get_permissions
19
- get_claim("permissions")
19
+ def refresh_token
20
+ new_tokens_hash = KindeSdk.refresh_token(tokens_hash)
21
+ set_hash_related_data(new_tokens_hash)
22
+ @kinde_api_client = KindeSdk.api_client(tokens_hash["access_token"])
23
+ new_tokens_hash
20
24
  end
21
25
 
22
- def get_permission(permission)
23
- {
24
- org_code: get_claim("org_code"),
25
- is_granted: permission_granted?(permission)
26
- }
27
- end
26
+ # token_type is one of: :access_token, :id_token
27
+ #
28
+ # @return [Hash]
29
+ # @example {name: "scp", value: ["openid", "offline"]}
30
+ def get_claim(claim, token_type = :access_token)
31
+ token = tokens_hash[token_type]
32
+ return unless token
28
33
 
29
- def permission_granted?(permission)
30
- get_claim("permissions").include?(permission)
31
- end
34
+ value = JWT.decode(token, nil, false)[0][claim]
35
+ return unless value
32
36
 
33
- def logout
34
- KindeSdk.logout(bearer_token, kinde_api_client)
37
+ { name: claim, value: value }
35
38
  end
36
39
 
37
40
  ::KindeApi.constants.filter { |klass| klass.to_s.end_with?("Api") }.each do |klass|
38
41
  api_klass = Kernel.const_get("KindeApi::#{klass}")
39
42
 
40
- define_method(klass.to_s.downcase.split("api")[0]) { init_instance_api(api_klass) }
43
+ define_method(klass.to_s.gsub(/([a-z\d])([A-Z])/) { "#{$1}_#{$2}" }.downcase.split("_api")[0]) do
44
+ init_instance_api(api_klass)
45
+ end
41
46
  end
42
47
 
43
48
  private
44
49
 
50
+ def set_hash_related_data(tokens_hash)
51
+ @tokens_hash = tokens_hash.transform_keys(&:to_sym)
52
+ @bearer_token = tokens_hash[:access_token]
53
+ @expires_at = tokens_hash[:expires_at]
54
+ end
55
+
45
56
  def init_instance_api(api_klass)
46
57
  api_klass.new(kinde_api_client)
47
58
  end
@@ -14,7 +14,6 @@ module KindeSdk
14
14
  attr_accessor :debugging
15
15
  attr_accessor :oauth_client
16
16
  attr_accessor :pkce_enabled
17
- attr_accessor :business_name
18
17
 
19
18
  def initialize
20
19
  @authorize_url = '/oauth2/auth'
@@ -23,7 +22,6 @@ module KindeSdk
23
22
  @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
24
23
  @scope = 'openid offline email profile'
25
24
  @pkce_enabled = true
26
- @business_name = nil
27
25
 
28
26
  yield(self) if block_given?
29
27
  end
@@ -1,3 +1,3 @@
1
1
  module KindeSdk
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/kinde_sdk.rb CHANGED
@@ -1,11 +1,14 @@
1
1
  require "kinde_sdk/version"
2
2
  require "kinde_sdk/configuration"
3
+ require "kinde_sdk/client/feature_flags"
4
+ require "kinde_sdk/client/permissions"
3
5
  require "kinde_sdk/client"
4
6
 
5
7
  require 'securerandom'
6
8
  require 'oauth2'
7
9
  require 'pkce_challenge'
8
10
  require 'faraday/follow_redirects'
11
+ require 'uri'
9
12
 
10
13
  module KindeSdk
11
14
  class << self
@@ -24,9 +27,9 @@ module KindeSdk
24
27
  # receive url for authorization in Kinde itself
25
28
  #
26
29
  # @return [Hash]
27
- def auth_url(**kwargs)
30
+ def auth_url(redirect_uri: @config.callback_url, **kwargs)
28
31
  params = {
29
- redirect_uri: @config.callback_url,
32
+ redirect_uri: redirect_uri,
30
33
  state: SecureRandom.hex,
31
34
  scope: @config.scope
32
35
  }.merge(**kwargs)
@@ -43,26 +46,34 @@ module KindeSdk
43
46
  # when callback processor receives code, it needs to be used for fetching bearer token
44
47
  #
45
48
  # @return [Hash]
46
- def fetch_tokens(params_or_code, code_verifier = nil)
49
+ def fetch_tokens(params_or_code, code_verifier: nil, redirect_uri: @config.callback_url)
47
50
  code = params_or_code.kind_of?(Hash) ? params.fetch("code") : params_or_code
48
- params = { redirect_uri: @config.callback_url }
51
+ params = {
52
+ redirect_uri: redirect_uri,
53
+ headers: { 'User-Agent' => "Kinde-SDK: Ruby/#{KindeSdk::VERSION}" }
54
+ }
49
55
  params[:code_verifier] = code_verifier if code_verifier
50
56
  @config.oauth_client.auth_code.get_token(code.to_s, params).to_hash
51
57
  end
52
58
 
59
+ # tokens_hash #=>
60
+ # {"access_token"=>"eyJhbGciOiJSUzI1NiIsIm...",
61
+ # "expires_in"=>86399,
62
+ # "id_token"=>"eyJhbGciOiJSUz",
63
+ # "refresh_token"=>"eyJhbGciOiJSUz",
64
+ # "scope"=>"openid offline email profile",
65
+ # "token_type"=>"bearer"}
66
+ #
53
67
  # @return [KindeSdk::Client]
54
- def client(bearer_token)
55
- sdk_api_client = api_client(bearer_token)
56
- KindeSdk::Client.new(sdk_api_client, bearer_token)
68
+ def client(tokens_hash)
69
+ sdk_api_client = api_client(tokens_hash["access_token"])
70
+ KindeSdk::Client.new(sdk_api_client, tokens_hash)
57
71
  end
58
72
 
59
- def logout(bearer_token, sdk_api_client = nil)
60
- (sdk_api_client || api_client(bearer_token))
61
- .call_api(
62
- :get, '/logout',
63
- query_params: { 'redirect' => @config.logout_url },
64
- header_params: { 'Authorization' => "Bearer #{bearer_token}" }
65
- )
73
+ def logout_url
74
+ query = @config.logout_url ? URI.encode_www_form(redirect: @config.logout_url) : nil
75
+ host = URI::parse(@config.domain).host
76
+ URI::HTTP.build(host: host, path: '/logout', query: query).to_s
66
77
  end
67
78
 
68
79
  def client_credentials_access(
@@ -97,10 +108,10 @@ module KindeSdk
97
108
  config = KindeApi::Configuration.default
98
109
  config.configure do |c|
99
110
  c.access_token = bearer_token
100
- c.server_variables = { businessName: business_name }
101
111
  c.host = @config.domain
102
112
  c.debugging = @config.debugging
103
113
  c.logger = @config.logger
114
+ c.scheme = url_scheme(c.scheme)
104
115
  end
105
116
 
106
117
  KindeApi::ApiClient.new(config)
@@ -108,10 +119,11 @@ module KindeSdk
108
119
 
109
120
  private
110
121
 
111
- def business_name
112
- # from https://example.kinde.com fetches `example`
113
- # from https://example-chamois.au.kinde.com fetches `example-chamois.au`
114
- @config.business_name || @config.domain.split("//")[1].split(".")[0..-3].join(".")
122
+ def url_scheme(default_scheme)
123
+ parsed_url = URI.parse(@config.domain.to_s)
124
+ parsed_url.scheme || default_scheme
125
+ rescue URI::InvalidURIError
126
+ default_scheme
115
127
  end
116
128
  end
117
129
  end
@@ -5,6 +5,7 @@ describe KindeSdk do
5
5
  let(:client_id) { "client_id" }
6
6
  let(:client_secret) { "client_secret" }
7
7
  let(:callback_url) { "http://localhost:3000/callback" }
8
+ let(:logout_url) { "http://localhost/logout-callback" }
8
9
 
9
10
  before do
10
11
  KindeSdk.configure do |c|
@@ -12,6 +13,7 @@ describe KindeSdk do
12
13
  c.client_id = client_id
13
14
  c.client_secret = client_secret
14
15
  c.callback_url = callback_url
16
+ c.logout_url = logout_url
15
17
  end
16
18
  end
17
19
 
@@ -20,12 +22,71 @@ describe KindeSdk do
20
22
  auth_obj = described_class.auth_url
21
23
  expect(auth_obj[:code_verifier]).not_to be_nil
22
24
  expect(auth_obj[:url]).to start_with("#{domain}/oauth2/auth?client_id=#{client_id}&")
25
+ expect(auth_obj[:url]).to match(/localhost%3A3000%2Fcallback/)
26
+ end
27
+
28
+ it "allows override callback url" do
29
+ auth_obj = described_class.auth_url(redirect_uri: "localhost:5000/another_callback")
30
+ expect(auth_obj[:url]).to match(/localhost%3A5000%2Fanother_callback/)
31
+ end
32
+ end
33
+
34
+ describe "#logout_url" do
35
+ it "returns logout url" do
36
+ expect(described_class.logout_url)
37
+ .to eq("http://example.com/logout?redirect=http%3A%2F%2Flocalhost%2Flogout-callback")
38
+ end
39
+
40
+ context "when logout url not set" do
41
+ let(:logout_url) { nil }
42
+ it "returns logout url without redirect query" do
43
+ expect(described_class.logout_url).to eq("http://example.com/logout")
44
+ end
23
45
  end
24
46
  end
25
47
 
26
48
  describe "#api_client" do
27
49
  it "returns initialized api_client instance of KindeApi" do
28
- expect(described_class.api_client("bearer-token")).to be_instance_of(KindeApi::ApiClient)
50
+ expect(described_class.api_client({ "access_token": "bearer-token" }))
51
+ .to be_instance_of(KindeApi::ApiClient)
52
+ end
53
+ end
54
+
55
+ describe "#fetch_tokens" do
56
+ let(:code) { "some-code" }
57
+ before do
58
+ stub_request(:post, "#{domain}/oauth2/token")
59
+ .with(
60
+ body: {
61
+ "code" => code,
62
+ "grant_type" => "authorization_code",
63
+ "redirect_uri" => callback_url
64
+ },
65
+ headers: {
66
+ 'Accept' => '*/*',
67
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
68
+ 'Authorization' => 'Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=',
69
+ 'Content-Type' => 'application/x-www-form-urlencoded',
70
+ 'User-Agent' => "Kinde-SDK: Ruby/#{KindeSdk::VERSION}"
71
+ }
72
+ )
73
+ .to_return(
74
+ status: 200,
75
+ body: { "access_token": "eyJ", "expires_in": 86399, "scope": "", "token_type": "bearer" }.to_json,
76
+ headers: { "content-type" => "application/json;charset=UTF-8" }
77
+ )
78
+ end
79
+
80
+ it "calls /token url with proper body and headers" do
81
+ expect(described_class.fetch_tokens(code).keys).to eq(%w[scope token_type access_token refresh_token expires_at])
82
+ end
83
+
84
+ context "with redefined callback_url" do
85
+ let(:callback_url) { "another-callback" }
86
+
87
+ it "calls /token url with proper body and headers" do
88
+ expect(described_class.fetch_tokens(code).keys.size).to eq(5)
89
+ end
29
90
  end
30
91
  end
31
92
 
@@ -59,22 +120,89 @@ describe KindeSdk do
59
120
  describe "client" do
60
121
  let(:hash_to_encode) do
61
122
  { "aud" => [],
62
- "azp" => "19ebb687cd2f405c9f2daf645a8db895",
63
- "exp" => 1679600554,
64
- "feature_flags" => nil,
65
- "iat" => 1679514154,
66
- "iss" => "https://example.kinde.com",
67
- "jti" => "22c48b2c-da46-4661-a7ff-425c23eceab5",
68
- "org_code" => "org_cb4544175bc",
69
- "permissions" => ["read:todos", "create:todos"],
70
- "scp" => ["openid", "offline"],
71
- "sub" => "kp:b17adf719f7d4b87b611d1a88a09fd15" }
123
+ "azp" => "19ebb687cd2f405c9f2daf645a8db895",
124
+ "exp" => 1679600554,
125
+ "feature_flags" => {
126
+ "asd" => { "t" => "b", "v" => true },
127
+ "eeeeee" => { "t" => "i", "v" => 111 },
128
+ "qqq" => { "t" => "s", "v" => "aa" }
129
+ },
130
+ "iat" => 1679514154,
131
+ "iss" => "https://example.kinde.com",
132
+ "jti" => "22c48b2c-da46-4661-a7ff-425c23eceab5",
133
+ "org_code" => "org_cb4544175bc",
134
+ "permissions" => ["read:todos", "create:todos"],
135
+ "scp" => ["openid", "offline"],
136
+ "sub" => "kp:b17adf719f7d4b87b611d1a88a09fd15" }
72
137
  end
73
138
  let(:token) { JWT.encode(hash_to_encode, nil, "none") }
74
- let(:client) { described_class.client(token) }
139
+ let(:expires_at) { Time.now.to_i + 10000000 }
140
+ let(:client) { described_class.client({ "access_token": token, "expires_at": expires_at }) }
141
+
142
+ context "with feature flags" do
143
+ it "returns existing flags", :aggregate_failures do
144
+ expect(client.get_flag("asd")).to eq({ code: "asd", is_default: false, type: "boolean", value: true })
145
+ expect(client.get_flag("eeeeee")).to eq({ code: "eeeeee", is_default: false, type: "integer", value: 111 })
146
+ expect(client.get_flag("qqq")).to eq({ code: "qqq", is_default: false, type: "string", value: "aa" })
147
+
148
+ expect { client.get_flag("undefined") }
149
+ .to raise_error(StandardError, "This flag was not found, and no default value has been provided")
150
+ end
151
+
152
+ it "returns fallbacks if no flag present", :aggregate_failures do
153
+ expect(client.get_flag("undefined", { default_value: true }))
154
+ .to eq({ code: "undefined", is_default: true, value: true })
155
+
156
+ expect(client.get_flag("undefined", { default_value: true }, "b")[:value]).to eq(true)
157
+ expect(client.get_flag("undefined", { default_value: "true" }, "s")[:value]).to eq("true")
158
+ expect(client.get_flag("undefined", { default_value: 111 }, "i")[:value]).to eq(111)
159
+ end
160
+
161
+ it "raises argument error when no value type match", :aggregate_failures do
162
+ expect { client.get_flag("undefined", { default_value: true }, "s") }
163
+ .to raise_error(ArgumentError, "Flag undefined value type is different from requested type")
164
+
165
+ expect { client.get_flag("undefined", { default_value: true }, "i") }
166
+ .to raise_error(ArgumentError, "Flag undefined value type is different from requested type")
167
+
168
+ expect { client.get_flag("undefined", { default_value: "true" }, "b") }
169
+ .to raise_error(ArgumentError, "Flag undefined value type is different from requested type")
170
+ end
171
+
172
+ it "behaves the same way for boolean flag wrapper getter", :aggregate_failures do
173
+ expect { client.get_boolean_flag("eeeeee") }
174
+ .to raise_error(ArgumentError, "Flag eeeeee value type is different from requested type")
175
+ expect(client.get_boolean_flag("asd")).to eq(true)
176
+ expect(client.get_boolean_flag("undefined", false)).to eq(false)
177
+
178
+ expect { client.get_boolean_flag("undefined", "true") }
179
+ .to raise_error(ArgumentError, "Flag undefined value type is different from requested type")
180
+ end
181
+
182
+ it "behaves the same way for integer flag wrapper getter", :aggregate_failures do
183
+ expect { client.get_integer_flag("asd") }
184
+ .to raise_error(ArgumentError, "Flag asd value type is different from requested type")
185
+ expect(client.get_integer_flag("eeeeee")).to eq(111)
186
+ expect(client.get_integer_flag("undefined", 111)).to eq(111)
187
+
188
+ expect { client.get_integer_flag("undefined", "true") }
189
+ .to raise_error(ArgumentError, "Flag undefined value type is different from requested type")
190
+ end
191
+
192
+ it "behaves the same way for string flag wrapper getter", :aggregate_failures do
193
+ expect { client.get_string_flag("asd") }
194
+ .to raise_error(ArgumentError, "Flag asd value type is different from requested type")
195
+ expect(client.get_string_flag("qqq")).to eq("aa")
196
+ expect(client.get_string_flag("undefined", "111")).to eq("111")
197
+
198
+ expect { client.get_string_flag("undefined", true) }
199
+ .to raise_error(ArgumentError, "Flag undefined value type is different from requested type")
200
+ end
201
+ end
75
202
 
76
203
  it "returns requested claim from bearer", :aggregate_failures do
77
- expect(client.get_claim("scp")).to eq(hash_to_encode["scp"])
204
+ expect(client.get_claim("scp")).to eq({ name: "scp", value: hash_to_encode["scp"] })
205
+ expect(client.get_claim("scp", :id_token)).to be_nil
78
206
  expect(client.get_claim("aaa")).to be_nil
79
207
  end
80
208
 
@@ -88,8 +216,18 @@ describe KindeSdk do
88
216
  expect(client.permission_granted?("asd")).to be(false)
89
217
  end
90
218
 
219
+ context "with expiration check" do
220
+ it { expect(client.token_expired?).to be(false) }
221
+
222
+ context "when token expired" do
223
+ let(:expires_at) { Time.now.to_i - 1 }
224
+
225
+ it { expect(client.token_expired?).to be(true) }
226
+ end
227
+ end
228
+
91
229
  describe "api instances" do
92
- it 'initializes client by passing the bearer' do
230
+ it 'initializes client by passing the tokens_hash' do
93
231
  expect(client).to be_instance_of(KindeSdk::Client)
94
232
  end
95
233
 
@@ -100,6 +238,10 @@ describe KindeSdk do
100
238
  it "initializes users instance api" do
101
239
  expect(client.users).to be_instance_of(KindeApi::UsersApi)
102
240
  end
241
+
242
+ it "initializes feature flags instance api" do
243
+ expect(client.feature_flags).to be_instance_of(KindeApi::FeatureFlagsApi)
244
+ end
103
245
  end
104
246
  end
105
247
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require "kinde_sdk"
2
2
  require "webmock/rspec"
3
3
 
4
+ WebMock.disable_net_connect!
5
+
4
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
5
7
  RSpec.configure do |config|
6
8
  # rspec-expectations config goes here. You can use an alternate