Skip to content
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

Smithy Bearer Auth #3102

Merged
merged 14 commits into from
Sep 10, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#pragma once

#include <smithy/identity/auth/AuthScheme.h>
#include <smithy/identity/auth/built-in/BearerTokenAuthSchemeOption.h>
#include <smithy/identity/identity/AwsBearerTokenIdentityBase.h>
#include <smithy/identity/resolver/AwsBearerTokenIdentityResolver.h>
#include <smithy/identity/signer/built-in/BearerTokenSigner.h>

namespace smithy
{
constexpr char BEARER_SIGNER[] = "Bearer";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was thinking about this the other day, do we really need this in its own constant or can we just pass the string in the parent ctor i.e.

AuthScheme("Bearer")

we avoid some namespace pollution that way

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had raised this before (during autha) but we agreed putting this in namespace smithy to comply with what Sergey originally proposed. We do pass it in the base class constructor. I can simply pass the raw string .
Whatever you finalize. I am ok with either

        std::shared_ptr<AwsCredentialIdentityResolverT> identityResolver,
        const Aws::String &serviceName, const Aws::String &region)
        : AuthScheme(BEARER_SIGNER), m_identityResolver{identityResolver},
          m_signer{Aws::MakeShared<smithy::BearerTokenSigner>(
              "BearerTokenAuthScheme", serviceName, region)}

Copy link
Contributor

@sbiscigl sbiscigl Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raw string is better in my opinion thinking about it, one less thing in the namespace that is actually internal to us


class BearerTokenAuthScheme : public AuthScheme<AwsBearerTokenIdentityBase>
{
public:
using AwsCredentialIdentityResolverT = IdentityResolverBase<IdentityT>;
using AwsCredentialSignerT = AwsSignerBase<IdentityT>;
using BearerTokenAuthSchemeParameters = DefaultAuthSchemeResolverParameters;

// This allows to override the identity resolver
explicit BearerTokenAuthScheme(
std::shared_ptr<AwsCredentialIdentityResolverT> identityResolver,
const Aws::String &serviceName, const Aws::String &region)
: AuthScheme(BEARER_SIGNER), m_identityResolver{identityResolver},
m_signer{Aws::MakeShared<smithy::BearerTokenSigner>(
"BearerTokenAuthScheme", serviceName, region)}
{
assert(m_identityResolver);
assert(m_signer);
}

explicit BearerTokenAuthScheme(const Aws::String &serviceName,
const Aws::String &region)
: BearerTokenAuthScheme(
Aws::MakeShared<DefaultAwsBearerTokenIdentityResolver>(
"BearerTokenAuthScheme"),
serviceName, region)
{
assert(m_identityResolver);

assert(m_signer);
}

virtual ~BearerTokenAuthScheme() = default;

std::shared_ptr<AwsCredentialIdentityResolverT> identityResolver() override
{
return m_identityResolver;
}

std::shared_ptr<AwsCredentialSignerT> signer() override { return m_signer; }

protected:
std::shared_ptr<AwsCredentialIdentityResolverT> m_identityResolver;
std::shared_ptr<AwsCredentialSignerT> m_signer;
};
} // namespace smithy
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#pragma once

#include <smithy/identity/auth/AuthSchemeOption.h>

namespace smithy
{
struct BearerTokenAuthSchemeOption
{
static AuthSchemeOption bearerTokenAuthSchemeOption;
};

AuthSchemeOption BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not inline this like in sigv4 and sigv4a, what makes this auth scheme different where a client needs this symbol and couldnt access it through BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption::schemeId

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change that but the main reason for having the variable is usage from other places, so that way we just refer to one constant instead of copies elsewhere. I think we should extend this to the other auths too if you like that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can refer to it other places using BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption::schemeId without needing the static const char

AuthSchemeOption("Bearer");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where did you get this string btw? in the smithy docs its smithy.api#HTTPBearerAuth

Copy link
Contributor Author

@sbera87 sbera87 Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@sbera87 sbera87 Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we moving to a new signer name? Coz, that's not what legacy signer name is . If so, an easy change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you think that is the name of the signer? look at what exists for the SigV4AuthSchemeOption
https://github.com/aws/aws-sdk-cpp/blob/main/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthSchemeOption.h#L15

and SigV4aAuthSchemeOption
https://github.com/aws/aws-sdk-cpp/blob/main/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4aAuthSchemeOption.h#L15

this is the AuthScheme name, which as in the SRA reference is named as smithy.api#HTTPBearerAuth. You can find the exact same in the scheme id for java for bearer auth
https://github.com/aws/aws-sdk-java-v2/blob/5253ae375004b0f51036657eda6fc539f9cd2035/core/http-auth/src/main/java/software/amazon/awssdk/http/auth/scheme/BearerAuthScheme.java#L36

} // namespace smithy
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#pragma once

#include <smithy/identity/auth/AuthSchemeResolverBase.h>
#include <smithy/identity/auth/built-in/BearerTokenAuthSchemeOption.h>

