Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Feature - Support an auth scheme signing preference list using `ENV['AWS_AUTH_SCHEME_PREFERENCE']` or `auth_scheme_preference` in shared configuration.

3.226.2 (2025-07-01)
------------------

Expand Down
50 changes: 37 additions & 13 deletions gems/aws-sdk-core/lib/aws-sdk-core/endpoints.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,28 @@
module Aws
# @api private
module Endpoints
SUPPORTED_AUTH_TRAITS = %w[
aws.auth#sigv4
aws.auth#sigv4a
smithy.api#httpBearerAuth
smithy.api#noAuth
].freeze
# Maps config auth scheme preferences to endpoint auth scheme names.
ENDPOINT_AUTH_PREFERENCE_MAP = {
'sigv4' => %w[sigv4 sigv4-s3express],
'sigv4a' => ['sigv4a'],
'httpBearerAuth' => ['bearer'],
'noAuth' => ['none']
}.freeze
SUPPORTED_ENDPOINT_AUTH = ENDPOINT_AUTH_PREFERENCE_MAP.values.flatten.freeze

# Maps configured auth scheme preferences to modeled auth traits.
MODELED_AUTH_PREFERENCE_MAP = {
'sigv4' => 'aws.auth#sigv4',
'sigv4a' => 'aws.auth#sigv4a',
'httpBearerAuth' => 'smithy.api#httpBearerAuth',
'noAuth' => 'smithy.api#noAuth'
}.freeze
SUPPORTED_MODELED_AUTH = MODELED_AUTH_PREFERENCE_MAP.values.freeze

class << self
def resolve_auth_scheme(context, endpoint)
if endpoint && (auth_schemes = endpoint.properties['authSchemes'])
auth_scheme = auth_schemes.find do |scheme|
Aws::Plugins::Sign::SUPPORTED_AUTH_TYPES.include?(scheme['name'])
end
auth_scheme = endpoint_auth_scheme_preference(auth_schemes, context.config.auth_scheme_preference)
raise 'No supported auth scheme for this endpoint.' unless auth_scheme

merge_signing_defaults(auth_scheme, context.config)
Expand All @@ -42,6 +51,16 @@ def resolve_auth_scheme(context, endpoint)

private

def endpoint_auth_scheme_preference(auth_schemes, preferred_auth)
ordered_auth = preferred_auth.each_with_object([]) do |pref, list|
next unless ENDPOINT_AUTH_PREFERENCE_MAP.key?(pref)

ENDPOINT_AUTH_PREFERENCE_MAP[pref].each { |name| list << { 'name' => name } }
end
ordered_auth += auth_schemes
ordered_auth.find { |auth| SUPPORTED_ENDPOINT_AUTH.include?(auth['name']) }
end

def merge_signing_defaults(auth_scheme, config)
if %w[sigv4 sigv4a sigv4-s3express].include?(auth_scheme['name'])
auth_scheme['signingName'] ||= sigv4_name(config)
Expand All @@ -64,13 +83,12 @@ def merge_signing_defaults(auth_scheme, config)
end

def sigv4_name(config)
config.api.metadata['signingName'] ||
config.api.metadata['endpointPrefix']
config.api.metadata['signingName'] || config.api.metadata['endpointPrefix']
end

def default_auth_scheme(context)
if (auth_list = default_api_auth(context))
auth = auth_list.find { |a| SUPPORTED_AUTH_TRAITS.include?(a) }
if (modeled_auth = default_api_auth(context))
auth = modeled_auth_scheme_preference(modeled_auth, context.config.auth_scheme_preference)
case auth
when 'aws.auth#sigv4', 'aws.auth#sigv4a'
auth_scheme = { 'name' => auth.split('#').last }
Expand All @@ -93,6 +111,12 @@ def default_auth_scheme(context)
end
end

def modeled_auth_scheme_preference(modeled_auth, preferred_auth)
ordered_auth = preferred_auth.map { |pref| MODELED_AUTH_PREFERENCE_MAP[pref] }.compact
ordered_auth += modeled_auth
ordered_auth.find { |auth| SUPPORTED_MODELED_AUTH.include?(auth) }
end

