Skip to content

Commit 7523195

Browse files
add token refresher
1 parent 5fe0624 commit 7523195

File tree

6 files changed

+221
-134
lines changed

6 files changed

+221
-134
lines changed

lib/zendesk_api.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ module ZendeskAPI; end
66
require 'zendesk_api/helpers'
77
require 'zendesk_api/core_ext/inflection'
88
require 'zendesk_api/client'
9+
require 'zendesk_api/token_refresher'

lib/zendesk_api/client.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ def set_authentication(builder, config)
240240
# Upon refreshing the access token, the configuration is updated accordingly.
241241
# Utilizing the proc here ensures that the token used is always valid.
242242
builder.request :authorization, "Bearer", -> { config.access_token }
243-
builder.use ZendeskAPI::Middleware::Response::TokenRefresher, config
243+
# TODO: If you decide to use TokenRefresher as a middleware, uncomment the below line.
244+
# builder.use ZendeskAPI::Middleware::Response::TokenRefresher, config
244245
elsif config.access_token
245246
builder.use ZendeskAPI::Middleware::Request::UrlBasedAccessToken, config.access_token
246247
else

lib/zendesk_api/middleware/response/token_refresher.rb

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,11 @@ def initialize(app, config)
1616
def on_complete(env)
1717
return unless ERROR_CODES.include?(env[:status])
1818

19-
return unless @config.client_id
20-
return unless @config.client_secret
21-
return unless @config.refresh_token
22-
23-
refresh_token
24-
end
25-
26-
def refresh_token
27-
response = connection.post "/oauth/tokens" do |req|
28-
req.body = {
29-
grant_type: "refresh_token",
30-
refresh_token: @config.refresh_token,
31-
client_id: @config.client_id,
32-
client_secret: @config.client_secret
33-
}.tap do |params|
34-
params.merge!(expires_in: @config.access_token_expiration) if @config.access_token_expiration
35-
params.merge!(refresh_token_expires_in: @config.refresh_token_expiration) if @config.refresh_token_expiration
19+
ZendeskAPI::TokenRefresher.new(@config).refresh_token do |access_token, refresh_token|
20+
if @config.refresh_token_callback.is_a?(Proc)
21+
@config.refresh_token_callback.call(access_token, refresh_token)
3622
end
3723
end
38-
@config.access_token = response.body["access_token"]
39-
@config.refresh_token = response.body["refresh_token"]
40-
41-
return unless @config.refresh_token_callback.is_a?(Proc)
42-
43-
@config.refresh_token_callback.call(@config.access_token, @config.refresh_token)
44-
end
45-
46-
def connection
47-
@connection ||= Faraday.new(faraday_options) do |builder|
48-
builder.use ZendeskAPI::Middleware::Response::RaiseError
49-
builder.use ZendeskAPI::Middleware::Response::Logger, @config.logger if @config.logger
50-
builder.use ZendeskAPI::Middleware::Response::ParseJson
51-
builder.use ZendeskAPI::Middleware::Response::SanitizeResponse
52-
builder.use ZendeskAPI::Middleware::Request::EncodeJson
53-
54-
adapter = @config.adapter || Faraday.default_adapter
55-
builder.adapter(*adapter, &@config.adapter_proc)
56-
end
57-
end
58-
59-
def faraday_options
60-
{
61-
url: @config.url
62-
}
6324
end
6425
end
6526
end

lib/zendesk_api/token_refresher.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
module ZendeskAPI
2+
# Obtains new OAuth access and refresh tokens.
3+
class TokenRefresher
4+
def initialize(config)
5+
@config = config
6+
end
7+
8+
def valid_config?
9+
return false unless @config.client_id
10+
return false unless @config.client_secret
11+
return false unless @config.refresh_token
12+
13+
true
14+
end
15+
16+
def refresh_token
17+
return unless valid_config?
18+
19+
response = connection.post "/oauth/tokens" do |req|
20+
req.body = {
21+
grant_type: "refresh_token",
22+
refresh_token: @config.refresh_token,
23+
client_id: @config.client_id,
24+
client_secret: @config.client_secret
25+
}.tap do |params|
26+
params.merge!(expires_in: @config.access_token_expiration) if @config.access_token_expiration
27+
params.merge!(refresh_token_expires_in: @config.refresh_token_expiration) if @config.refresh_token_expiration
28+
end
29+
end
30+
new_access_token = response.body["access_token"]
31+
new_refresh_token = response.body["refresh_token"]
32+
@config.access_token = new_access_token
33+
@config.refresh_token = new_refresh_token
34+
35+
yield new_access_token, new_refresh_token if block_given?
36+
end
37+
38+
private
39+
40+
def connection
41+
@connection ||= Faraday.new(faraday_options) do |builder|
42+
builder.use ZendeskAPI::Middleware::Response::RaiseError
43+
builder.use ZendeskAPI::Middleware::Response::Logger, @config.logger if @config.logger
44+
builder.use ZendeskAPI::Middleware::Response::ParseJson
45+
builder.use ZendeskAPI::Middleware::Response::SanitizeResponse
46+
builder.use ZendeskAPI::Middleware::Request::EncodeJson
47+
48+
# TODO: Should it also use Request::Retry and Request::RaiseRateLimited ZendeskAPI::Middleware
49+
# if those options are enabled in client config?
50+
51+
adapter = @config.adapter || Faraday.default_adapter
52+
builder.adapter(*adapter, &@config.adapter_proc)
53+
end
54+
end
55+
56+
def faraday_options
57+
{
58+
url: @config.url
59+
}
60+
end
61+
end
62+
end

