Skip to content

Support auth scheme preference config #3268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 21, 2025
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