kinetic_sdk 7.0.0.rc3 → 7.0.0.rc4

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: 7728373bd9c4a15c643c54aed99acd289e5f776ee0819c8b14f7aea9ec54c106
4
- data.tar.gz: d22c5e6907360c11cb886dfb2c58f5c80c5434fcd2b8d669f846b074a57d78ae
3
+ metadata.gz: 0ab81a960e1aebbf2e7cd839fdf3fd60c33ca71e985e0c6530c7e84ca8e8ec6f
4
+ data.tar.gz: d485a88f2085aa142c0015c2899999d46c403f66b8b460efffb0918730ca51aa
5
5
  SHA512:
6
- metadata.gz: 0eaa0e7c58dead1010dbd1bd7964f42cf762a712ab7bc888037e6a8e6709b2af1b2562df834ed0d759cc1e677473ffacad035582b10542baaaf45d58401c4979
7
- data.tar.gz: f882b337544ec35443094eb971e821859d9480994ab7200519f090607672bc4e35ae284a04bb84aa2ffdc7fb15f1e6755c9712905c9188c518bb3751a06fdde3
6
+ metadata.gz: dbd1dbf0607dc20f54e2cbea981496d1fbc8088c8b00fa8ce6483317577fe15bff2b8ae3328d7e797506dea4db66a9cc5c6d3b3ca491bdc99a0ff6fd144e9725
7
+ data.tar.gz: 86092ddc85c919ace2d1bb3133c8feb9aa320969162e471cc100617b81fef05fa0dc5b644c770dbb1a41ebc8c4b6e8aa53586bcc31463b966caef2fb44aa74fe
data/CHANGELOG.md CHANGED
@@ -1,6 +1,40 @@
1
1
  # Change Log
2
2
 
3
3
 
4
+ ## [7.0.0.rc4](https://github.com/kineticdata/kinetic-sdk-rb/tree/7.0.0.rc4) (2026-09-01)
5
+
6
+ **Breaking changes:**
7
+
8
+ - `jwt_token` now uses the OAuth 2.0 `client_credentials` grant, a single POST to
9
+ `/{space_slug}/app/oauth2/token`. Kinetic Platform 7 runs a standards compliant
10
+ authorization server in which `/app/oauth2/authorize` is an interactive endpoint
11
+ requiring a browser session, consent, a registered redirect_uri and PKCE, so the
12
+ previous authorization-code flow returned `400`. The response shape is unchanged:
13
+ callers reading `jwt_response.content["access_token"]` are unaffected.
14
+ - `jwt_code` has been removed. It drove the authorization-code flow that no longer
15
+ applies to machine to machine clients.
16
+ - The OAuth client used by the SDK must be registered in the space as a **confidential**
17
+ client (or with no `clientType`, which defaults to confidential). Public clients and
18
+ the built in system client register only the `authorization_code` grant and are
19
+ rejected by the token endpoint.
20
+
21
+ **Fixed bugs:**
22
+
23
+ - The OAuth routes are now space scoped (`/{space_slug}/app/oauth2/...`) when the SDK is
24
+ built from an `:app_server_url` plus a `:space_slug`. The authorization server resolves
25
+ the space from the request path and rejects requests made outside a space context.
26
+ - OAuth failures now report the `error` and `error_description` from a JSON error, or the
27
+ `X-Kinetic-CID` correlation id when Core answers with its generic HTML error page,
28
+ instead of dumping the inspected response object.
29
+
30
+ **Implemented enhancements:**
31
+
32
+ - Added the `:oauth_scope` option, defaulting to `full`. Valid scopes are `full`, `read`,
33
+ `write`, `admin`, `submissions` and `submissions:read`.
34
+ - Added specs covering the token exchange, scope configuration, and the failure modes for
35
+ both the Integrator and Discussions SDKs.
36
+
37
+
4
38
  ## [7.0.0.rc3](https://github.com/kineticdata/kinetic-sdk-rb/tree/7.0.0.rc3) (2026-09-01)
5
39
 
6
40
  **Fixed bugs:**
@@ -14,7 +14,8 @@ module KineticSdk
14
14
  include KineticSdk::Utils::KineticExportUtils
15
15
 
16
16
  attr_reader :api_url, :username, :options, :password, :proxy_url,
