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}