diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthScheme.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthScheme.h new file mode 100644 index 00000000000..6d05f280b93 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthScheme.h @@ -0,0 +1,59 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include +#include +#include +#include +namespace smithy +{ +class BearerTokenAuthScheme : public AuthScheme +{ + public: + using AwsCredentialIdentityResolverT = IdentityResolverBase; + using AwsCredentialSignerT = AwsSignerBase; + using BearerTokenAuthSchemeParameters = DefaultAuthSchemeResolverParameters; + + // This allows to override the identity resolver + explicit BearerTokenAuthScheme( + std::shared_ptr identityResolver, + const Aws::String &serviceName, const Aws::String ®ion) + : AuthScheme("smithy.api#HTTPBearerAuth"), + m_identityResolver{identityResolver}, + m_signer{Aws::MakeShared( + "BearerTokenAuthScheme", serviceName, region)} + { + assert(m_identityResolver); + assert(m_signer); + } + + explicit BearerTokenAuthScheme(const Aws::String &serviceName, + const Aws::String ®ion) + : BearerTokenAuthScheme( + Aws::MakeShared( + "BearerTokenAuthScheme"), + serviceName, region) + { + assert(m_identityResolver); + + assert(m_signer); + } + + virtual ~BearerTokenAuthScheme() = default; + + std::shared_ptr identityResolver() override + { + return m_identityResolver; + } + + std::shared_ptr signer() override { return m_signer; } + + protected: + std::shared_ptr m_identityResolver; + std::shared_ptr m_signer; +}; +} // namespace smithy diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthSchemeOption.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthSchemeOption.h new file mode 100644 index 00000000000..2fbf4bec104 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthSchemeOption.h @@ -0,0 +1,17 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +namespace smithy +{ +struct BearerTokenAuthSchemeOption +{ + static AuthSchemeOption bearerTokenAuthSchemeOption; +}; + +AuthSchemeOption BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption = + AuthSchemeOption("smithy.api#HTTPBearerAuth"); +} // namespace smithy \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthSchemeResolver.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthSchemeResolver.h new file mode 100644 index 00000000000..091e64219d0 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthSchemeResolver.h @@ -0,0 +1,28 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include + +namespace smithy +{ +template +class BearerTokenAuthSchemeResolver + : public AuthSchemeResolverBase +{ + public: + using ServiceAuthSchemeParameters = ServiceAuthSchemeParametersT; + virtual ~BearerTokenAuthSchemeResolver() = default; + + Aws::Vector resolveAuthScheme( + const ServiceAuthSchemeParameters &identityProperties) override + { + AWS_UNREFERENCED_PARAM(identityProperties); + return {BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption}; + } +}; +} // namespace smithy \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentity.h b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentity.h index 4a0fffc585f..9491c748629 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentity.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentity.h @@ -9,9 +9,17 @@ namespace smithy { class AwsBearerTokenIdentity : public AwsBearerTokenIdentityBase { public: - virtual Aws::String token() override; + virtual const Aws::String &token() const override; - virtual Aws::Crt::Optional expiration() override; + virtual Aws::Crt::Optional + expiration() const override; + + Aws::String &token() { return m_token; } + + Aws::Crt::Optional &expiration() + { + return m_expiration; + } protected: Aws::String m_token; diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentityBase.h b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentityBase.h index bb4aabe2204..f66586abd6c 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentityBase.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsBearerTokenIdentityBase.h @@ -9,8 +9,9 @@ namespace smithy { class AwsBearerTokenIdentityBase : public AwsIdentity { public: - virtual Aws::String token() = 0; + virtual const Aws::String &token() const = 0; - virtual Aws::Crt::Optional expiration() override = 0 ; + virtual Aws::Crt::Optional + expiration() const override = 0; }; } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsBearerTokenIdentityImpl.h b/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsBearerTokenIdentityImpl.h index a27f46558ce..b9ca17748f2 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsBearerTokenIdentityImpl.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsBearerTokenIdentityImpl.h @@ -8,11 +8,11 @@ #include namespace smithy { - Aws::String AwsBearerTokenIdentity::token() { - return m_token; - } +const Aws::String &AwsBearerTokenIdentity::token() const { return m_token; } - Aws::Crt::Optional AwsBearerTokenIdentity::expiration() { - return m_expiration; - } +Aws::Crt::Optional +AwsBearerTokenIdentity::expiration() const +{ + return m_expiration; +} } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h index 5c387797e15..38ca79964a3 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h @@ -4,16 +4,94 @@ */ #pragma once +#include +#include +#include #include -#include +namespace smithy +{ + +class AwsBearerTokenIdentityResolver + : public IdentityResolverBase +{ + public: + static const char BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG[]; + + using IdentityT = AwsBearerTokenIdentity; + virtual ~AwsBearerTokenIdentityResolver() = default; + + AwsBearerTokenIdentityResolver() = default; + + AwsBearerTokenIdentityResolver( + const Aws::Vector< + std::shared_ptr> + &providerChain) + : m_providerChainLegacy{providerChain} + { + } + + ResolveIdentityFutureOutcome + getIdentity(const IdentityProperties &identityProperties, + const AdditionalParameters &additionalParameters) override + { + AWS_UNREFERENCED_PARAM(identityProperties); + AWS_UNREFERENCED_PARAM(additionalParameters); + for (auto &bearerTokenProvider : m_providerChainLegacy) + { + if (!bearerTokenProvider) + { + AWS_LOGSTREAM_FATAL( + BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG, + "Unexpected nullptr in " + "DefaultBearerTokenProviderChain::m_providerChain"); + return Aws::Client::AWSError( + Aws::Client::CoreErrors::INVALID_PARAMETER_VALUE, "", + "Unexpected nullptr in " + "BearerTokenProviderChain::m_providerChain", + false); + } + auto bearerToken = bearerTokenProvider->GetAWSBearerToken(); + if (!bearerToken.IsExpiredOrEmpty()) + { + auto outcomePtr = Aws::MakeUnique( + BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG); + outcomePtr->token() = bearerToken.GetToken(); + outcomePtr->expiration() = bearerToken.GetExpiration(); + return ResolveIdentityFutureOutcome(std::move(outcomePtr)); + } + } + + return Aws::Client::AWSError( + Aws::Client::CoreErrors::NOT_INITIALIZED, "", + "No bearer token provider in chain found", false); + } + + void AddBearerTokenProvider( + std::shared_ptr provider) + { + m_providerChainLegacy.emplace_back(std::move(provider)); + } + + protected: + Aws::Vector> + m_providerChainLegacy; +}; + +class DefaultAwsBearerTokenIdentityResolver + : public AwsBearerTokenIdentityResolver +{ + public: + using IdentityT = AwsBearerTokenIdentity; + virtual ~DefaultAwsBearerTokenIdentityResolver() = default; -namespace smithy { - class AwsBearerTokenIdentityResolver : public IdentityResolverBase { - public: - using IdentityT = AwsBearerTokenIdentity; - virtual ~AwsBearerTokenIdentityResolver() = default; + DefaultAwsBearerTokenIdentityResolver() + : AwsBearerTokenIdentityResolver( + {Aws::MakeShared( + "SSOBearerTokenProvider")}){}; +}; +const char + AwsBearerTokenIdentityResolver::BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG[] = + "BearerTokenProvider"; - ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) override = 0; - }; -} \ No newline at end of file +} // namespace smithy \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/signer/built-in/BearerTokenSigner.h b/src/aws-cpp-sdk-core/include/smithy/identity/signer/built-in/BearerTokenSigner.h new file mode 100644 index 00000000000..0c4551f076e --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/signer/built-in/BearerTokenSigner.h @@ -0,0 +1,68 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace smithy +{ +static const char AUTHORIZATION_HEADER[] = "authorization"; + +class BearerTokenSigner : public AwsSignerBase +{ + + public: + static const char LOGGING_TAG[]; + + using BearerTokenAuthSchemeParameters = + smithy::DefaultAuthSchemeResolverParameters; + explicit BearerTokenSigner(const Aws::String &serviceName, + const Aws::String ®ion) + : m_serviceName(serviceName), m_region(region) + { + } + + SigningFutureOutcome + sign(std::shared_ptr httpRequest, + const smithy::AwsBearerTokenIdentityBase &identity, + SigningProperties properties) override + { + AWS_UNREFERENCED_PARAM(properties); + + if (Aws::Http::Scheme::HTTPS != httpRequest->GetUri().GetScheme()) + { + // Clients MUST always use TLS (https) or equivalent transport + // security when making requests with bearer tokens. + // https://datatracker.ietf.org/doc/html/rfc6750 + AWS_LOGSTREAM_ERROR( + LOGGING_TAG, + "HTTPS scheme must be used with a bearer token authorization"); + return SigningError( + Aws::Client::CoreErrors::INVALID_PARAMETER_VALUE, "", + "Failed to sign the request with bearer", false); + } + + httpRequest->SetHeaderValue(AUTHORIZATION_HEADER, + "Bearer " + identity.token()); + + return SigningFutureOutcome(std::move(httpRequest)); + } + + virtual ~BearerTokenSigner(){}; + + protected: + Aws::String m_serviceName; + Aws::String m_region; +}; + +const char BearerTokenSigner::LOGGING_TAG[] = "BearerTokenSigner"; +} // namespace smithy diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp index 7bd40e01bb2..24a6a0e61eb 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp @@ -7,11 +7,14 @@ #include #include - #include #include #include +#include +#include +#include + #include #include #include @@ -21,6 +24,11 @@ #include #include +#include +#include + +static const char ALLOC_TAG[] = "SmithyClientTest"; + class TestEndPointProvider : public Aws::Endpoint::EndpointProviderBase<> { }; @@ -75,14 +83,39 @@ class SmithyClientTest : public Aws::Testing::AwsCppSdkGTestSuite { }; -const char SmithyClientTest::ALLOCATION_TAG[] = "SmithyClientTest"; - +//====================bearer token =============================== +class TestSSOBearerTokenProvider : public Aws::Auth::AWSBearerTokenProviderBase +{ + public: + Aws::Auth::AWSBearerToken GetAWSBearerToken() override + { + return Aws::Auth::AWSBearerToken{"testBearerToken", + Aws::Utils::DateTime::Now() + + std::chrono::milliseconds{100000}}; + } +}; +class TestAwsBearerTokenIdentityResolver + : public smithy::DefaultAwsBearerTokenIdentityResolver +{ + public: + TestAwsBearerTokenIdentityResolver() + : smithy::DefaultAwsBearerTokenIdentityResolver() + { + m_providerChainLegacy.insert( + m_providerChainLegacy.begin(), + Aws::MakeShared(ALLOC_TAG)); + } +}; +//=============================================================== +const char SmithyClientTest::ALLOCATION_TAG[] = "SmithyClientTest"; using MySmithyClientConfig = Aws::Client::ClientConfiguration; using MyServiceAuthSchemeResolver = smithy::AuthSchemeResolverBase; //smithy::SigV4AuthSchemeResolver<>; static constexpr char MyServiceName[] = "MySuperService"; -using SigVariant = Aws::Crt::Variant; +using SigVariant = + Aws::Crt::Variant; using MySmithyClient = smithy::client::AwsSmithyClientTGetUri().GetURIString(true).empty()); } + +TEST_F(SmithyClientTest, bearer) +{ + + std::shared_ptr authSchemeResolver = + Aws::MakeShared>( + ALLOCATION_TAG); + + Aws::UnorderedMap authSchemesMap; + + Aws::String key{ + smithy::BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption + .schemeId}; + + std::shared_ptr resolver = + Aws::MakeShared(ALLOCATION_TAG); + + SigVariant val{ + smithy::BearerTokenAuthScheme(resolver, "MyService", "us-west-2")}; + + authSchemesMap.emplace(key, val); + + std::shared_ptr ptr = Aws::MakeShared( + ALLOCATION_TAG, clientConfig, "MyAuthaService", httpClient, + errorMarshaller, endPointProvider, authSchemeResolver, authSchemesMap); + smithy::client::AwsSmithyClientAsyncRequestContext ctx; + ctx.m_pRequest = nullptr; + + auto res = ptr->SelectAuthSchemeOption(ctx); + + EXPECT_EQ(res.IsSuccess(), true); + + std::cout << "selected scheme id=" << res.GetResult().schemeId << std::endl; + EXPECT_EQ(res.GetResult().schemeId, key); + + Aws::String uri{ + "https://" + "treasureisland-cb93079d-24a0-4862-8es2-88456ead.xyz.amazonaws.com"}; + + std::shared_ptr httpRequest( + Aws::Http::CreateHttpRequest( + uri, Aws::Http::HttpMethod::HTTP_GET, + Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); + + auto res2 = ptr->SignRequest(httpRequest, res.GetResult()); + + EXPECT_EQ(res2.IsSuccess(), true); + + EXPECT_TRUE(!res2.GetResult()->GetHeaderValue("authorization").empty()); + + std::cout << "header=" << res2.GetResult()->GetHeaderValue("authorization") + << std::endl; + + EXPECT_EQ(res2.GetResult()->GetHeaderValue("authorization"), + "Bearer testBearerToken"); +} \ No newline at end of file