namespace smithy
{
template <typename ServiceAuthSchemeParametersT =
DefaultAuthSchemeResolverParameters>
class BearerTokenAuthSchemeResolver
: public AuthSchemeResolverBase<ServiceAuthSchemeParametersT>
{
public:
using ServiceAuthSchemeParameters = ServiceAuthSchemeParametersT;
virtual ~BearerTokenAuthSchemeResolver() = default;

Aws::Vector<AuthSchemeOption> resolveAuthScheme(
const ServiceAuthSchemeParameters &identityProperties) override
{
AWS_UNREFERENCED_PARAM(identityProperties);
return {BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption};
}
};
} // namespace smithy
Original file line number Diff line number Diff line change
Expand Up @@ -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<AwsIdentity::DateTime> expiration() override;
virtual Aws::Crt::Optional<AwsIdentity::DateTime>
expiration() const override;

Aws::String &token() { return m_token; }

Aws::Crt::Optional<AwsIdentity::DateTime> &expiration()
{
return m_expiration;
}

protected:
Aws::String m_token;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AwsIdentity::DateTime> expiration() override = 0 ;
virtual Aws::Crt::Optional<AwsIdentity::DateTime>
expiration() const override = 0;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
#include <smithy/identity/identity/AwsBearerTokenIdentity.h>

namespace smithy {
Aws::String AwsBearerTokenIdentity::token() {
return m_token;
}
const Aws::String &AwsBearerTokenIdentity::token() const { return m_token; }

Aws::Crt::Optional<AwsIdentity::DateTime> AwsBearerTokenIdentity::expiration() {
return m_expiration;
}
Aws::Crt::Optional<AwsIdentity::DateTime>
AwsBearerTokenIdentity::expiration() const
{
return m_expiration;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,92 @@
*/
#pragma once

#include <aws/core/auth/bearer-token-provider/AWSBearerTokenProviderBase.h>
#include <aws/core/auth/bearer-token-provider/SSOBearerTokenProvider.h>
#include <smithy/identity/identity/AwsBearerTokenIdentity.h>
#include <smithy/identity/resolver/AwsIdentityResolverBase.h>

#include <smithy/identity/identity/AwsBearerTokenIdentity.h>
namespace smithy
{

static const char SSO_DEFAULT_BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG[] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above, maybe we should just inline these to avoid pollution

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can put it in the class scope to make it better

"SSOBearerTokenProvider";
static const char BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG[] = "BearerTokenProvider";

class AwsBearerTokenIdentityResolver
: public IdentityResolverBase<AwsBearerTokenIdentityBase>
{
public:
using IdentityT = AwsBearerTokenIdentity;
virtual ~AwsBearerTokenIdentityResolver() = default;

AwsBearerTokenIdentityResolver() = default;

AwsBearerTokenIdentityResolver(
const Aws::Vector<
std::shared_ptr<Aws::Auth::AWSBearerTokenProviderBase>>
&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>(
Aws::Client::CoreErrors::INVALID_PARAMETER_VALUE, "",
"Unexpected nullptr in "
"BearerTokenProviderChain::m_providerChain",
false);
}
auto bearerToken = bearerTokenProvider->GetAWSBearerToken();
if (!bearerToken.IsExpiredOrEmpty())
{
auto outcomePtr = Aws::MakeUnique<AwsBearerTokenIdentity>(
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>(
Aws::Client::CoreErrors::NOT_INITIALIZED, "",
"No bearer token provider in chain found", false);
}

void AddBearerTokenProvider(
std::shared_ptr<Aws::Auth::AWSBearerTokenProviderBase> provider)
{
m_providerChainLegacy.emplace_back(std::move(provider));
}

protected:
Aws::Vector<std::shared_ptr<Aws::Auth::AWSBearerTokenProviderBase>>
m_providerChainLegacy;
};

namespace smithy {
class AwsBearerTokenIdentityResolver : public IdentityResolverBase<AwsBearerTokenIdentity> {
public:
using IdentityT = AwsBearerTokenIdentity;
virtual ~AwsBearerTokenIdentityResolver() = default;
class DefaultAwsBearerTokenIdentityResolver
: public AwsBearerTokenIdentityResolver
{
public:
using IdentityT = AwsBearerTokenIdentity;
virtual ~DefaultAwsBearerTokenIdentityResolver() = default;

ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) override = 0;
};
}
DefaultAwsBearerTokenIdentityResolver()
: AwsBearerTokenIdentityResolver(
{Aws::MakeShared<Aws::Auth::SSOBearerTokenProvider>(
SSO_DEFAULT_BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG)}){};
};
} // namespace smithy
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

#pragma once

#include <smithy/identity/identity/AwsBearerTokenIdentityBase.h>
#include <smithy/identity/signer/AwsSignerBase.h>

#include <aws/core/auth/signer/AWSAuthSignerHelper.h>
#include <aws/core/http/HttpRequest.h>
#include <aws/crt/http/HttpConnection.h>
#include <aws/crt/http/HttpRequestResponse.h>

namespace smithy
{
static const char AUTHORIZATION_HEADER[] = "authorization";

class BearerTokenSigner : public AwsSignerBase<AwsBearerTokenIdentityBase>
{

public:
static const char LOGGING_TAG[];

using BearerTokenAuthSchemeParameters =
smithy::DefaultAuthSchemeResolverParameters;
explicit BearerTokenSigner(const Aws::String &serviceName,
const Aws::String &region)
: m_serviceName(serviceName), m_region(region)
{
}

SigningFutureOutcome
sign(std::shared_ptr<HttpRequest> 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
Loading
Loading