Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def initialize(token_request_context, allowed_hosts = [], scopes = [])
'Parameter token_request_context must be an instance of one of our grant flow context classes.'
end

@cached_token = nil
@cached_tokens = {}

@host_validator = if allowed_hosts.nil? || allowed_hosts.empty?
MicrosoftKiotaAbstractions::AllowedHostsValidator.new([])
Expand All @@ -38,7 +38,7 @@ def initialize(token_request_context, allowed_hosts = [], scopes = [])
@scopes = if scopes.nil?
[]
else
scopes
scopes.dup
end
end

Expand All @@ -54,34 +54,39 @@ def get_authorization_token(uri, _additional_properties = {})

raise StandardError, 'Only https is supported' if parsed_url.scheme != 'https'

@scopes << "#{parsed_url.scheme}://#{parsed_url.host}/.default" if @scopes.empty?
@token_request_context.initialize_scopes(@scopes)
effective_scopes = @scopes.empty? ? ["#{parsed_url.scheme}://#{parsed_url.host}/.default"] : @scopes.dup
cache_key = effective_scopes.freeze
Fiber.new do
if @cached_token
token = OAuth2::AccessToken.from_hash(@token_request_context.oauth_provider, @cached_token)
token.token unless token.nil? || token.expired?

if token.expired?
@token_request_context.initialize_scopes(effective_scopes)
token_value = nil
cached_token = @cached_tokens[cache_key]
unless cached_token.nil?
token = OAuth2::AccessToken.from_hash(@token_request_context.oauth_provider, cached_token)
if token && !token.expired?
token_value = token.token
elsif token
token = token.refresh!
@cached_token = token.to_hash
token.token
@cached_tokens[cache_key] = token.to_hash
token_value = token.token
end
end

nil
token = @token_request_context.get_token

unless token.nil?
@cached_token = token.to_hash
token.token
if token_value.nil?
token = @token_request_context.get_token
unless token.nil?
@cached_tokens[cache_key] = token.to_hash
token_value = token.token
end
end

token_value
end
end

attr_reader :scopes, :host_validator

protected

attr_writer :host_validator, :token_credential, :scopes, :cached_token
attr_writer :host_validator, :token_credential, :scopes, :cached_tokens
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,54 @@
end
end

context 'with request-derived default scopes' do
let(:allowed_hosts) { %w[one.example.com two.example.com] }
let(:provider) { described_class.new(context, allowed_hosts) }
let(:tokens) do
[
instance_double(OAuth2::AccessToken, token: 'one_token', to_hash: {}),
instance_double(OAuth2::AccessToken, token: 'two_token', to_hash: {})
]
end

it 'derives the default scope for each request without mutating configured scopes' do
allow(context).to receive(:get_token).and_return(*tokens)

expect(provider.get_authorization_token('https://one.example.com').resume).to eq('one_token')
expect(provider.get_authorization_token('https://two.example.com').resume).to eq('two_token')

expect(context).to have_received(:initialize_scopes).with(['https://one.example.com/.default'])
expect(context).to have_received(:initialize_scopes).with(['https://two.example.com/.default'])
expect(context).to have_received(:get_token).twice
expect(provider.scopes).to eq([])
end

it 'reuses a cached token only for its effective scopes' do
one_token = OAuth2::AccessToken.new(context.oauth_provider, 'one_token', expires_in: 3600)
two_token = OAuth2::AccessToken.new(context.oauth_provider, 'two_token', expires_in: 3600)
allow(context).to receive(:get_token).and_return(one_token, two_token)

expect(provider.get_authorization_token('https://one.example.com').resume).to eq('one_token')
expect(provider.get_authorization_token('https://two.example.com').resume).to eq('two_token')
expect(provider.get_authorization_token('https://one.example.com').resume).to eq('one_token')

expect(context).to have_received(:get_token).twice
end

it 'does not mutate explicitly configured scopes' do
configured_scopes = ['https://resource.example.com/.default']
explicit_provider = described_class.new(context, allowed_hosts, configured_scopes)

expect(explicit_provider.scopes).to eq(configured_scopes)
expect(explicit_provider.scopes).not_to equal(configured_scopes)
explicit_provider.get_authorization_token('https://one.example.com').resume

expect(explicit_provider.scopes).to eq(configured_scopes)
expect(configured_scopes).to eq(['https://resource.example.com/.default'])
expect(context).to have_received(:initialize_scopes).with(configured_scopes)
end
end

it 'leaves a request to a host outside the allowed hosts without an authorization header' do
authentication_provider = MicrosoftKiotaAuthenticationOAuth::OAuthAuthenticationProvider.new(
context, allowed_hosts, ['https://api.example.com/.default']
Expand Down
Loading