17
- :space_slug, :server, :version, :logger
17
+ :space_slug, :server, :version, :logger, :oauth_url,
18
+ :oauth_scope
18
19
 
19
20
  # Initalize the Core SDK with the web server URL, the space user
20
21
  # username and password, along with any custom option values.
@@ -55,6 +56,8 @@ module KineticSdk
55
56
  # * :max_redirects (Fixnum) (_defaults to: 5_) maximum number of redirects to follow
56
57
  # * :ssl_ca_file (String) full path to PEM certificate used to verify the server
57
58
  # * :ssl_verify_mode (String) (_defaults to: none_) - none | peer
59
+ # * :oauth_scope (String) (_defaults to: full_) scope requested when retrieving a
60
+ # JWT - full | read | write | admin | submissions | submissions:read
58
61
  #
59
62
  # Example: using a configuration file
60
63
  #
@@ -154,12 +157,17 @@ module KineticSdk
154
157
  @server = options[:app_server_url].chomp('/')
155
158
  @api_url = @server + (@space_slug.nil? ? "/app/api/v1" : "/#{@space_slug}/app/api/v1")
156
159
  @proxy_url = @space_slug.nil? ? nil : "#{@server}/#{@space_slug}/app/components"
160
+ # The authorization server resolves the space from the request path, so the
161
+ # OAuth routes must be space scoped exactly like the API routes.
162
+ @oauth_url = @server + (@space_slug.nil? ? "/app/oauth2" : "/#{@space_slug}/app/oauth2")
157
163
  else
158
164
  raise StandardError.new "The :space_slug option is required when using the :space_server_url option" if @space_slug.nil?
159
165
  @server = options[:space_server_url].chomp('/')
160
166
  @api_url = "#{@server}/app/api/v1"
161
167
  @proxy_url = "#{@server}/app/components"
168
+ @oauth_url = "#{@server}/app/oauth2"
162
169
  end
170
+ @oauth_scope = @options[:oauth_scope] || @options["oauth_scope"] || "full"
163
171
  @version = 1
164
172
  end
165
173
 
@@ -1,55 +1,79 @@
1
1
  module KineticSdk
2
2
  class Core
3
3
 
4
- # Gets an authentication token
4
+ # Gets an authentication token using the OAuth 2.0 client credentials grant.
5
+ #
6
+ # Kinetic Platform 7 runs a standards compliant authorization server, in which
7
+ # `/app/oauth2/authorize` is an interactive endpoint: it expects an authenticated
8
+ # browser session, a consent round trip, a registered redirect_uri, and PKCE. A
9
+ # machine to machine client cannot drive it, and should not try - it exchanges its
10
+ # own credentials for a token directly at the token endpoint instead.
11
+ #
12
+ # The OAuth client must be registered in the space as a confidential client (or with
13
+ # no clientType, which defaults to confidential) so that it carries the
14
+ # `client_credentials` grant. Public clients and the built in system client support
15
+ # only `authorization_code`, and will be rejected here.
5
16
  #
6
17
  # @param client_id [String] the oauth client id
7
18
  # @param client_secret [String] the oauth client secret
8
- # @param headers [Hash] hash of headers to send, default is basic authentication and accept JSON content type
19
+ # @param headers [Hash] additional headers to send. The Accept, Authorization and
20
+ # Content-Type headers required by the token endpoint always take precedence.
21
+ # @param scope [String] scope to request, defaults to the +:oauth_scope+ option (+full+)
9
22
  # @return [KineticSdk::Utils::KineticHttpResponse] object, with +code+, +message+, +content_string+, and +content+ properties
10
- def jwt_token(client_id, client_secret, headers = default_headers)
11
- # retrieve the jwt code
12
- jwt_code = jwt_code(client_id, headers)
13
- # retrieve the jwt token
23
+ def jwt_token(client_id, client_secret, headers = {}, scope = oauth_scope)
14
24
  @logger.info("Retrieving JWT authorization token")