def default_api_auth(context)
context.config.api.operation(context.operation_name)['auth'] ||
context.config.api.metadata['auth']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,61 +17,61 @@ class CredentialsConfiguration < Seahorse::Client::Plugin
option(:profile,
doc_default: 'default',
doc_type: String,
docstring: <<-DOCS)
Used when loading credentials from the shared credentials file
at HOME/.aws/credentials. When not specified, 'default' is used.
docstring: <<~DOCS)
Used when loading credentials from the shared credentials file at HOME/.aws/credentials.
When not specified, 'default' is used.
DOCS

option(:credentials,
required: true,
doc_type: 'Aws::CredentialProvider',
rbs_type: 'untyped',
docstring: <<-DOCS
Your AWS credentials. This can be an instance of any one of the
following classes:
docstring: <<~DOCS
Your AWS credentials used for authentication. This can be an instance of any one of the
following classes:

* `Aws::Credentials` - Used for configuring static, non-refreshing
credentials.
* `Aws::Credentials` - Used for configuring static, non-refreshing
credentials.

* `Aws::SharedCredentials` - Used for loading static credentials from a
shared file, such as `~/.aws/config`.
* `Aws::SharedCredentials` - Used for loading static credentials from a
shared file, such as `~/.aws/config`.

* `Aws::AssumeRoleCredentials` - Used when you need to assume a role.
* `Aws::AssumeRoleCredentials` - Used when you need to assume a role.

* `Aws::AssumeRoleWebIdentityCredentials` - Used when you need to
assume a role after providing credentials via the web.
* `Aws::AssumeRoleWebIdentityCredentials` - Used when you need to
assume a role after providing credentials via the web.

* `Aws::SSOCredentials` - Used for loading credentials from AWS SSO using an
access token generated from `aws login`.
* `Aws::SSOCredentials` - Used for loading credentials from AWS SSO using an
access token generated from `aws login`.

* `Aws::ProcessCredentials` - Used for loading credentials from a
process that outputs to stdout.
* `Aws::ProcessCredentials` - Used for loading credentials from a
process that outputs to stdout.

* `Aws::InstanceProfileCredentials` - Used for loading credentials
from an EC2 IMDS on an EC2 instance.
* `Aws::InstanceProfileCredentials` - Used for loading credentials
from an EC2 IMDS on an EC2 instance.

* `Aws::ECSCredentials` - Used for loading credentials from
instances running in ECS.
* `Aws::ECSCredentials` - Used for loading credentials from
instances running in ECS.

* `Aws::CognitoIdentityCredentials` - Used for loading credentials
from the Cognito Identity service.
* `Aws::CognitoIdentityCredentials` - Used for loading credentials
from the Cognito Identity service.

When `:credentials` are not configured directly, the following
locations will be searched for credentials:
When `:credentials` are not configured directly, the following
locations will be searched for credentials:

* `Aws.config[:credentials]`
* The `:access_key_id`, `:secret_access_key`, `:session_token`, and
`:account_id` options.
* ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'],
ENV['AWS_SESSION_TOKEN'], and ENV['AWS_ACCOUNT_ID']
* `~/.aws/credentials`
* `~/.aws/config`
* EC2/ECS IMDS instance profile - When used by default, the timeouts
are very aggressive. Construct and pass an instance of
`Aws::InstanceProfileCredentials` or `Aws::ECSCredentials` to
enable retries and extended timeouts. Instance profile credential
fetching can be disabled by setting ENV['AWS_EC2_METADATA_DISABLED']
to true.
* `Aws.config[:credentials]`
* The `:access_key_id`, `:secret_access_key`, `:session_token`, and
`:account_id` options.
* ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'],
ENV['AWS_SESSION_TOKEN'], and ENV['AWS_ACCOUNT_ID']
* `~/.aws/credentials`
* `~/.aws/config`
* EC2/ECS IMDS instance profile - When used by default, the timeouts
are very aggressive. Construct and pass an instance of
`Aws::InstanceProfileCredentials` or `Aws::ECSCredentials` to
enable retries and extended timeouts. Instance profile credential
fetching can be disabled by setting ENV['AWS_EC2_METADATA_DISABLED']
to true.
DOCS
) do |config|
CredentialProviderChain.new(config).resolve
Expand All @@ -82,22 +82,21 @@ class CredentialsConfiguration < Seahorse::Client::Plugin
option(:instance_profile_credentials_timeout, 1)

