booqable 1.1.0 → 1.2.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 +7 -1
- data/README.md +24 -0
- data/lib/booqable/auth.rb +2 -1
- data/lib/booqable/configurable.rb +2 -0
- data/lib/booqable/default.rb +11 -0
- data/lib/booqable/middleware/auth/oauth.rb +20 -3
- data/lib/booqable/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ce7d8b0be4851e4e78229078331a2f73cbe7cb7702c1555df9dec0903be28d0e
|
|
4
|
+
data.tar.gz: c79ed318eb3a38534291ef5e4abeb6a5749c7a2684bcd1befbf43d6f7f1d0e8a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d6dead61b0b4bfd8374c9b51aec7dccc724988b5417f9585018ee549840ee207756642b38aedbfcf4fdb4e313f9764fdc64b1d1d6e7593c057edb20d6095afc
|
|
7
|
+
data.tar.gz: c5e34b3a736ab3bdca397dfd1245d07b8dd8e246e32d4d0f24e097c41f52d47307c645a547592e60b90dc27f98e2e817193a1059f2696c0a726316c0693c1f88
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [1.2.0] - 2026-05-27
|
|
2
|
+
|
|
3
|
+
- Add optional `around_refresh_token` configuration. When provided, the OAuth
|
|
4
|
+
middleware yields the read + expiry-check + refresh sequence to the callable
|
|
5
|
+
so host applications can serialize concurrent token refreshes (e.g. with a
|
|
6
|
+
database transaction and advisory lock). The gem keeps no lock dependency.
|
|
7
|
+
|
|
2
8
|
|
|
3
9
|
## [1.1.0] - 2026-03-11
|
|
4
10
|
|
data/README.md
CHANGED
|
@@ -105,6 +105,30 @@ client = Booqable::Client.new(
|
|
|
105
105
|
client.authenticate_with_code(params[:code])
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
#### Serializing concurrent token refreshes
|
|
109
|
+
|
|
110
|
+
When multiple processes share the same OAuth token (e.g. the same installation
|
|
111
|
+
serving concurrent requests), pass an `around_refresh_token` callable to
|
|
112
|
+
serialize the read + expiry-check + refresh sequence. The middleware yields
|
|
113
|
+
to the callable once per request; the host application decides how to lock.
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
Booqable::Client.new(
|
|
117
|
+
# ...other oauth options...
|
|
118
|
+
around_refresh_token: ->(&block) {
|
|
119
|
+
AppInstallation.transaction do
|
|
120
|
+
installation.with_advisory_lock!("app_installation:#{installation.id}", transaction: true) do
|
|
121
|
+
installation.reload
|
|
122
|
+
block.call
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The gem itself has no advisory-lock dependency — `around_refresh_token` is
|
|
130
|
+
just a callable that takes a block.
|
|
131
|
+
|
|
108
132
|
### Single-Use Token Authentication
|
|
109
133
|
|
|
110
134
|
For server-to-server communication requiring enhanced security:
|
data/lib/booqable/auth.rb
CHANGED
|
@@ -57,7 +57,8 @@ module Booqable
|
|
|
57
57
|
api_endpoint: api_endpoint,
|
|
58
58
|
redirect_uri: redirect_uri,
|
|
59
59
|
read_token: read_token,
|
|
60
|
-
write_token: write_token
|
|
60
|
+
write_token: write_token,
|
|
61
|
+
around_refresh_token: around_refresh_token
|
|
61
62
|
} if oauth_authenticated?
|
|
62
63
|
|
|
63
64
|
builder.use Booqable::Middleware::Auth::ApiKey, {
|
|
@@ -48,6 +48,7 @@ module Booqable
|
|
|
48
48
|
:proxy,
|
|
49
49
|
:read_token,
|
|
50
50
|
:redirect_uri,
|
|
51
|
+
:around_refresh_token,
|
|
51
52
|
:single_use_token,
|
|
52
53
|
:single_use_token_algorithm,
|
|
53
54
|
:single_use_token_company_id,
|
|
@@ -81,6 +82,7 @@ module Booqable
|
|
|
81
82
|
proxy
|
|
82
83
|
read_token
|
|
83
84
|
redirect_uri
|
|
85
|
+
around_refresh_token
|
|
84
86
|
single_use_token
|
|
85
87
|
single_use_token_algorithm
|
|
86
88
|
single_use_token_company_id
|
data/lib/booqable/default.rb
CHANGED
|
@@ -151,6 +151,17 @@ module Booqable
|
|
|
151
151
|
Proc.new { }
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
+
# Default `around_refresh_token` callable
|
|
155
|
+
#
|
|
156
|
+
# When non-nil, the OAuth middleware yields its read+check+refresh
|
|
157
|
+
# sequence to this callable so the host application can serialize
|
|
158
|
+
# concurrent refreshes (e.g. with an advisory lock).
|
|
159
|
+
#
|
|
160
|
+
# @return [Proc, nil]
|
|
161
|
+
def around_refresh_token
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
|
|
154
165
|
# Default API key from ENV
|
|
155
166
|
# @return [String, nil] API key for authentication
|
|
156
167
|
def api_key
|
|
@@ -24,6 +24,10 @@ module Booqable
|
|
|
24
24
|
# @option options [String] :api_endpoint API endpoint URL for the OAuth provider
|
|
25
25
|
# @option options [Proc] :read_token Proc to read stored token
|
|
26
26
|
# @option options [Proc] :write_token Proc to store new token
|
|
27
|
+
# @option options [Proc, nil] :around_refresh_token Optional callable
|
|
28
|
+
# invoked with a block around the read+check+refresh sequence. The
|
|
29
|
+
# host application can use it to serialize concurrent refreshes
|
|
30
|
+
# (e.g. wrap the block in a database transaction + advisory lock).
|
|
27
31
|
# @raise [KeyError] If required options are not provided
|
|
28
32
|
def initialize(app, options = {})
|
|
29
33
|
super(app)
|
|
@@ -33,6 +37,7 @@ module Booqable
|
|
|
33
37
|
@api_endpoint = options.fetch(:api_endpoint)
|
|
34
38
|
@read_token = options.fetch(:read_token)
|
|
35
39
|
@write_token = options.fetch(:write_token)
|
|
40
|
+
@around_refresh_token = options[:around_refresh_token]
|
|
36
41
|
|
|
37
42
|
@client = OAuthClient.new(
|
|
38
43
|
client_id: @client_id,
|
|
@@ -50,10 +55,12 @@ module Booqable
|
|
|
50
55
|
# @param env [Faraday::Env] The request environment
|
|
51
56
|
# @return [Faraday::Response] The response from the next middleware
|
|
52
57
|
def call(env)
|
|
53
|
-
|
|
58
|
+
around_refresh_token do
|
|
59
|
+
@token = @client.get_access_token_from_hash(@read_token.call)
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
if @token.expired? || @token.expires_at.nil?
|
|
62
|
+
@token = refresh_token!
|
|
63
|
+
end
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
env.request_headers["Authorization"] ||= "Bearer #{@token.token}"
|
|
@@ -63,6 +70,16 @@ module Booqable
|
|
|
63
70
|
|
|
64
71
|
private
|
|
65
72
|
|
|
73
|
+
# Yield to the configured around-callback, if any
|
|
74
|
+
#
|
|
75
|
+
# When a host application provides one (e.g. an advisory lock), the
|
|
76
|
+
# read+check+refresh sequence runs inside it so concurrent callers
|
|
77
|
+
# cannot interleave a read with another caller's refresh.
|
|
78
|
+
def around_refresh_token(&block)
|
|
79
|
+
return yield unless @around_refresh_token
|
|
80
|
+
@around_refresh_token.call(&block)
|
|
81
|
+
end
|
|
82
|
+
|
|
66
83
|
# Refresh the expired OAuth token
|
|
67
84
|
#
|
|
68
85
|
# Uses the refresh token to obtain a new access token and stores it
|
data/lib/booqable/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: booqable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hrvoje Šimić
|
|
@@ -166,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
166
166
|
- !ruby/object:Gem::Version
|
|
167
167
|
version: '0'
|
|
168
168
|
requirements: []
|
|
169
|
-
rubygems_version:
|
|
169
|
+
rubygems_version: 4.0.10
|
|
170
170
|
specification_version: 4
|
|
171
171
|
summary: Official Booqable API client for Ruby.
|
|
172
172
|
test_files: []
|