15
- url = "#{@server}/app/oauth2/token?grant_type=authorization_code&response_type=token&client_id=#{client_id}&code=#{jwt_code}"
16
- token_headers = header_accept_json.merge(header_basic_auth(client_id, client_secret))
17
- response = post(url, {}, token_headers, { :max_redirects => 0 })
25
+ url = "#{@oauth_url}/token"
26
+
27
+ # The required headers are merged last so that a caller passing the SDK default
28
+ # headers cannot replace the client's basic authentication with the user's.
29
+ token_headers = headers
30
+ .merge(header_accept_json)
31
+ .merge(header_basic_auth(client_id, client_secret))
32
+ .merge({ "Content-Type" => "application/x-www-form-urlencoded" })
33
+
34
+ payload = { "grant_type" => "client_credentials" }
35
+ payload["scope"] = scope unless scope.nil? || scope.to_s.empty?
18
36
 
19
- if response.status == 401
20
- raise StandardError.new "#{response.message}, the oauth client id and secret are invalid."
21
- elsif response.status == 200
37
+ # Redirects are not followed: the token endpoint has no reason to redirect, and
38
+ # following one would forward the client credentials to another host.
39
+ response = post(url, URI.encode_www_form(payload), token_headers, { :max_redirects => 0 })
40
+
41
+ case response.status
42
+ when 200
22
43
  response
44
+ when 401
45
+ raise StandardError.new(
46
+ "Unable to retrieve token: #{oauth_error_message(response)}. The oauth client id " \
47
+ "and secret are invalid, or no such client is registered in this space."
48
+ )
23
49
  else
24
- raise StandardError.new "Unable to retrieve token: #{response}"
50
+ raise StandardError.new("Unable to retrieve token: #{oauth_error_message(response)}")
25
51
  end
26
52
  end
27
53
 
28
- # Gets an authentication code.
54
+ private
55
+
56
+ # Builds an actionable message from an OAuth error response.
29
57
  #
30
- # This method should really never need to be called externally.
58
+ # The authorization server answers most failures with a JSON OAuth error, but falls
59
+ # back to Core's generic HTML error page for others. That page carries no error code,
60
+ # only a correlation id in the X-Kinetic-CID response header, which is what is needed
61
+ # to find the matching entry in the server log.
31
62
  #
32
- # @param client_id [String]
33
- # @param headers [Hash] hash of headers to send, default is basic authentication and accept JSON content type
34
- # @return [KineticSdk::Utils::KineticHttpResponse] object, with +code+, +message+, +content_string+, and +content+ properties
35
- def jwt_code(client_id, headers = default_headers)
36
- @logger.info("Retrieving JWT authorization code")
37
- url = "#{@server}/app/oauth2/authorize?grant_type=authorization_code&response_type=code&client_id=#{client_id}"
38
- response = post(url, {}, headers, { :max_redirects => -1 })
39
-
40
- if response.status == 401
41
- raise StandardError.new "#{response.message}: #{response.content["error"]}"
42
- elsif response.status == 302 || response.status == 303
43
- location = response.headers["location"]
44
- if location.nil?
45
- raise StandardError.new "Unable to retrieve code: #{response.inspect}"
46
- elsif !location.include?("?code=")
47
- raise StandardError.new "Unable to retrieve code, the authorize endpoint redirected to #{location} without an authorization code."
48
- else
49
- location.split("?code=").last.split("#/").first
50
- end
63
+ # @param response [KineticSdk::Utils::KineticHttpResponse] the failed response
64
+ # @return [String] a single line description of the failure
65
+ def oauth_error_message(response)
66
+ content = response.content.is_a?(Hash) ? response.content : {}
67
+ if content["error"]
68
+ message = "#{response.status} #{content["error"]}"
69
+ message += " - #{content["error_description"]}" if content["error_description"]
70
+ message
51
71
  else
52
- raise StandardError.new "Unable to retrieve code #{response.inspect}"
72
+ message = "#{response.status} #{response.message}"
73
+ headers = response.headers
74
+ correlation_id = headers.is_a?(Hash) ? headers["x-kinetic-cid"] : nil
75
+ message += " (correlation id #{correlation_id})" if correlation_id
76
+ message
53
77
  end
54
78
  end
55
79
  end
@@ -3,5 +3,5 @@ module KineticSdk
3
3
  # Version of Kinetic SDK
4
4
  #
5
5
  # @return [String] Version of the SDK
6
- VERSION = "7.0.0.rc3"
6
+ VERSION = "7.0.0.rc4"
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kinetic_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.0.rc3
4
+ version: 7.0.0.rc4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kinetic Data