Skip to content

Commit 6649098

Browse files
authored
Support selectable auth schemes (#4203)
~**semver-hazards check failure will be resolved when #4211 is released from `aws-sdk-rust`**~ ## Description Add support for auth scheme preference, which allows customers to re-prioritize the order of auth schemes originally determined by the auth scheme resolver. See [the changelog](https://github.com/smithy-lang/smithy-rs/blob/2d483a0880c30683858329fc8411e9f9282de633/.changelog/1752160009.md) where the auth scheme preference can be configured. Note: The preference list is merely a hint rather than a strict override. Any auth schemes not included in the originally resolved auth schemes will be ignored, and won't be an error. ~To support the implementation, a utility method has been added to `NowOrLater` to map an inner future.~ ## Testing Those called out in the feature spec: - [auth_scheme_preference_should_take_the_highest_priority](https://github.com/smithy-lang/smithy-rs/pull/4203/files#diff-b45485e1d1dd937e149c9c703156aa1a7f71b2bbbd5dddb29ffba385b65c50b0R12) for `Alternative Auth Resolution` - [test_resolve_identity](https://github.com/smithy-lang/smithy-rs/pull/4203/files#diff-db7260e3be202c63463123101814c7bf23ef47796bf23a14d67f567285039d46R891) to verify `Resolving Auth and Credentials` - [auth scheme preference](https://github.com/smithy-lang/smithy-rs/pull/4203/files#diff-e63831c17293fd55b608a406f472155f8809239f3c3afba20bcd77bdc8da2ea3R140) to verify `Manual auth schemes configuration` - [test for parsing auth scheme preference from environmental configuration](https://github.com/smithy-lang/smithy-rs/pull/4203/files#diff-d9b0e8d5e74b3e0542908745494b34c1784153ed4e566fe1ce581f8605e801eaR99) ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [x] For changes to the smithy-rs codegen or runtime crates, I have created a changelog entry Markdown file in the `.changelog` directory, specifying "client," "server," or both in the `applies_to` key. - [x] For changes to the AWS SDK, generated SDK code, or SDK runtime crates, I have created a changelog entry Markdown file in the `.changelog` directory, specifying "aws-sdk-rust" in the `applies_to` key. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
1 parent 55c9267 commit 6649098

File tree

21 files changed

+757
-38
lines changed

21 files changed

+757
-38
lines changed

.changelog/1752160009.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
applies_to:
3+
- aws-sdk-rust
4+
- client
5+
authors:
6+
- ysaito1001
7+
references:
8+
- smithy-rs#4203
9+
breaking: false
10+
new_feature: true
11+
bug_fix: false
12+
---
13+
Add support for configuring auth schemes manually using an auth scheme preference list.
14+
The preference list allows customers to reprioritize the order of auth schemes originally
15+
determined by the auth scheme resolver.
16+
Customers can configure the auth scheme preference at the following locations, listed in order of precedence:
17+
1. Service Client Configuration
18+
```rust
19+
use aws_runtime::auth::sigv4;
20+
use aws_smithy_runtime_api::client::auth::AuthSchemeId;
21+
use aws_smithy_runtime_api::client::auth::http::HTTP_BEARER_AUTH_SCHEME_ID;
22+
23+
let config = aws_sdk_s3::Config::builder()
24+
.auth_scheme_preference([AuthSchemeId::from("scheme1"), sigv4::SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID])
25+
// ...
26+
.build();
27+
```
28+
2. Environment Variable
29+
```
30+
AWS_AUTH_SCHEME_PREFERENCE=scheme1, sigv4, httpBearerAuth
31+
```
32+
3. Configuration File
33+
```
34+
auth_scheme_preference=scheme1, sigv4, httpBearerAuth
35+
```
36+
With this configuration, the auth scheme resolver will prefer to select them in the specified order,
37+
if they are supported.

aws/rust-runtime/aws-config/Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aws/rust-runtime/aws-config/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-config"
3-
version = "1.8.2"
3+
version = "1.8.3"
44
authors = [
55
"AWS Rust SDK Team <aws-sdk-rust@amazon.com>",
66
"Russell Cohen <rcoh@amazon.com>",

aws/rust-runtime/aws-config/external-types.toml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
# require manual version bumping every time an automated version bump
44
# to the exposed SDK crates happens.
55
allowed_external_types = [
6-
"aws_credential_types::cache::CredentialsCache",
76
"aws_credential_types::provider::credentials::ProvideCredentials",
87
"aws_credential_types::provider::credentials::Result",
9-
"aws_credential_types::provider::credentials::SharedCredentialsProvider",
108
"aws_credential_types::provider::token::ProvideToken",
119
"aws_runtime::env_config::error::EnvConfigFileLoadError",
1210
"aws_runtime::env_config::file::Builder",
@@ -17,30 +15,23 @@ allowed_external_types = [
1715
"aws_runtime::env_config::section::EnvConfigSections",
1816
"aws_runtime::env_config::section::Profile",
1917
"aws_smithy_async::rt::sleep::AsyncSleep",
20-
"aws_smithy_async::rt::sleep::SharedAsyncSleep",
21-
"aws_smithy_async::time::SharedTimeSource",
2218
"aws_smithy_async::time::TimeSource",
2319
"aws_smithy_runtime::client::identity::cache::IdentityCache",
2420
"aws_smithy_runtime::client::identity::cache::lazy::LazyCacheBuilder",
21+
"aws_smithy_runtime_api::client::auth::AuthSchemePreference",
2522
"aws_smithy_runtime_api::box_error::BoxError",
2623
"aws_smithy_runtime_api::client::behavior_version::BehaviorVersion",
2724
"aws_smithy_runtime_api::client::dns::ResolveDns",
28-
"aws_smithy_runtime_api::client::dns::SharedDnsResolver",
2925
"aws_smithy_runtime_api::client::http::HttpClient",
30-
"aws_smithy_runtime_api::client::http::SharedHttpClient",
3126
"aws_smithy_runtime_api::client::identity::ResolveCachedIdentity",
3227
"aws_smithy_runtime_api::client::identity::ResolveIdentity",
3328
"aws_smithy_runtime_api::client::orchestrator::HttpResponse",
34-
"aws_smithy_runtime_api::client::result::SdkError",
3529
"aws_smithy_runtime_api::client::retries::classifiers::ClassifyRetry",
3630
"aws_smithy_runtime_api::client::retries::classifiers::SharedRetryClassifier",
3731
"aws_smithy_runtime_api::client::stalled_stream_protection::StalledStreamProtectionConfig",
38-
"aws_smithy_types::body::SdkBody",
3932
"aws_smithy_types::checksum_config::RequestChecksumCalculation",
4033
"aws_smithy_types::checksum_config::ResponseChecksumValidation",
41-
"aws_smithy_types::retry",
4234
"aws_smithy_types::retry::*",
43-
"aws_smithy_types::timeout",
4435
"aws_smithy_types::timeout::OperationTimeoutConfig",
4536
"aws_smithy_types::timeout::TimeoutConfig",
4637
"aws_smithy_types::timeout::TimeoutConfigBuilder",

aws/rust-runtime/aws-config/src/default_provider.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,6 @@ pub mod checksums;
6969

7070
/// Default provider chain for account-based endpoint mode
7171
pub mod account_id_endpoint_mode;
72+
73+
/// Default provider chain for auth scheme preference list
74+
pub mod auth_scheme_preference;
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
use crate::provider_config::ProviderConfig;
7+
use aws_runtime::env_config::EnvConfigValue;
8+
use aws_smithy_runtime_api::client::auth::{AuthSchemeId, AuthSchemePreference};
9+
use aws_smithy_types::error::display::DisplayErrorContext;
10+
use std::borrow::Cow;
11+
use std::fmt;
12+
13+
mod env {
14+
pub(super) const AUTH_SCHEME_PREFERENCE: &str = "AWS_AUTH_SCHEME_PREFERENCE";
15+
}
16+
17+
mod profile_key {
18+
pub(super) const AUTH_SCHEME_PREFERENCE: &str = "auth_scheme_preference";
19+
}
20+
21+
/// Load the value for the auth scheme preference
22+
///
23+
/// This checks the following sources:
24+
/// 1. The environment variable `AWS_AUTH_SCHEME_PREFERENCE=scheme1,scheme2,scheme3`
25+
/// 2. The profile key `auth_scheme_preference=scheme1,scheme2,scheme3`
26+
///
27+
/// A scheme name can be either a fully qualified name or a shorthand with the namespace prefix trimmed.
28+
/// For example, valid scheme names include "aws.auth#sigv4", "smithy.api#httpBasicAuth", "sigv4", and "httpBasicAuth".
29+
/// Whitespace (spaces or tabs), including leading, trailing, and between names, is ignored.
30+
///
31+
/// Returns `None` if a parsed string component is empty when creating an `AuthSchemeId`.
32+
pub(crate) async fn auth_scheme_preference_provider(
33+
provider_config: &ProviderConfig,
34+
) -> Option<AuthSchemePreference> {
35+
let env = provider_config.env();
36+
let profiles = provider_config.profile().await;
37+
38+
EnvConfigValue::new()
39+
.env(env::AUTH_SCHEME_PREFERENCE)
40+
.profile(profile_key::AUTH_SCHEME_PREFERENCE)
41+
.validate(&env, profiles, parse_auth_scheme_names)
42+
.map_err(|err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for `AuthSchemePreference`"))
43+
.unwrap_or(None)
44+
}
45+
46+
fn parse_auth_scheme_names(csv: &str) -> Result<AuthSchemePreference, InvalidAuthSchemeNamesCsv> {
47+
csv.split(',')
48+
.map(|s| {
49+
let trimmed = s.trim().replace([' ', '\t'], "");
50+
if trimmed.is_empty() {
51+
return Err(InvalidAuthSchemeNamesCsv {
52+
value: format!("Empty name found in `{csv}`."),
53+
});
54+
}
55+
let scheme_name = trimmed.split('#').next_back().unwrap_or(&trimmed);
56+
Ok(AuthSchemeId::from(Cow::Owned(scheme_name.to_owned())))
57+
})
58+
.collect::<Result<Vec<_>, _>>()
59+
.map(AuthSchemePreference::from)
60+
}
61+
62+
#[derive(Debug)]
63+
pub(crate) struct InvalidAuthSchemeNamesCsv {
64+
value: String,
65+
}
66+
67+
impl fmt::Display for InvalidAuthSchemeNamesCsv {
68+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69+
write!(
70+
f,
71+
"Not a valid comma-separated auth scheme names: {}",
72+
self.value
73+
)
74+
}
75+
}
76+
77+
impl std::error::Error for InvalidAuthSchemeNamesCsv {}
78+
79+
#[cfg(test)]
80+
mod test {
81+
use super::env;
82+
use crate::{
83+
default_provider::auth_scheme_preference::auth_scheme_preference_provider,
84+
provider_config::ProviderConfig,
85+
};
86+
use aws_types::os_shim_internal::Env;
87+
use tracing_test::traced_test;
88+
89+
#[tokio::test]
90+
#[traced_test]
91+
async fn log_error_on_invalid_value() {
92+
let conf = ProviderConfig::empty().with_env(Env::from_slice(&[(
93+
env::AUTH_SCHEME_PREFERENCE,
94+
"scheme1, , \tscheme2",
95+
)]));
96+
assert_eq!(None, auth_scheme_preference_provider(&conf).await);
97+
assert!(logs_contain(
98+
"Not a valid comma-separated auth scheme names: Empty name found"
99+
));
100+
}
101+
102+
#[cfg(feature = "sso")] // for aws-smithy-runtime-api/http-auth
103+
mod http_auth_tests {
104+
use super::env;
105+
#[allow(deprecated)]
106+
use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
107+
use crate::{
108+
default_provider::auth_scheme_preference::auth_scheme_preference_provider,
109+
provider_config::ProviderConfig,
110+
};
111+
use aws_smithy_runtime_api::client::auth::AuthSchemePreference;
112+
use aws_types::os_shim_internal::{Env, Fs};
113+
114+
#[tokio::test]
115+
async fn environment_priority() {
116+
let conf = ProviderConfig::empty()
117+
.with_env(Env::from_slice(&[(
118+
env::AUTH_SCHEME_PREFERENCE,
119+
"aws.auth#sigv4, smithy.api#httpBasicAuth, smithy.api#httpDigestAuth, smithy.api#httpBearerAuth, smithy.api#httpApiKeyAuth",
120+
)]))
121+
.with_profile_config(
122+
Some(
123+
#[allow(deprecated)]
124+
ProfileFiles::builder()
125+
.with_file(
126+
#[allow(deprecated)]
127+
ProfileFileKind::Config,
128+
"conf",
129+
)
130+
.build(),
131+
),
132+
None,
133+
)
134+
.with_fs(Fs::from_slice(&[(
135+
"conf",
136+
"[default]\nauth_scheme_preference = scheme1, scheme2 , \tscheme3 \t",
137+
)]));
138+
assert_eq!(
139+
AuthSchemePreference::from([
140+
aws_runtime::auth::sigv4::SCHEME_ID,
141+
aws_smithy_runtime_api::client::auth::http::HTTP_BASIC_AUTH_SCHEME_ID,
142+
aws_smithy_runtime_api::client::auth::http::HTTP_DIGEST_AUTH_SCHEME_ID,
143+
aws_smithy_runtime_api::client::auth::http::HTTP_BEARER_AUTH_SCHEME_ID,
144+
aws_smithy_runtime_api::client::auth::http::HTTP_API_KEY_AUTH_SCHEME_ID,
145+
]),
146+
auth_scheme_preference_provider(&conf).await.unwrap()
147+
);
148+
}
149+
150+
#[tokio::test]
151+
async fn load_from_profile() {
152+
let conf = ProviderConfig::empty()
153+
.with_profile_config(
154+
Some(
155+
#[allow(deprecated)]
156+
ProfileFiles::builder()
157+
.with_file(
158+
#[allow(deprecated)]
159+
ProfileFileKind::Config,
160+
"conf",
161+
)
162+
.build(),
163+
),
164+
None,
165+
)
166+
.with_fs(Fs::from_slice(&[(
167+
"conf",
168+
"[default]\nauth_scheme_preference = sigv4, httpBasicAuth, httpDigestAuth, \thttpBearerAuth \t, httpApiKeyAuth ",
169+
)]));
170+
assert_eq!(
171+
AuthSchemePreference::from([
172+
aws_runtime::auth::sigv4::SCHEME_ID,
173+
aws_smithy_runtime_api::client::auth::http::HTTP_BASIC_AUTH_SCHEME_ID,
174+
aws_smithy_runtime_api::client::auth::http::HTTP_DIGEST_AUTH_SCHEME_ID,
175+
aws_smithy_runtime_api::client::auth::http::HTTP_BEARER_AUTH_SCHEME_ID,
176+
aws_smithy_runtime_api::client::auth::http::HTTP_API_KEY_AUTH_SCHEME_ID,
177+
]),
178+
auth_scheme_preference_provider(&conf).await.unwrap()
179+
);
180+
}
181+
}
182+
}

aws/rust-runtime/aws-config/src/lib.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ mod loader {
223223
use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
224224
use aws_smithy_async::time::{SharedTimeSource, TimeSource};
225225
use aws_smithy_runtime::client::identity::IdentityCache;
226+
use aws_smithy_runtime_api::client::auth::AuthSchemePreference;
226227
use aws_smithy_runtime_api::client::behavior_version::BehaviorVersion;
227228
use aws_smithy_runtime_api::client::http::HttpClient;
228229
use aws_smithy_runtime_api::client::identity::{ResolveCachedIdentity, SharedIdentityCache};
@@ -242,9 +243,10 @@ mod loader {
242243
use aws_types::SdkConfig;
243244

244245
use crate::default_provider::{
245-
account_id_endpoint_mode, app_name, checksums, credentials, disable_request_compression,
246-
endpoint_url, ignore_configured_endpoint_urls as ignore_ep, region,
247-
request_min_compression_size_bytes, retry_config, timeout_config, use_dual_stack, use_fips,
246+
account_id_endpoint_mode, app_name, auth_scheme_preference, checksums, credentials,
247+
disable_request_compression, endpoint_url, ignore_configured_endpoint_urls as ignore_ep,
248+
region, request_min_compression_size_bytes, retry_config, timeout_config, use_dual_stack,
249+
use_fips,
248250
};
249251
use crate::meta::region::ProvideRegion;
250252
#[allow(deprecated)]
@@ -271,6 +273,7 @@ mod loader {
271273
#[derive(Default, Debug)]
272274
pub struct ConfigLoader {
273275
app_name: Option<AppName>,
276+
auth_scheme_preference: Option<AuthSchemePreference>,
274277
identity_cache: Option<SharedIdentityCache>,
275278
credentials_provider: TriStateOption<SharedCredentialsProvider>,
276279
token_provider: Option<SharedTokenProvider>,
@@ -403,6 +406,28 @@ mod loader {
403406
self
404407
}
405408

409+
#[doc = docs_for!(auth_scheme_preference)]
410+
///
411+
/// # Examples
412+
/// ```no_run
413+
/// # use aws_smithy_runtime_api::client::auth::AuthSchemeId;
414+
/// # async fn create_config() {
415+
/// let config = aws_config::from_env()
416+
/// // Favors a custom auth scheme over the SigV4 auth scheme.
417+
/// // Note: This will not result in an error, even if the custom scheme is missing from the resolved auth schemes.
418+
/// .auth_scheme_preference([AuthSchemeId::from("custom"), aws_runtime::auth::sigv4::SCHEME_ID])
419+
/// .load()
420+
/// .await;
421+
/// # }
422+
/// ```
423+
pub fn auth_scheme_preference(
424+
mut self,
425+
auth_scheme_preference: impl Into<AuthSchemePreference>,
426+
) -> Self {
427+
self.auth_scheme_preference = Some(auth_scheme_preference.into());
428+
self
429+
}
430+
406431
/// Override the identity cache used to build [`SdkConfig`].
407432
///
408433
/// The identity cache caches AWS credentials and SSO tokens. By default, a lazy cache is used
@@ -962,6 +987,13 @@ mod loader {
962987
account_id_endpoint_mode::account_id_endpoint_mode_provider(&conf).await
963988
};
964989

990+
let auth_scheme_preference =
991+
if let Some(auth_scheme_preference) = self.auth_scheme_preference {
992+
Some(auth_scheme_preference)
993+
} else {
994+
auth_scheme_preference::auth_scheme_preference_provider(&conf).await
995+
};
996+
965997
builder.set_request_checksum_calculation(request_checksum_calculation);
966998
builder.set_response_checksum_validation(response_checksum_validation);
967999
builder.set_identity_cache(identity_cache);
@@ -974,6 +1006,7 @@ mod loader {
9741006
builder.set_request_min_compression_size_bytes(request_min_compression_size_bytes);
9751007
builder.set_stalled_stream_protection(self.stalled_stream_protection_config);
9761008
builder.set_account_id_endpoint_mode(account_id_endpoint_mode);
1009+
builder.set_auth_scheme_preference(auth_scheme_preference);
9771010
builder.build()
9781011
}
9791012
}

0 commit comments

Comments
 (0)