diff --git a/custom_templates/api_client.mustache b/custom_templates/api_client.mustache index 2dbafa04..c5391b50 100644 --- a/custom_templates/api_client.mustache +++ b/custom_templates/api_client.mustache @@ -166,8 +166,10 @@ module {{moduleName}} # @param [Hash] query_params Query parameters # @param [String] auth_names Authentication scheme name def update_params_for_auth!(header_params, query_params, auth_names) + auth_settings = nil Array(auth_names).each do |auth_name| - auth_setting = @config.auth_settings[auth_name] + auth_settings ||= @config.auth_settings + auth_setting = auth_settings[auth_name] next unless auth_setting case auth_setting[:in] when 'header' then header_params[auth_setting[:key]] = auth_setting[:value] unless auth_setting[:value].nil? diff --git a/custom_templates/configuration.mustache b/custom_templates/configuration.mustache index 1897e3b4..b21388df 100644 --- a/custom_templates/configuration.mustache +++ b/custom_templates/configuration.mustache @@ -274,7 +274,8 @@ module {{moduleName}} {{#isOAuth}} # Gets Bearer auth token string def oauth_bearer_token - "Bearer #{access_token_with_refresh}" unless access_token_with_refresh.nil? + token = access_token_with_refresh + "Bearer #{token}" unless token.nil? end {{/isOAuth}} diff --git a/lib/bandwidth-sdk/api_client.rb b/lib/bandwidth-sdk/api_client.rb index 789e4942..b180d4ac 100644 --- a/lib/bandwidth-sdk/api_client.rb +++ b/lib/bandwidth-sdk/api_client.rb @@ -353,8 +353,10 @@ def build_request_url(path, opts = {}) # @param [Hash] query_params Query parameters # @param [String] auth_names Authentication scheme name def update_params_for_auth!(header_params, query_params, auth_names) + auth_settings = nil Array(auth_names).each do |auth_name| - auth_setting = @config.auth_settings[auth_name] + auth_settings ||= @config.auth_settings + auth_setting = auth_settings[auth_name] next unless auth_setting case auth_setting[:in] when 'header' then header_params[auth_setting[:key]] = auth_setting[:value] unless auth_setting[:value].nil? diff --git a/lib/bandwidth-sdk/configuration.rb b/lib/bandwidth-sdk/configuration.rb index e263f7fa..303f043d 100644 --- a/lib/bandwidth-sdk/configuration.rb +++ b/lib/bandwidth-sdk/configuration.rb @@ -288,7 +288,8 @@ def basic_auth_token # Gets Bearer auth token string def oauth_bearer_token - "Bearer #{access_token_with_refresh}" unless access_token_with_refresh.nil? + token = access_token_with_refresh + "Bearer #{token}" unless token.nil? end diff --git a/spec/unit/client/api_client_spec.rb b/spec/unit/client/api_client_spec.rb index ca8fbf2e..8856f068 100644 --- a/spec/unit/client/api_client_spec.rb +++ b/spec/unit/client/api_client_spec.rb @@ -292,9 +292,78 @@ end end - describe '#update_params_for_auth' do - it 'updates header and query params based on authentication settings' do - + describe 'request authentication' do + let(:messages_api) { Bandwidth::MessagesApi.new(api_client) } + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + let(:token_requests) { [] } + let(:authorization_headers) { [] } + let(:expires_in) { 3600 } + let(:now) { Time.utc(2026, 1, 1) } + + before do + allow(Time).to receive(:now).and_return(now) + allow(Faraday).to receive(:new).and_wrap_original do |original, *args, &block| + original.call(*args, &block).tap { |connection| connection.adapter :test, stubs } + end + config.client_id = 'client_id' + config.client_secret = 'client_secret' + stubs.post('https://api.bandwidth.com/api/v1/oauth2/token') do |request| + token_requests << request + [200, { 'Content-Type' => 'application/json' }, + { access_token: "token_#{token_requests.length}", expires_in: expires_in }.to_json] + end + stubs.get('https://messaging.bandwidth.com/api/v2/users/account/messages') do |request| + authorization_headers << request.request_headers['Authorization'] + [200, { 'Content-Type' => 'application/json' }, '{"data":[]}'] + end + end + + [1, 30, 60].each do |lifetime| + context "with a #{lifetime}-second token" do + let(:expires_in) { lifetime } + + it 'acquires once per request and checks expiration again on the next request' do + messages_api.list_messages('account') + expect(token_requests.length).to eq(1) + expect(authorization_headers).to eq(['Bearer token_1']) + + messages_api.list_messages('account') + expect(token_requests.length).to eq(2) + expect(authorization_headers).to eq(['Bearer token_1', 'Bearer token_2']) + end + end + end + + it 'reuses a token until it reaches the 60-second expiration buffer' do + messages_api.list_messages('account') + allow(Time).to receive(:now).and_return(now + 3539) + messages_api.list_messages('account') + expect(token_requests.length).to eq(1) + expect(authorization_headers).to eq(['Bearer token_1', 'Bearer token_1']) + + allow(Time).to receive(:now).and_return(now + 3540) + messages_api.list_messages('account') + expect(token_requests.length).to eq(2) + expect(authorization_headers.last).to eq('Bearer token_2') + end + + it 'uses Basic authentication when OAuth credentials are absent' do + config.client_id = nil + config.client_secret = nil + config.username = 'username' + config.password = 'password' + messages_api.list_messages('account') + expect(token_requests).to be_empty + expect(authorization_headers).to eq(['Basic dXNlcm5hbWU6cGFzc3dvcmQ=']) + end + + [nil, []].each do |auth_names| + it "does not acquire a token when auth_names is #{auth_names.inspect}" do + api_client.call_api(:GET, '/users/account/messages', + operation: :'MessagesApi.list_messages', auth_names: auth_names, header_params: {}) + expect(token_requests).to be_empty + expect(authorization_headers).to eq([nil]) + end end end diff --git a/spec/unit/client/configuration_spec.rb b/spec/unit/client/configuration_spec.rb index 0db15b46..ff62e78e 100644 --- a/spec/unit/client/configuration_spec.rb +++ b/spec/unit/client/configuration_spec.rb @@ -121,8 +121,10 @@ end it 'returns the Bearer string using access_token_getter when defined' do - config.access_token_getter = proc { token } - expect(config.oauth_bearer_token).to eq("Bearer #{token}") + tokens = ['first_token', 'second_token'] + config.access_token_getter = proc { tokens.shift } + expect(config.oauth_bearer_token).to eq('Bearer first_token') + expect(config.oauth_bearer_token).to eq('Bearer second_token') end it 'returns nil when no access_token is available' do