spec/core/middleware/response/token_refresher_spec.rb

Lines changed: 10 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
end
1414

1515
before do
16+
skip "TODO: If you decide to use TokenRefresher as a middleware, remove this skip."
17+
1618
client.config.client_id = "client"
1719
client.config.client_secret = "secret"
1820
client.config.refresh_token = "abc"
@@ -30,23 +32,19 @@
3032
body: "",
3133
headers: { content_type: "application/json" }
3234
)
35+
stub_request(:post, %r{/oauth/tokens}).to_return(
36+
status: refresh_token_status,
37+
body: refresh_token_body.to_json,
38+
headers: { content_type: "application/json" }
39+
)
3340
end
3441

3542
describe "when refreshing token succeeds" do
3643
let(:refresh_token_status) { 200 }
3744

38-
before do
39-
stub_request(:post, %r{/oauth/tokens}).to_return(
40-
status: refresh_token_status,
41-
body: refresh_token_body.to_json,
42-
headers: { content_type: "application/json" }
43-
)
44-
end
45-
4645
it "refreshes token" do
4746
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
4847

49-
assert_requested :post, %r{oauth/tokens}
5048
expect(client.config.access_token).to eq "nt"
5149
expect(client.config.refresh_token).to eq "nrt"
5250
end
@@ -64,85 +62,17 @@
6462
expect(new_refresh_token).to eq "nrt"
6563
end
6664

67-
it "does not include expiration params when not configured" do
68-
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
69-
70-
assert_requested(:post, %r{oauth/tokens}) do |req|
71-
expect(req.body).to_not match(/expires_in/)
72-
expect(req.body).to_not match(/refresh_token_expires_in/)
73-
end
74-
end
75-
76-
it "includes expiration params when configured" do
77-
client.config.access_token_expiration = 300
78-
client.config.refresh_token_expiration = 604800
79-
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
80-
81-
assert_requested(:post, %r{oauth/tokens}) do |req|
82-
expect(req.body).to match(/expires_in/)
83-
expect(req.body).to match(/refresh_token_expires_in/)
84-
end
85-
end
86-
8765
it "raises unauthorized exception" do
8866
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
8967
end
9068
end
9169

92-
describe "with client id not configuration" do
93-
before do
94-
client.config.client_id = nil
95-
end
96-
97-
it "does not try to refresh token" do
98-
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
99-
100-
refute_requested :post, %r{/oauth/tokens}
101-
expect(client.config.access_token).to eq "xyz"
102-
end
103-
end
104-
105-
describe "with client secret not configuration" do
106-
before do
107-
client.config.client_secret = nil
108-
end
109-
110-
it "does not try to refresh token" do
111-
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
112-
113-
refute_requested :post, %r{/oauth/tokens}
114-
expect(client.config.access_token).to eq "xyz"
115-
end
116-
end
117-
118-
describe "with client secret not configuration" do
119-
before do
120-
client.config.refresh_token = nil
121-
end
122-
123-
it "does not try to refresh token" do
124-
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
125-
126-
refute_requested :post, %r{oauth/tokens}
127-
expect(client.config.access_token).to eq "xyz"
128-
end
129-
end
130-
13170
describe "when refreshing token fails" do
13271
let(:refresh_token_status) { 500 }
13372

134-
before do
135-
stub_request(:post, %r{/oauth/tokens}).to_return(
136-
status: refresh_token_status,
137-
body: refresh_token_body.to_json,
138-
headers: { content_type: "application/json" }
139-
)
140-
end
141-
142-
it "does not change token configuration" do
73+
it "does not update configuration" do
14374
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::NetworkError)
14475

145-
assert_requested :post, %r{oauth/tokens}
14676
expect(client.config.access_token).to eq "xyz"
14777
expect(client.config.refresh_token).to eq "abc"
14878
end
@@ -158,15 +88,10 @@
15888
)
15989
end
16090

161-
it "succeeds" do
162-
result = client.connection.get "/whatever"
163-
expect(result.status).to eq 200
164-
end
165-
16691
it "does not refresh token" do
16792
client.connection.get "/whatever"
16893

169-
refute_requested :post, %r{oauth/tokens}
94+
expect_any_instance_of(ZendeskAPI::TokenRefresher).to receive(:refresh_token).never
17095
expect(client.config.access_token).to eq "xyz"
17196
end
17297
end
@@ -182,18 +107,12 @@
182107
body: "",
183108
headers: { content_type: "application/json" }
184109
)
185-
186-
stub_request(:post, %r{/oauth/tokens}).to_return(
187-
status: 200,
188-
body: refresh_token_body.to_json,
189-
headers: { content_type: "application/json" }
190-
)
191110
end
192111

193112
it "refreshes token" do
194113
expect { client.connection.get "/whatever" }.to raise_error(ZendeskAPI::Error::Unauthorized)
195114

196-
refute_requested :post, %r{oauth/tokens}
115+
expect_any_instance_of(ZendeskAPI::TokenRefresher).to receive(:refresh_token).never
197116
end
198117
end
199118
end

0 commit comments

Comments
 (0)