aws_smithy_runtime_api/client/
auth.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! APIs for request authentication.
7
8use crate::box_error::BoxError;
9use crate::client::identity::{Identity, SharedIdentityResolver};
10use crate::client::orchestrator::HttpRequest;
11use crate::client::runtime_components::sealed::ValidateConfig;
12use crate::client::runtime_components::{GetIdentityResolver, RuntimeComponents};
13use crate::impl_shared_conversions;
14use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
15use aws_smithy_types::type_erasure::TypeErasedBox;
16use aws_smithy_types::Document;
17use std::borrow::Cow;
18use std::fmt;
19use std::sync::Arc;
20
21/// Auth schemes for the HTTP `Authorization` header.
22#[cfg(feature = "http-auth")]
23pub mod http;
24
25/// Static auth scheme option resolver.
26pub mod static_resolver;
27
28/// New type around an auth scheme ID.
29///
30/// Each auth scheme must have a unique string identifier associated with it,
31/// which is used to refer to auth schemes by the auth scheme option resolver, and
32/// also used to select an identity resolver to use.
33#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
34pub struct AuthSchemeId {
35    scheme_id: &'static str,
36}
37
38impl AuthSchemeId {
39    /// Creates a new auth scheme ID.
40    pub const fn new(scheme_id: &'static str) -> Self {
41        Self { scheme_id }
42    }
43
44    /// Returns the string equivalent of this auth scheme ID.
45    pub const fn as_str(&self) -> &'static str {
46        self.scheme_id
47    }
48}
49
50impl From<&'static str> for AuthSchemeId {
51    fn from(scheme_id: &'static str) -> Self {
52        Self::new(scheme_id)
53    }
54}
55
56/// Parameters needed to resolve auth scheme options.
57///
58/// Most generated clients will use the [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver),
59/// which doesn't require any parameters for resolution (and has its own empty params struct).
60///
61/// However, more complex auth scheme resolvers may need modeled parameters in order to resolve
62/// the auth scheme options. For those, this params struct holds a type erased box so that any
63/// kind of parameters can be contained within, and type casted by the auth scheme option resolver
64/// implementation.
65#[derive(Debug)]
66pub struct AuthSchemeOptionResolverParams(TypeErasedBox);
67
68impl AuthSchemeOptionResolverParams {
69    /// Creates a new [`AuthSchemeOptionResolverParams`].
70    pub fn new<T: fmt::Debug + Send + Sync + 'static>(params: T) -> Self {
71        Self(TypeErasedBox::new(params))
72    }
73
74    /// Returns the underlying parameters as the type `T` if they are that type.
75    pub fn get<T: fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
76        self.0.downcast_ref()
77    }
78}
79
80impl Storable for AuthSchemeOptionResolverParams {
81    type Storer = StoreReplace<Self>;
82}
83
84/// Resolver for auth scheme options.
85///
86/// The orchestrator needs to select an auth scheme to sign requests with, and potentially
87/// from several different available auth schemes. Smithy models have a number of ways
88/// to specify which operations can use which auth schemes under which conditions, as
89/// documented in the [Smithy spec](https://smithy.io/2.0/spec/authentication-traits.html).
90///
91/// The orchestrator uses the auth scheme option resolver runtime component to resolve
92/// an ordered list of options that are available to choose from for a given request.
93/// This resolver can be a simple static list, such as with the
94/// [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver),
95/// or it can be a complex code generated resolver that incorporates parameters from both
96/// the model and the resolved endpoint.
97pub trait ResolveAuthSchemeOptions: Send + Sync + fmt::Debug {
98    /// Returns a list of available auth scheme options to choose from.
99    fn resolve_auth_scheme_options(
100        &self,
101        params: &AuthSchemeOptionResolverParams,
102    ) -> Result<Cow<'_, [AuthSchemeId]>, BoxError>;
103}
104
105/// A shared auth scheme option resolver.
106#[derive(Clone, Debug)]
107pub struct SharedAuthSchemeOptionResolver(Arc<dyn ResolveAuthSchemeOptions>);
108
109impl SharedAuthSchemeOptionResolver {
110    /// Creates a new [`SharedAuthSchemeOptionResolver`].
111    pub fn new(auth_scheme_option_resolver: impl ResolveAuthSchemeOptions + 'static) -> Self {
112        Self(Arc::new(auth_scheme_option_resolver))
113    }
114}
115
116impl ResolveAuthSchemeOptions for SharedAuthSchemeOptionResolver {
117    fn resolve_auth_scheme_options(
118        &self,
119        params: &AuthSchemeOptionResolverParams,
120    ) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
121        (*self.0).resolve_auth_scheme_options(params)
122    }
123}
124
125impl_shared_conversions!(
126    convert SharedAuthSchemeOptionResolver
127    from ResolveAuthSchemeOptions
128    using SharedAuthSchemeOptionResolver::new
129);
130
131/// An auth scheme.
132///
133/// Auth schemes have unique identifiers (the `scheme_id`),
134/// and provide an identity resolver and a signer.
135pub trait AuthScheme: Send + Sync + fmt::Debug {
136    /// Returns the unique identifier associated with this auth scheme.
137    ///
138    /// This identifier is used to refer to this auth scheme from the
139    /// [`ResolveAuthSchemeOptions`], and is also associated with
140    /// identity resolvers in the config.
141    fn scheme_id(&self) -> AuthSchemeId;
142
143    /// Returns the identity resolver that can resolve an identity for this scheme, if one is available.
144    ///
145    /// The [`AuthScheme`] doesn't actually own an identity resolver. Rather, identity resolvers
146    /// are configured as runtime components. The auth scheme merely chooses a compatible identity
147    /// resolver from the runtime components via the [`GetIdentityResolver`] trait. The trait is
148    /// given rather than the full set of runtime components to prevent complex resolution logic
149    /// involving multiple components from taking place in this function, since that's not the
150    /// intended use of this design.
151    fn identity_resolver(
152        &self,
153        identity_resolvers: &dyn GetIdentityResolver,
154    ) -> Option<SharedIdentityResolver>;
155
156    /// Returns the signing implementation for this auth scheme.
157    fn signer(&self) -> &dyn Sign;
158}
159
160/// Container for a shared auth scheme implementation.
161#[derive(Clone, Debug)]
162pub struct SharedAuthScheme(Arc<dyn AuthScheme>);
163
164impl SharedAuthScheme {
165    /// Creates a new [`SharedAuthScheme`] from the given auth scheme.
166    pub fn new(auth_scheme: impl AuthScheme + 'static) -> Self {
167        Self(Arc::new(auth_scheme))
168    }
169}
170
171impl AuthScheme for SharedAuthScheme {
172    fn scheme_id(&self) -> AuthSchemeId {
173        self.0.scheme_id()
174    }
175
176    fn identity_resolver(
177        &self,
178        identity_resolvers: &dyn GetIdentityResolver,
179    ) -> Option<SharedIdentityResolver> {
180        self.0.identity_resolver(identity_resolvers)
181    }
182
183    fn signer(&self) -> &dyn Sign {
184        self.0.signer()
185    }
186}
187
188impl ValidateConfig for SharedAuthScheme {}
189
190impl_shared_conversions!(convert SharedAuthScheme from AuthScheme using SharedAuthScheme::new);
191
192/// Signing implementation for an auth scheme.
193pub trait Sign: Send + Sync + fmt::Debug {
194    /// Sign the given request with the given identity, components, and config.
195    ///
196    /// If the provided identity is incompatible with this signer, an error must be returned.
197    fn sign_http_request(
198        &self,
199        request: &mut HttpRequest,
200        identity: &Identity,
201        auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>,
202        runtime_components: &RuntimeComponents,
203        config_bag: &ConfigBag,
204    ) -> Result<(), BoxError>;
205}
206
207/// Endpoint configuration for the selected auth scheme.
208///
209/// The configuration held by this struct originates from the endpoint rule set in the service model.
210///
211/// This struct gets added to the request state by the auth orchestrator.
212#[non_exhaustive]
213#[derive(Clone, Debug)]
214pub struct AuthSchemeEndpointConfig<'a>(Option<&'a Document>);
215
216impl<'a> AuthSchemeEndpointConfig<'a> {
217    /// Creates an empty [`AuthSchemeEndpointConfig`].
218    pub fn empty() -> Self {
219        Self(None)
220    }
221
222    /// Returns the endpoint configuration as a [`Document`].
223    pub fn as_document(&self) -> Option<&'a Document> {
224        self.0
225    }
226}
227
228impl<'a> From<Option<&'a Document>> for AuthSchemeEndpointConfig<'a> {
229    fn from(value: Option<&'a Document>) -> Self {
230        Self(value)
231    }
232}
233
234impl<'a> From<&'a Document> for AuthSchemeEndpointConfig<'a> {
235    fn from(value: &'a Document) -> Self {
236        Self(Some(value))
237    }
238}