option(:token_provider,
required: false,
doc_type: 'Aws::TokenProvider',
rbs_type: 'untyped',
docstring: <<-DOCS
A Bearer Token Provider. This can be an instance of any one of the
following classes:

* `Aws::StaticTokenProvider` - Used for configuring static, non-refreshing
tokens.

* `Aws::SSOTokenProvider` - Used for loading tokens from AWS SSO using an
access token generated from `aws login`.

When `:token_provider` is not configured directly, the `Aws::TokenProviderChain`
will be used to search for tokens configured for your profile in shared configuration files.
DOCS
doc_type: 'Aws::TokenProvider',
rbs_type: 'untyped',
docstring: <<~DOCS
Your Bearer token used for authentication. This can be an instance of any one of the
following classes:

* `Aws::StaticTokenProvider` - Used for configuring static, non-refreshing
tokens.

* `Aws::SSOTokenProvider` - Used for loading tokens from AWS SSO using an
access token generated from `aws login`.

When `:token_provider` is not configured directly, the `Aws::TokenProviderChain`
will be used to search for tokens configured for your profile in shared configuration files.
DOCS
) do |config|
if config.stub_responses
StaticTokenProvider.new('token')
Expand All @@ -106,6 +105,21 @@ class CredentialsConfiguration < Seahorse::Client::Plugin
end
end

option(:auth_scheme_preference,
doc_type: 'Array<String>',
rbs_type: 'Array[String]',
docstring: <<~DOCS
A list of preferred authentication schemes to use when making a request. Supported values are:
`sigv4`, `sigv4a`, `httpBearerAuth`, and `noAuth`. When set using `ENV['AWS_AUTH_SCHEME_PREFERENCE']` or in
shared config as `auth_scheme_preference`, the value should be a comma-separated list.
DOCS
) do |config|
value =
ENV['AWS_AUTH_SCHEME_PREFERENCE'] ||
Aws.shared_config.auth_scheme_preference(profile: config.profile) ||
''
value.gsub(' ', '').gsub("\t", '').split(',')
end
end
end
end
3 changes: 0 additions & 3 deletions gems/aws-sdk-core/lib/aws-sdk-core/plugins/sign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ class Sign < Seahorse::Client::Plugin
option(:sigv4_region)
option(:unsigned_operations, default: [])

supported_auth_types = %w[sigv4 bearer sigv4-s3express sigv4a none]
SUPPORTED_AUTH_TYPES = supported_auth_types.freeze

def add_handlers(handlers, cfg)
operations = cfg.api.operation_names - cfg.unsigned_operations
handlers.add(Handler, step: :sign, operations: operations)
Expand Down
1 change: 1 addition & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/shared_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ def self.config_reader(*attrs)
config_reader(
:region,
:account_id_endpoint_mode,
:auth_scheme_preference,
:sigv4a_signing_region_set,
:ca_bundle,
:credential_process,
Expand Down
27 changes: 27 additions & 0 deletions gems/aws-sdk-core/spec/auth_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module AuthHelper
# Expect the signer to be called with the given auth scheme.
def expect_auth(expected_auth_scheme, region: nil, credentials: nil)
expect(Aws::Plugins::Sign).to receive(:signer_for).and_wrap_original do |m, *args|
actual_auth_scheme = args[0]
_config = args[1]
_sigv4_region_override = args[2]
_sigv4_credentials_override = args[3]

expect(actual_auth_scheme).to include(expected_auth_scheme)
signer = m.call(*args)
case signer
when Aws::Plugins::Sign::SignatureV4
sigv4_signer = signer.signer
case expected_auth_scheme['name']
when 'sigv4'
region = region || expected_auth_scheme['signingRegion']
when 'sigv4a'
region = region || expected_auth_scheme['signingRegionSet']&.join(',')
end
expect(sigv4_signer.region).to eq(region) if region
expect(sigv4_signer.credentials_provider).to eq(credentials) if credentials
end
signer
end
end
end
Loading
Loading