dodopayments 2.35.0 → 2.36.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/CHANGELOG.md +9 -0
- data/README.md +21 -1
- data/lib/dodopayments/client.rb +133 -4
- data/lib/dodopayments/version.rb +1 -1
- data/lib/dodopayments/workload_identity.rb +351 -0
- data/lib/dodopayments.rb +1 -0
- data/rbi/dodopayments/client.rbi +15 -2
- data/rbi/dodopayments/workload_identity.rbi +219 -0
- data/sig/dodopayments/client.rbs +6 -1
- data/sig/dodopayments/workload_identity.rbs +89 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 92bc6483e15b2acb4336b40dc7b0506cfcf6459c1df9534b5f2298f5c914578f
|
|
4
|
+
data.tar.gz: 031e012d15a53b9e54b167ecd3c294c82eec15c36f6553791b9831e53cdb5355
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a58eba2a64ac73d40c74e345aca8a06e15ecea1bbaa9e64e4812431ade3778748fe4fcdf543bba82fe8031fba96f9b25ab3cfd7059fc375dd27de5d348a28a5
|
|
7
|
+
data.tar.gz: 365c61a379aab7d5500c558a76d7153dda3486a4fabdd4af7e90185752aeeb18f2fa45f38116e714e58d5394315decff720996ed7c8ff31c6c111e6e9b35510a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.36.0](https://github.com/dodopayments/dodopayments-ruby/compare/v2.35.0...v2.36.0) (2026-10-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **api:** regenerate SDK from latest OpenAPI spec ([8f4451c](https://github.com/dodopayments/dodopayments-ruby/commit/8f4451cf4bee3e5ed70f7d83b0e59ca1ca822a1e))
|
|
9
|
+
* **api:** regenerate SDK from latest OpenAPI spec ([3089c6d](https://github.com/dodopayments/dodopayments-ruby/commit/3089c6df767756323fc412e46b4c78100727dd0b))
|
|
10
|
+
* **api:** regenerate SDK from latest OpenAPI spec ([075d355](https://github.com/dodopayments/dodopayments-ruby/commit/075d35555b354afd7d6b3a4d16d1924805c28b4a))
|
|
11
|
+
|
|
3
12
|
## [2.35.0](https://github.com/dodopayments/dodopayments-ruby/compare/v2.34.0...v2.35.0) (2026-10-24)
|
|
4
13
|
|
|
5
14
|
|
data/README.md
CHANGED
|
@@ -23,7 +23,7 @@ To use this gem, install via Bundler by adding the following to your application
|
|
|
23
23
|
<!-- x-release-please-start-version -->
|
|
24
24
|
|
|
25
25
|
```ruby
|
|
26
|
-
gem "dodopayments", "~> 2.
|
|
26
|
+
gem "dodopayments", "~> 2.36.0"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
<!-- x-release-please-end -->
|
|
@@ -72,6 +72,26 @@ if page.next_page?
|
|
|
72
72
|
end
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
### Workload identity
|
|
76
|
+
|
|
77
|
+
Instead of an API key, the client can exchange an OIDC token (JWT) from your platform for a short-lived access token. The client caches the access token and gets a new one when 5 minutes or less remain.
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
dodo_payments = Dodopayments::Client.new(
|
|
81
|
+
workload_identity: Dodopayments::WorkloadIdentity::Config.new(
|
|
82
|
+
identity_provider_id: "wip_...", # defaults to ENV["DODO_PAYMENTS_IDENTITY_PROVIDER_ID"]
|
|
83
|
+
provider: Dodopayments::WorkloadIdentity.github_actions
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Revoke the cached access token, for example at the end of a job.
|
|
88
|
+
dodo_payments.workload_identity.revoke
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The `Dodopayments::WorkloadIdentity` module has these token sources: `github_actions`, `gitlab`, `terraform_cloud`, `gcp`, `vercel`, `kubernetes`, `file`, `aws` (wraps your function), `static`, and `auto`. A `provider` can also be any object that responds to `call` and returns the JWT.
|
|
92
|
+
|
|
93
|
+
Do not set `bearer_token` together with `workload_identity`. If you set neither, and `ENV["DODO_PAYMENTS_API_KEY"]` is not set, the client reads `ENV["DODO_PAYMENTS_IDENTITY_PROVIDER_ID"]` and uses the token source that `auto` finds.
|
|
94
|
+
|
|
75
95
|
### Handling errors
|
|
76
96
|
|
|
77
97
|
When the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `Dodopayments::Errors::APIError` will be thrown:
|
data/lib/dodopayments/client.rb
CHANGED
|
@@ -21,12 +21,17 @@ module Dodopayments
|
|
|
21
21
|
# rubocop:enable Style/MutableConstant
|
|
22
22
|
|
|
23
23
|
# Bearer Token for API authentication
|
|
24
|
-
# @return [String]
|
|
24
|
+
# @return [String, nil]
|
|
25
25
|
attr_reader :bearer_token
|
|
26
26
|
|
|
27
27
|
# @return [String, nil]
|
|
28
28
|
attr_reader :webhook_key
|
|
29
29
|
|
|
30
|
+
# Workload identity session, or `nil` if the client uses a bearer token.
|
|
31
|
+
#
|
|
32
|
+
# @return [Dodopayments::WorkloadIdentity::Session, nil]
|
|
33
|
+
attr_reader :workload_identity
|
|
34
|
+
|
|
30
35
|
# @return [Dodopayments::Resources::CheckoutSessions]
|
|
31
36
|
attr_reader :checkout_sessions
|
|
32
37
|
|
|
@@ -114,12 +119,114 @@ module Dodopayments
|
|
|
114
119
|
{"authorization" => "Bearer #{@bearer_token}"}
|
|
115
120
|
end
|
|
116
121
|
|
|
122
|
+
# @api private
|
|
123
|
+
#
|
|
124
|
+
# With workload identity, a 401 response clears the cached access token. The
|
|
125
|
+
# client then gets a new access token and sends the request one more time.
|
|
126
|
+
#
|
|
127
|
+
# @param req [Hash{Symbol=>Object}]
|
|
128
|
+
#
|
|
129
|
+
# @raise [Dodopayments::Errors::APIError]
|
|
130
|
+
# @return [Object]
|
|
131
|
+
def request(req)
|
|
132
|
+
return super if @workload_identity.nil?
|
|
133
|
+
|
|
134
|
+
token = @workload_identity.access_token
|
|
135
|
+
begin
|
|
136
|
+
super(workload_identity_authorize(req, token))
|
|
137
|
+
rescue Dodopayments::Errors::AuthenticationError
|
|
138
|
+
@workload_identity.invalidate(token)
|
|
139
|
+
super(workload_identity_authorize(req, @workload_identity.access_token))
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @api private
|
|
144
|
+
#
|
|
145
|
+
# @param req [Hash{Symbol=>Object}]
|
|
146
|
+
# @param token [String]
|
|
147
|
+
#
|
|
148
|
+
# @return [Hash{Symbol=>Object}]
|
|
149
|
+
private def workload_identity_authorize(req, token)
|
|
150
|
+
{**req, headers: {**req[:headers].to_h, "authorization" => "Bearer #{token}"}}
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# @api private
|
|
154
|
+
#
|
|
155
|
+
# Sends a workload identity request without the client credentials. It retries
|
|
156
|
+
# connection errors and 5xx responses, but not 4xx responses.
|
|
157
|
+
#
|
|
158
|
+
# @param method [Symbol]
|
|
159
|
+
# @param path [String]
|
|
160
|
+
# @param headers [Hash{String=>String}]
|
|
161
|
+
# @param body [Object, nil]
|
|
162
|
+
#
|
|
163
|
+
# @raise [Dodopayments::Errors::APIError]
|
|
164
|
+
# @return [Object]
|
|
165
|
+
private def workload_identity_request(method:, path:, headers:, body:)
|
|
166
|
+
url = Dodopayments::Internal::Util.join_parsed_uri(@base_url_components, {path: path})
|
|
167
|
+
headers = Dodopayments::Internal::Util.normalized_headers(@headers.except("authorization"), headers)
|
|
168
|
+
encoded = nil
|
|
169
|
+
if body.nil?
|
|
170
|
+
headers.delete("content-type")
|
|
171
|
+
else
|
|
172
|
+
headers, encoded = Dodopayments::Internal::Util.encode_content(headers, body)
|
|
173
|
+
end
|
|
174
|
+
request = {method: method, url: url, headers: headers, body: encoded, max_retries: 0, timeout: @timeout}
|
|
175
|
+
|
|
176
|
+
retry_count = 0
|
|
177
|
+
begin
|
|
178
|
+
_, response, stream = send_request(
|
|
179
|
+
request,
|
|
180
|
+
redirect_count: 0,
|
|
181
|
+
retry_count: retry_count,
|
|
182
|
+
send_retry_header: true
|
|
183
|
+
)
|
|
184
|
+
rescue Dodopayments::Errors::APIConnectionError, Dodopayments::Errors::InternalServerError => e
|
|
185
|
+
if retry_count < @max_retries
|
|
186
|
+
sleep(retry_delay(e.headers.to_h, retry_count: retry_count))
|
|
187
|
+
retry_count += 1
|
|
188
|
+
retry
|
|
189
|
+
end
|
|
190
|
+
raise workload_identity_error(e)
|
|
191
|
+
rescue Dodopayments::Errors::APIStatusError => e
|
|
192
|
+
raise workload_identity_error(e)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
response_headers = Dodopayments::Internal::Util.normalized_headers(response.each_header.to_h)
|
|
196
|
+
Dodopayments::Internal::Util.decode_content(response_headers, stream: stream, suppress_error: true)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# @api private
|
|
200
|
+
#
|
|
201
|
+
# @param error [Dodopayments::Errors::APIError]
|
|
202
|
+
#
|
|
203
|
+
# @return [Dodopayments::Errors::APIError]
|
|
204
|
+
private def workload_identity_error(error)
|
|
205
|
+
return error unless error.is_a?(Dodopayments::Errors::APIStatusError)
|
|
206
|
+
|
|
207
|
+
body = error.body
|
|
208
|
+
details = body.is_a?(Hash) ? [body[:error], body[:error_description]].compact : []
|
|
209
|
+
message = ["Workload identity request failed with status #{error.status}", *details].join(": ")
|
|
210
|
+
Dodopayments::Errors::APIStatusError.for(
|
|
211
|
+
url: error.url,
|
|
212
|
+
status: error.status.to_i,
|
|
213
|
+
headers: error.headers,
|
|
214
|
+
body: body,
|
|
215
|
+
request: nil,
|
|
216
|
+
response: nil,
|
|
217
|
+
message: message
|
|
218
|
+
)
|
|
219
|
+
end
|
|
220
|
+
|
|
117
221
|
# Creates and returns a new client for interacting with the API.
|
|
118
222
|
#
|
|
119
223
|
# @param bearer_token [String, nil] Bearer Token for API authentication Defaults to `ENV["DODO_PAYMENTS_API_KEY"]`
|
|
120
224
|
#
|
|
121
225
|
# @param webhook_key [String, nil] Defaults to `ENV["DODO_PAYMENTS_WEBHOOK_KEY"]`
|
|
122
226
|
#
|
|
227
|
+
# @param workload_identity [Dodopayments::WorkloadIdentity::Config, Hash{Symbol=>Object}, nil] Exchange an OIDC token for an access token, instead of a bearer token. See
|
|
228
|
+
# {Dodopayments::WorkloadIdentity}.
|
|
229
|
+
#
|
|
123
230
|
# @param environment [:live_mode, :test_mode, nil] Specifies the environment to use for the API.
|
|
124
231
|
#
|
|
125
232
|
# Each environment maps to a different base URL:
|
|
@@ -138,8 +245,9 @@ module Dodopayments
|
|
|
138
245
|
#
|
|
139
246
|
# @param max_retry_delay [Float]
|
|
140
247
|
def initialize(
|
|
141
|
-
bearer_token:
|
|
248
|
+
bearer_token: nil,
|
|
142
249
|
webhook_key: ENV["DODO_PAYMENTS_WEBHOOK_KEY"],
|
|
250
|
+
workload_identity: nil,
|
|
143
251
|
environment: nil,
|
|
144
252
|
base_url: ENV["DODO_PAYMENTS_BASE_URL"],
|
|
145
253
|
max_retries: self.class::DEFAULT_MAX_RETRIES,
|
|
@@ -152,7 +260,17 @@ module Dodopayments
|
|
|
152
260
|
raise ArgumentError.new(message)
|
|
153
261
|
end
|
|
154
262
|
|
|
155
|
-
|
|
263
|
+
unless bearer_token.nil? || workload_identity.nil?
|
|
264
|
+
raise ArgumentError.new("bearer_token and workload_identity cannot be used together")
|
|
265
|
+
end
|
|
266
|
+
bearer_token = ENV["DODO_PAYMENTS_API_KEY"] if bearer_token.nil? && workload_identity.nil?
|
|
267
|
+
if bearer_token.nil? && workload_identity.nil? && !ENV["DODO_PAYMENTS_IDENTITY_PROVIDER_ID"].to_s.empty?
|
|
268
|
+
provider = Dodopayments::WorkloadIdentity.auto
|
|
269
|
+
workload_identity = {provider: provider} unless provider.nil?
|
|
270
|
+
end
|
|
271
|
+
workload_identity = Dodopayments::WorkloadIdentity::Config.coerce(workload_identity) unless workload_identity.nil?
|
|
272
|
+
|
|
273
|
+
if bearer_token.nil? && workload_identity.nil?
|
|
156
274
|
raise ArgumentError.new("bearer_token is required, and can be set via environ: \"DODO_PAYMENTS_API_KEY\"")
|
|
157
275
|
end
|
|
158
276
|
|
|
@@ -169,7 +287,7 @@ module Dodopayments
|
|
|
169
287
|
headers = parsed.merge(headers)
|
|
170
288
|
end
|
|
171
289
|
|
|
172
|
-
@bearer_token = bearer_token
|
|
290
|
+
@bearer_token = bearer_token&.to_s
|
|
173
291
|
@webhook_key = webhook_key&.to_s
|
|
174
292
|
|
|
175
293
|
super(
|
|
@@ -207,6 +325,17 @@ module Dodopayments
|
|
|
207
325
|
@entitlements = Dodopayments::Resources::Entitlements.new(client: self)
|
|
208
326
|
@product_collections = Dodopayments::Resources::ProductCollections.new(client: self)
|
|
209
327
|
@moderation = Dodopayments::Resources::Moderation.new(client: self)
|
|
328
|
+
|
|
329
|
+
@workload_identity = nil
|
|
330
|
+
return if workload_identity.nil?
|
|
331
|
+
@workload_identity = Dodopayments::WorkloadIdentity::Session.new(
|
|
332
|
+
config: workload_identity,
|
|
333
|
+
context: Dodopayments::WorkloadIdentity::Context.new(
|
|
334
|
+
base_url: @base_url,
|
|
335
|
+
audience: Dodopayments::Internal::Util.uri_origin(@base_url)
|
|
336
|
+
),
|
|
337
|
+
transport: method(:workload_identity_request)
|
|
338
|
+
)
|
|
210
339
|
end
|
|
211
340
|
end
|
|
212
341
|
end
|
data/lib/dodopayments/version.rb
CHANGED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dodopayments
|
|
4
|
+
# Workload identity federation: exchange a short-lived OIDC token (JWT) from
|
|
5
|
+
# your platform for a short-lived API access token.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# client = Dodopayments::Client.new(
|
|
9
|
+
# workload_identity: Dodopayments::WorkloadIdentity::Config.new(
|
|
10
|
+
# identity_provider_id: "wip_...",
|
|
11
|
+
# provider: Dodopayments::WorkloadIdentity.github_actions
|
|
12
|
+
# )
|
|
13
|
+
# )
|
|
14
|
+
module WorkloadIdentity
|
|
15
|
+
# @api private
|
|
16
|
+
GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"
|
|
20
|
+
|
|
21
|
+
# The client refreshes the access token when this many seconds or less remain.
|
|
22
|
+
REFRESH_WINDOW_IN_SECONDS = 300
|
|
23
|
+
|
|
24
|
+
# @api private
|
|
25
|
+
PROVIDER_TIMEOUT_IN_SECONDS = 10
|
|
26
|
+
|
|
27
|
+
# @api private
|
|
28
|
+
KUBERNETES_TOKEN_PATH = "/var/run/secrets/tokens/dodo-token"
|
|
29
|
+
|
|
30
|
+
# @api private
|
|
31
|
+
GCP_METADATA_URL =
|
|
32
|
+
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity"
|
|
33
|
+
|
|
34
|
+
# The values that a subject token provider gets on each token exchange.
|
|
35
|
+
class Context
|
|
36
|
+
# The base URL of the client.
|
|
37
|
+
# @return [URI::Generic]
|
|
38
|
+
attr_reader :base_url
|
|
39
|
+
|
|
40
|
+
# The default audience: the origin of the client base URL.
|
|
41
|
+
# @return [String]
|
|
42
|
+
attr_reader :audience
|
|
43
|
+
|
|
44
|
+
# @api private
|
|
45
|
+
#
|
|
46
|
+
# @param base_url [URI::Generic]
|
|
47
|
+
# @param audience [String]
|
|
48
|
+
def initialize(base_url:, audience:)
|
|
49
|
+
@base_url = base_url
|
|
50
|
+
@audience = audience
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# The `workload_identity` option of {Dodopayments::Client}.
|
|
55
|
+
class Config
|
|
56
|
+
# @return [String]
|
|
57
|
+
attr_reader :identity_provider_id
|
|
58
|
+
|
|
59
|
+
# @return [#call]
|
|
60
|
+
attr_reader :provider
|
|
61
|
+
|
|
62
|
+
# @param provider [#call] Returns the subject token (a JWT string). It can take one
|
|
63
|
+
# argument, a {Dodopayments::WorkloadIdentity::Context}.
|
|
64
|
+
#
|
|
65
|
+
# @param identity_provider_id [String, nil] Defaults to `ENV["DODO_PAYMENTS_IDENTITY_PROVIDER_ID"]`
|
|
66
|
+
def initialize(provider:, identity_provider_id: ENV["DODO_PAYMENTS_IDENTITY_PROVIDER_ID"])
|
|
67
|
+
if identity_provider_id.nil? || identity_provider_id.to_s.empty?
|
|
68
|
+
raise ArgumentError.new(
|
|
69
|
+
"identity_provider_id is required, and can be set via environ: \"DODO_PAYMENTS_IDENTITY_PROVIDER_ID\""
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
unless provider.respond_to?(:call)
|
|
73
|
+
raise ArgumentError.new("provider must respond to `call`")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
@identity_provider_id = identity_provider_id.to_s
|
|
77
|
+
@provider = provider
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @api private
|
|
81
|
+
#
|
|
82
|
+
# @param value [Dodopayments::WorkloadIdentity::Config, Hash{Symbol=>Object}]
|
|
83
|
+
#
|
|
84
|
+
# @return [Dodopayments::WorkloadIdentity::Config]
|
|
85
|
+
def self.coerce(value)
|
|
86
|
+
case value
|
|
87
|
+
in Dodopayments::WorkloadIdentity::Config
|
|
88
|
+
value
|
|
89
|
+
in Hash
|
|
90
|
+
new(**value)
|
|
91
|
+
else
|
|
92
|
+
raise ArgumentError.new("workload_identity must be a #{name} or a Hash, got #{value.class}")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @return [String]
|
|
97
|
+
def inspect = "#<#{self.class.name} identity_provider_id=#{@identity_provider_id}>"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Caches the access token of one client. All methods are thread-safe.
|
|
101
|
+
class Session
|
|
102
|
+
# @return [String]
|
|
103
|
+
attr_reader :identity_provider_id
|
|
104
|
+
|
|
105
|
+
# @api private
|
|
106
|
+
#
|
|
107
|
+
# @param config [Dodopayments::WorkloadIdentity::Config]
|
|
108
|
+
# @param context [Dodopayments::WorkloadIdentity::Context]
|
|
109
|
+
# @param transport [Proc] Sends a request without the client credentials.
|
|
110
|
+
def initialize(config:, context:, transport:)
|
|
111
|
+
@identity_provider_id = config.identity_provider_id
|
|
112
|
+
@provider = config.provider
|
|
113
|
+
@context = context
|
|
114
|
+
@transport = transport
|
|
115
|
+
@mutex = Mutex.new
|
|
116
|
+
@access_token = nil
|
|
117
|
+
@expires_at = nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Returns a valid access token. This method does a token exchange if no token is
|
|
121
|
+
# cached, or if 5 minutes or less remain. Concurrent callers share one exchange.
|
|
122
|
+
#
|
|
123
|
+
# @raise [Dodopayments::Errors::APIError]
|
|
124
|
+
# @return [String]
|
|
125
|
+
def access_token
|
|
126
|
+
@mutex.synchronize do
|
|
127
|
+
exchange! unless fresh?
|
|
128
|
+
@access_token.to_s
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @api private
|
|
133
|
+
#
|
|
134
|
+
# Clears the cache, but only if it still holds `token`.
|
|
135
|
+
#
|
|
136
|
+
# @param token [String, nil]
|
|
137
|
+
def invalidate(token = nil)
|
|
138
|
+
@mutex.synchronize do
|
|
139
|
+
clear! if token.nil? || token == @access_token
|
|
140
|
+
end
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Revokes the cached access token and clears the cache. Does nothing if no token
|
|
145
|
+
# is cached.
|
|
146
|
+
#
|
|
147
|
+
# @raise [Dodopayments::Errors::APIError]
|
|
148
|
+
def revoke
|
|
149
|
+
token = @mutex.synchronize { @access_token }
|
|
150
|
+
return if token.nil?
|
|
151
|
+
|
|
152
|
+
begin
|
|
153
|
+
@transport.call(
|
|
154
|
+
method: :post,
|
|
155
|
+
path: "workload-identity/token/revoke",
|
|
156
|
+
headers: {"authorization" => "Bearer #{token}"},
|
|
157
|
+
body: nil
|
|
158
|
+
)
|
|
159
|
+
ensure
|
|
160
|
+
invalidate(token)
|
|
161
|
+
end
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# @return [String]
|
|
166
|
+
def inspect = "#<#{self.class.name} identity_provider_id=#{@identity_provider_id}>"
|
|
167
|
+
|
|
168
|
+
private def fresh?
|
|
169
|
+
!@access_token.nil? && !@expires_at.nil? && @expires_at - Time.now > REFRESH_WINDOW_IN_SECONDS
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
private def clear!
|
|
173
|
+
@access_token = nil
|
|
174
|
+
@expires_at = nil
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
private def subject_token
|
|
178
|
+
token = @provider.respond_to?(:arity) && @provider.arity.zero? ? @provider.call : @provider.call(@context)
|
|
179
|
+
if !token.is_a?(String) || token.strip.empty?
|
|
180
|
+
raise Dodopayments::Errors::Error.new("The workload identity provider did not return a token")
|
|
181
|
+
end
|
|
182
|
+
token.strip
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private def exchange!
|
|
186
|
+
clear!
|
|
187
|
+
body = {
|
|
188
|
+
grant_type: GRANT_TYPE,
|
|
189
|
+
subject_token_type: SUBJECT_TOKEN_TYPE,
|
|
190
|
+
subject_token: subject_token,
|
|
191
|
+
identity_provider_id: @identity_provider_id
|
|
192
|
+
}
|
|
193
|
+
now = Time.now
|
|
194
|
+
response = @transport.call(method: :post, path: "workload-identity/token", headers: {}, body: body)
|
|
195
|
+
token = response.is_a?(Hash) ? response[:access_token] : nil
|
|
196
|
+
if !token.is_a?(String) || token.empty?
|
|
197
|
+
raise Dodopayments::Errors::Error.new("The token exchange response has no access_token")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
@expires_at = expiry(response, now: now)
|
|
201
|
+
@access_token = token
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
private def expiry(response, now:)
|
|
205
|
+
expires_at = response[:expires_at]
|
|
206
|
+
if expires_at.is_a?(String)
|
|
207
|
+
begin
|
|
208
|
+
return Time.iso8601(expires_at)
|
|
209
|
+
rescue ArgumentError
|
|
210
|
+
nil
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
now + Float(response[:expires_in], exception: false).to_f
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
class << self
|
|
218
|
+
# GitHub Actions. The job must have the `id-token: write` permission.
|
|
219
|
+
#
|
|
220
|
+
# @param audience [String, nil] Defaults to the origin of the client base URL.
|
|
221
|
+
#
|
|
222
|
+
# @return [Proc]
|
|
223
|
+
def github_actions(audience: nil)
|
|
224
|
+
lambda do |context|
|
|
225
|
+
url = env!("ACTIONS_ID_TOKEN_REQUEST_URL")
|
|
226
|
+
request_token = env!("ACTIONS_ID_TOKEN_REQUEST_TOKEN")
|
|
227
|
+
separator = url.include?("?") ? "&" : "?"
|
|
228
|
+
uri = URI("#{url}#{separator}audience=#{CGI.escape(audience || context.audience)}")
|
|
229
|
+
body = http_get(uri, {"authorization" => "bearer #{request_token}"})
|
|
230
|
+
JSON.parse(body).fetch("value")
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# GitLab CI/CD. Declare the token in `id_tokens` of the job.
|
|
235
|
+
#
|
|
236
|
+
# @param env_var [String]
|
|
237
|
+
#
|
|
238
|
+
# @return [Proc]
|
|
239
|
+
def gitlab(env_var: "DODO_PAYMENTS_ID_TOKEN") = ->(_context = nil) { env!(env_var) }
|
|
240
|
+
|
|
241
|
+
# HCP Terraform (Terraform Cloud) dynamic credentials.
|
|
242
|
+
#
|
|
243
|
+
# @return [Proc]
|
|
244
|
+
def terraform_cloud
|
|
245
|
+
lambda do |_context = nil|
|
|
246
|
+
ENV["TFC_WORKLOAD_IDENTITY_TOKEN"] || env!("TFC_WORKLOAD_IDENTITY_TOKEN_DODO")
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Google Cloud: the metadata server of the attached service account.
|
|
251
|
+
#
|
|
252
|
+
# @param audience [String, nil] Defaults to the origin of the client base URL.
|
|
253
|
+
#
|
|
254
|
+
# @return [Proc]
|
|
255
|
+
def gcp(audience: nil)
|
|
256
|
+
lambda do |context|
|
|
257
|
+
query = URI.encode_www_form(audience: audience || context.audience, format: "full")
|
|
258
|
+
http_get(URI("#{GCP_METADATA_URL}?#{query}"), {"metadata-flavor" => "Google"})
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Vercel OIDC federation.
|
|
263
|
+
#
|
|
264
|
+
# @return [Proc]
|
|
265
|
+
def vercel = ->(_context = nil) { env!("VERCEL_OIDC_TOKEN") }
|
|
266
|
+
|
|
267
|
+
# Kubernetes: a projected service account token. The file is read on each exchange.
|
|
268
|
+
#
|
|
269
|
+
# @param path [String]
|
|
270
|
+
#
|
|
271
|
+
# @return [Proc]
|
|
272
|
+
def kubernetes(path: KUBERNETES_TOKEN_PATH) = file(path: path)
|
|
273
|
+
|
|
274
|
+
# A token file. The file is read on each exchange.
|
|
275
|
+
#
|
|
276
|
+
# @param path [String, nil] Defaults to `ENV["DODO_PAYMENTS_IDENTITY_TOKEN_FILE"]`
|
|
277
|
+
#
|
|
278
|
+
# @return [Proc]
|
|
279
|
+
def file(path: ENV["DODO_PAYMENTS_IDENTITY_TOKEN_FILE"])
|
|
280
|
+
if path.nil? || path.empty?
|
|
281
|
+
raise ArgumentError.new(
|
|
282
|
+
"path is required, and can be set via environ: \"DODO_PAYMENTS_IDENTITY_TOKEN_FILE\""
|
|
283
|
+
)
|
|
284
|
+
end
|
|
285
|
+
->(_context = nil) { File.read(path).strip }
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# AWS: wraps your function that returns the JWT, for example from STS
|
|
289
|
+
# `GetWebIdentityToken`.
|
|
290
|
+
#
|
|
291
|
+
# @param fn [#call, nil]
|
|
292
|
+
#
|
|
293
|
+
# @return [Proc]
|
|
294
|
+
def aws(fn = nil, &blk)
|
|
295
|
+
callable = fn || blk
|
|
296
|
+
raise ArgumentError.new("aws requires a function or a block") if callable.nil?
|
|
297
|
+
->(_context = nil) { callable.call }
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# A fixed token. Use it for tests.
|
|
301
|
+
#
|
|
302
|
+
# @param token [String]
|
|
303
|
+
#
|
|
304
|
+
# @return [Proc]
|
|
305
|
+
def static(token) = ->(_context = nil) { token }
|
|
306
|
+
|
|
307
|
+
# Finds a token source in the environment, in this order: GitHub Actions, HCP
|
|
308
|
+
# Terraform, `ENV["DODO_PAYMENTS_IDENTITY_TOKEN_FILE"]`, and the Kubernetes path.
|
|
309
|
+
#
|
|
310
|
+
# @return [Proc, nil] `nil` if no source is found.
|
|
311
|
+
def auto
|
|
312
|
+
if ENV["ACTIONS_ID_TOKEN_REQUEST_URL"] && ENV["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
|
|
313
|
+
return github_actions
|
|
314
|
+
end
|
|
315
|
+
if ENV["TFC_WORKLOAD_IDENTITY_TOKEN"] || ENV["TFC_WORKLOAD_IDENTITY_TOKEN_DODO"]
|
|
316
|
+
return terraform_cloud
|
|
317
|
+
end
|
|
318
|
+
path = ENV["DODO_PAYMENTS_IDENTITY_TOKEN_FILE"]
|
|
319
|
+
return file(path: path) unless path.nil? || path.empty?
|
|
320
|
+
return kubernetes if File.file?(KUBERNETES_TOKEN_PATH)
|
|
321
|
+
nil
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
private def env!(name)
|
|
325
|
+
value = ENV[name]
|
|
326
|
+
if value.nil? || value.empty?
|
|
327
|
+
raise Dodopayments::Errors::Error.new("The environment variable #{name} is not set")
|
|
328
|
+
end
|
|
329
|
+
value
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
private def http_get(uri, headers)
|
|
333
|
+
response = Net::HTTP.start(
|
|
334
|
+
uri.host,
|
|
335
|
+
uri.port,
|
|
336
|
+
use_ssl: uri.scheme == "https",
|
|
337
|
+
open_timeout: PROVIDER_TIMEOUT_IN_SECONDS,
|
|
338
|
+
read_timeout: PROVIDER_TIMEOUT_IN_SECONDS
|
|
339
|
+
) do |http|
|
|
340
|
+
http.get(uri.request_uri, headers)
|
|
341
|
+
end
|
|
342
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
343
|
+
raise Dodopayments::Errors::Error.new(
|
|
344
|
+
"The subject token request to #{uri.host} failed with status #{response.code}"
|
|
345
|
+
)
|
|
346
|
+
end
|
|
347
|
+
response.body.to_s
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
data/lib/dodopayments.rb
CHANGED
|
@@ -55,6 +55,7 @@ require_relative "dodopayments/internal/transport/pooled_net_requester"
|
|
|
55
55
|
require_relative "dodopayments/client"
|
|
56
56
|
require_relative "dodopayments/internal/cursor_page_pagination"
|
|
57
57
|
require_relative "dodopayments/internal/default_page_number_pagination"
|
|
58
|
+
require_relative "dodopayments/workload_identity"
|
|
58
59
|
require_relative "dodopayments/models/blocklist/block_by_email"
|
|
59
60
|
require_relative "dodopayments/models/blocklist/block_by_customer_id"
|
|
60
61
|
require_relative "dodopayments/models/blocklist/create_blocked_customer_request"
|
data/rbi/dodopayments/client.rbi
CHANGED
|
@@ -20,12 +20,15 @@ module Dodopayments
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
# Bearer Token for API authentication
|
|
23
|
-
sig { returns(String) }
|
|
23
|
+
sig { returns(T.nilable(String)) }
|
|
24
24
|
attr_reader :bearer_token
|
|
25
25
|
|
|
26
26
|
sig { returns(T.nilable(String)) }
|
|
27
27
|
attr_reader :webhook_key
|
|
28
28
|
|
|
29
|
+
sig { returns(T.nilable(Dodopayments::WorkloadIdentity::Session)) }
|
|
30
|
+
attr_reader :workload_identity
|
|
31
|
+
|
|
29
32
|
sig { returns(Dodopayments::Resources::CheckoutSessions) }
|
|
30
33
|
attr_reader :checkout_sessions
|
|
31
34
|
|
|
@@ -114,6 +117,13 @@ module Dodopayments
|
|
|
114
117
|
params(
|
|
115
118
|
bearer_token: T.nilable(String),
|
|
116
119
|
webhook_key: T.nilable(String),
|
|
120
|
+
workload_identity:
|
|
121
|
+
T.nilable(
|
|
122
|
+
T.any(
|
|
123
|
+
Dodopayments::WorkloadIdentity::Config,
|
|
124
|
+
T::Hash[Symbol, T.anything]
|
|
125
|
+
)
|
|
126
|
+
),
|
|
117
127
|
environment: T.nilable(T.any(Symbol, String)),
|
|
118
128
|
base_url: T.nilable(String),
|
|
119
129
|
max_retries: Integer,
|
|
@@ -124,9 +134,12 @@ module Dodopayments
|
|
|
124
134
|
end
|
|
125
135
|
def self.new(
|
|
126
136
|
# Bearer Token for API authentication Defaults to `ENV["DODO_PAYMENTS_API_KEY"]`
|
|
127
|
-
bearer_token:
|
|
137
|
+
bearer_token: nil,
|
|
128
138
|
# Defaults to `ENV["DODO_PAYMENTS_WEBHOOK_KEY"]`
|
|
129
139
|
webhook_key: ENV["DODO_PAYMENTS_WEBHOOK_KEY"],
|
|
140
|
+
# Exchange an OIDC token for an access token, instead of a bearer token. See
|
|
141
|
+
# {Dodopayments::WorkloadIdentity}.
|
|
142
|
+
workload_identity: nil,
|
|
130
143
|
# Specifies the environment to use for the API.
|
|
131
144
|
#
|
|
132
145
|
# Each environment maps to a different base URL:
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
|
|
3
|
+
# typed: strong
|
|
4
|
+
|
|
5
|
+
module Dodopayments
|
|
6
|
+
# Workload identity federation: exchange a short-lived OIDC token (JWT) from
|
|
7
|
+
# your platform for a short-lived API access token.
|
|
8
|
+
module WorkloadIdentity
|
|
9
|
+
GRANT_TYPE = T.let(T.unsafe(nil), String)
|
|
10
|
+
|
|
11
|
+
SUBJECT_TOKEN_TYPE = T.let(T.unsafe(nil), String)
|
|
12
|
+
|
|
13
|
+
# The client refreshes the access token when this many seconds or less remain.
|
|
14
|
+
REFRESH_WINDOW_IN_SECONDS = T.let(T.unsafe(nil), Integer)
|
|
15
|
+
|
|
16
|
+
PROVIDER_TIMEOUT_IN_SECONDS = T.let(T.unsafe(nil), Integer)
|
|
17
|
+
|
|
18
|
+
KUBERNETES_TOKEN_PATH = T.let(T.unsafe(nil), String)
|
|
19
|
+
|
|
20
|
+
GCP_METADATA_URL = T.let(T.unsafe(nil), String)
|
|
21
|
+
|
|
22
|
+
# The values that a subject token provider gets on each token exchange.
|
|
23
|
+
class Context
|
|
24
|
+
# The base URL of the client.
|
|
25
|
+
sig { returns(URI::Generic) }
|
|
26
|
+
attr_reader :base_url
|
|
27
|
+
|
|
28
|
+
# The default audience: the origin of the client base URL.
|
|
29
|
+
sig { returns(String) }
|
|
30
|
+
attr_reader :audience
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
sig do
|
|
34
|
+
params(base_url: URI::Generic, audience: String).returns(
|
|
35
|
+
T.attached_class
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
def self.new(base_url:, audience:)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The `workload_identity` option of the client.
|
|
43
|
+
class Config
|
|
44
|
+
sig { returns(String) }
|
|
45
|
+
attr_reader :identity_provider_id
|
|
46
|
+
|
|
47
|
+
sig { returns(T.anything) }
|
|
48
|
+
attr_reader :provider
|
|
49
|
+
|
|
50
|
+
sig do
|
|
51
|
+
params(
|
|
52
|
+
provider: T.anything,
|
|
53
|
+
identity_provider_id: T.nilable(String)
|
|
54
|
+
).returns(T.attached_class)
|
|
55
|
+
end
|
|
56
|
+
def self.new(
|
|
57
|
+
# Returns the subject token (a JWT string). It can take one argument, a
|
|
58
|
+
# `WorkloadIdentity::Context`.
|
|
59
|
+
provider:,
|
|
60
|
+
# Defaults to `ENV["DODO_PAYMENTS_IDENTITY_PROVIDER_ID"]`
|
|
61
|
+
identity_provider_id: ENV["DODO_PAYMENTS_IDENTITY_PROVIDER_ID"]
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @api private
|
|
66
|
+
sig do
|
|
67
|
+
params(
|
|
68
|
+
value:
|
|
69
|
+
T.any(
|
|
70
|
+
Dodopayments::WorkloadIdentity::Config,
|
|
71
|
+
T::Hash[Symbol, T.anything]
|
|
72
|
+
)
|
|
73
|
+
).returns(Dodopayments::WorkloadIdentity::Config)
|
|
74
|
+
end
|
|
75
|
+
def self.coerce(value)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
sig { returns(String) }
|
|
79
|
+
def inspect
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Caches the access token of one client. All methods are thread-safe.
|
|
84
|
+
class Session
|
|
85
|
+
sig { returns(String) }
|
|
86
|
+
attr_reader :identity_provider_id
|
|
87
|
+
|
|
88
|
+
# @api private
|
|
89
|
+
sig do
|
|
90
|
+
params(
|
|
91
|
+
config: Dodopayments::WorkloadIdentity::Config,
|
|
92
|
+
context: Dodopayments::WorkloadIdentity::Context,
|
|
93
|
+
transport: T.anything
|
|
94
|
+
).returns(T.attached_class)
|
|
95
|
+
end
|
|
96
|
+
def self.new(config:, context:, transport:)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns a valid access token. This method does a token exchange if no token is
|
|
100
|
+
# cached, or if 5 minutes or less remain. Concurrent callers share one exchange.
|
|
101
|
+
sig { returns(String) }
|
|
102
|
+
def access_token
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @api private
|
|
106
|
+
sig { params(token: T.nilable(String)).returns(NilClass) }
|
|
107
|
+
def invalidate(token = nil)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Revokes the cached access token and clears the cache. Does nothing if no token
|
|
111
|
+
# is cached.
|
|
112
|
+
sig { returns(NilClass) }
|
|
113
|
+
def revoke
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
sig { returns(String) }
|
|
117
|
+
def inspect
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
Provider =
|
|
122
|
+
T.type_alias do
|
|
123
|
+
T
|
|
124
|
+
.proc
|
|
125
|
+
.params(context: Dodopayments::WorkloadIdentity::Context)
|
|
126
|
+
.returns(String)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class << self
|
|
130
|
+
# GitHub Actions. The job must have the `id-token: write` permission.
|
|
131
|
+
sig do
|
|
132
|
+
params(audience: T.nilable(String)).returns(
|
|
133
|
+
Dodopayments::WorkloadIdentity::Provider
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
def github_actions(
|
|
137
|
+
# Defaults to the origin of the client base URL.
|
|
138
|
+
audience: nil
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# GitLab CI/CD. Declare the token in `id_tokens` of the job.
|
|
143
|
+
sig do
|
|
144
|
+
params(env_var: String).returns(
|
|
145
|
+
Dodopayments::WorkloadIdentity::Provider
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
def gitlab(env_var: "DODO_PAYMENTS_ID_TOKEN")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# HCP Terraform (Terraform Cloud) dynamic credentials.
|
|
152
|
+
sig { returns(Dodopayments::WorkloadIdentity::Provider) }
|
|
153
|
+
def terraform_cloud
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Google Cloud: the metadata server of the attached service account.
|
|
157
|
+
sig do
|
|
158
|
+
params(audience: T.nilable(String)).returns(
|
|
159
|
+
Dodopayments::WorkloadIdentity::Provider
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
def gcp(
|
|
163
|
+
# Defaults to the origin of the client base URL.
|
|
164
|
+
audience: nil
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Vercel OIDC federation.
|
|
169
|
+
sig { returns(Dodopayments::WorkloadIdentity::Provider) }
|
|
170
|
+
def vercel
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Kubernetes: a projected service account token. The file is read on each
|
|
174
|
+
# exchange.
|
|
175
|
+
sig do
|
|
176
|
+
params(path: String).returns(Dodopayments::WorkloadIdentity::Provider)
|
|
177
|
+
end
|
|
178
|
+
def kubernetes(path: KUBERNETES_TOKEN_PATH)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# A token file. The file is read on each exchange.
|
|
182
|
+
sig do
|
|
183
|
+
params(path: T.nilable(String)).returns(
|
|
184
|
+
Dodopayments::WorkloadIdentity::Provider
|
|
185
|
+
)
|
|
186
|
+
end
|
|
187
|
+
def file(
|
|
188
|
+
# Defaults to `ENV["DODO_PAYMENTS_IDENTITY_TOKEN_FILE"]`
|
|
189
|
+
path: ENV["DODO_PAYMENTS_IDENTITY_TOKEN_FILE"]
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# AWS: wraps your function that returns the JWT, for example from STS
|
|
194
|
+
# `GetWebIdentityToken`.
|
|
195
|
+
sig do
|
|
196
|
+
params(
|
|
197
|
+
fn: T.nilable(T.anything),
|
|
198
|
+
blk: T.nilable(T.proc.returns(String))
|
|
199
|
+
).returns(Dodopayments::WorkloadIdentity::Provider)
|
|
200
|
+
end
|
|
201
|
+
def aws(fn = nil, &blk)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# A fixed token. Use it for tests.
|
|
205
|
+
sig do
|
|
206
|
+
params(token: String).returns(Dodopayments::WorkloadIdentity::Provider)
|
|
207
|
+
end
|
|
208
|
+
def static(token)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Finds a token source in the environment, in this order: GitHub Actions, HCP
|
|
212
|
+
# Terraform, `ENV["DODO_PAYMENTS_IDENTITY_TOKEN_FILE"]`, and the Kubernetes path.
|
|
213
|
+
# Returns `nil` if no source is found.
|
|
214
|
+
sig { returns(T.nilable(Dodopayments::WorkloadIdentity::Provider)) }
|
|
215
|
+
def auto
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
data/sig/dodopayments/client.rbs
CHANGED
|
@@ -13,10 +13,12 @@ module Dodopayments
|
|
|
13
13
|
test_mode: "https://test.dodopayments.com"
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
attr_reader bearer_token: String
|
|
16
|
+
attr_reader bearer_token: String?
|
|
17
17
|
|
|
18
18
|
attr_reader webhook_key: String?
|
|
19
19
|
|
|
20
|
+
attr_reader workload_identity: Dodopayments::WorkloadIdentity::Session?
|
|
21
|
+
|
|
20
22
|
attr_reader checkout_sessions: Dodopayments::Resources::CheckoutSessions
|
|
21
23
|
|
|
22
24
|
attr_reader payments: Dodopayments::Resources::Payments
|
|
@@ -74,6 +76,9 @@ module Dodopayments
|
|
|
74
76
|
def initialize: (
|
|
75
77
|
?bearer_token: String?,
|
|
76
78
|
?webhook_key: String?,
|
|
79
|
+
?workload_identity: Dodopayments::WorkloadIdentity::Config
|
|
80
|
+
| ::Hash[Symbol, top]
|
|
81
|
+
| nil,
|
|
77
82
|
?environment: :live_mode | :test_mode | nil,
|
|
78
83
|
?base_url: String?,
|
|
79
84
|
?max_retries: Integer,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Dodopayments
|
|
2
|
+
module WorkloadIdentity
|
|
3
|
+
GRANT_TYPE: String
|
|
4
|
+
|
|
5
|
+
SUBJECT_TOKEN_TYPE: String
|
|
6
|
+
|
|
7
|
+
REFRESH_WINDOW_IN_SECONDS: Integer
|
|
8
|
+
|
|
9
|
+
PROVIDER_TIMEOUT_IN_SECONDS: Integer
|
|
10
|
+
|
|
11
|
+
KUBERNETES_TOKEN_PATH: String
|
|
12
|
+
|
|
13
|
+
GCP_METADATA_URL: String
|
|
14
|
+
|
|
15
|
+
type provider = ^(Dodopayments::WorkloadIdentity::Context context) -> String
|
|
16
|
+
|
|
17
|
+
class Context
|
|
18
|
+
attr_reader base_url: URI::Generic
|
|
19
|
+
|
|
20
|
+
attr_reader audience: String
|
|
21
|
+
|
|
22
|
+
def initialize: (base_url: URI::Generic, audience: String) -> void
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Config
|
|
26
|
+
attr_reader identity_provider_id: String
|
|
27
|
+
|
|
28
|
+
attr_reader provider: top
|
|
29
|
+
|
|
30
|
+
def initialize: (provider: top, ?identity_provider_id: String?) -> void
|
|
31
|
+
|
|
32
|
+
def self.coerce: (
|
|
33
|
+
Dodopayments::WorkloadIdentity::Config | ::Hash[Symbol, top] value
|
|
34
|
+
) -> Dodopayments::WorkloadIdentity::Config
|
|
35
|
+
|
|
36
|
+
def inspect: -> String
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Session
|
|
40
|
+
attr_reader identity_provider_id: String
|
|
41
|
+
|
|
42
|
+
def initialize: (
|
|
43
|
+
config: Dodopayments::WorkloadIdentity::Config,
|
|
44
|
+
context: Dodopayments::WorkloadIdentity::Context,
|
|
45
|
+
transport: top
|
|
46
|
+
) -> void
|
|
47
|
+
|
|
48
|
+
def access_token: -> String
|
|
49
|
+
|
|
50
|
+
def invalidate: (?String? token) -> nil
|
|
51
|
+
|
|
52
|
+
def revoke: -> nil
|
|
53
|
+
|
|
54
|
+
def inspect: -> String
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.github_actions: (
|
|
58
|
+
?audience: String?
|
|
59
|
+
) -> Dodopayments::WorkloadIdentity::provider
|
|
60
|
+
|
|
61
|
+
def self.gitlab: (
|
|
62
|
+
?env_var: String
|
|
63
|
+
) -> Dodopayments::WorkloadIdentity::provider
|
|
64
|
+
|
|
65
|
+
def self.terraform_cloud: -> Dodopayments::WorkloadIdentity::provider
|
|
66
|
+
|
|
67
|
+
def self.gcp: (
|
|
68
|
+
?audience: String?
|
|
69
|
+
) -> Dodopayments::WorkloadIdentity::provider
|
|
70
|
+
|
|
71
|
+
def self.vercel: -> Dodopayments::WorkloadIdentity::provider
|
|
72
|
+
|
|
73
|
+
def self.kubernetes: (
|
|
74
|
+
?path: String
|
|
75
|
+
) -> Dodopayments::WorkloadIdentity::provider
|
|
76
|
+
|
|
77
|
+
def self.file: (?path: String?) -> Dodopayments::WorkloadIdentity::provider
|
|
78
|
+
|
|
79
|
+
def self.aws: (
|
|
80
|
+
?top fn
|
|
81
|
+
) ?{
|
|
82
|
+
-> String
|
|
83
|
+
} -> Dodopayments::WorkloadIdentity::provider
|
|
84
|
+
|
|
85
|
+
def self.static: (String token) -> Dodopayments::WorkloadIdentity::provider
|
|
86
|
+
|
|
87
|
+
def self.auto: -> Dodopayments::WorkloadIdentity::provider?
|
|
88
|
+
end
|
|
89
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dodopayments
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.36.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dodo Payments
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cgi
|
|
@@ -542,6 +542,7 @@ files:
|
|
|
542
542
|
- lib/dodopayments/resources/webhooks.rb
|
|
543
543
|
- lib/dodopayments/resources/webhooks/headers.rb
|
|
544
544
|
- lib/dodopayments/version.rb
|
|
545
|
+
- lib/dodopayments/workload_identity.rb
|
|
545
546
|
- manifest.yaml
|
|
546
547
|
- rbi/dodopayments/client.rbi
|
|
547
548
|
- rbi/dodopayments/errors.rbi
|
|
@@ -1021,6 +1022,7 @@ files:
|
|
|
1021
1022
|
- rbi/dodopayments/resources/webhooks.rbi
|
|
1022
1023
|
- rbi/dodopayments/resources/webhooks/headers.rbi
|
|
1023
1024
|
- rbi/dodopayments/version.rbi
|
|
1025
|
+
- rbi/dodopayments/workload_identity.rbi
|
|
1024
1026
|
- sig/dodopayments/client.rbs
|
|
1025
1027
|
- sig/dodopayments/errors.rbs
|
|
1026
1028
|
- sig/dodopayments/file_part.rbs
|
|
@@ -1499,6 +1501,7 @@ files:
|
|
|
1499
1501
|
- sig/dodopayments/resources/webhooks.rbs
|
|
1500
1502
|
- sig/dodopayments/resources/webhooks/headers.rbs
|
|
1501
1503
|
- sig/dodopayments/version.rbs
|
|
1504
|
+
- sig/dodopayments/workload_identity.rbs
|
|
1502
1505
|
homepage: https://gemdocs.org/gems/dodopayments
|
|
1503
1506
|
licenses: []
|
|
1504
1507
|
metadata:
|