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 +4 -4
- data/CHANGELOG.md +34 -0
- data/lib/kinetic_sdk/core/core-sdk.rb +9 -1
- data/lib/kinetic_sdk/core/lib/jwt.rb +59 -35
- data/lib/kinetic_sdk/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0ab81a960e1aebbf2e7cd839fdf3fd60c33ca71e985e0c6530c7e84ca8e8ec6f
|
|
4
|
+
data.tar.gz: d485a88f2085aa142c0015c2899999d46c403f66b8b460efffb0918730ca51aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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]
|
|
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 =
|
|
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 = "#{@
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
50
|
+
raise StandardError.new("Unable to retrieve token: #{oauth_error_message(response)}")
|
|
25
51
|
end
|
|
26
52
|
end
|
|
27
53
|
|
|
28
|
-
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Builds an actionable message from an OAuth error response.
|
|
29
57
|
#
|
|
30
|
-
#
|
|
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
|
|
33
|
-
# @
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
data/lib/kinetic_sdk/version.rb
CHANGED