Skip to content

Add Environment Token support #6130

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

Merged
merged 62 commits into from
Jun 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
e2e2fbf
Prototype implementation of Auth Scheme Preference
alextwoods Apr 15, 2025
6790d42
Add tracking of explictly set token provider
alextwoods Apr 16, 2025
b894b88
Use generated PreferredAuthSchemeProvider to wrap/delegate
alextwoods Apr 16, 2025
2bdaac7
Add generic-service environment token provider + customization config…
alextwoods Apr 16, 2025
f72dac1
Use generated PreferredAuthSchemeProvider to wrap/delegate
alextwoods Apr 16, 2025
7cc52ee
Merge branch 'alexwoo/configure-auth-preference' into alexwoo/bedrock…
alextwoods Apr 21, 2025
26b8fda
Include tokenProvider in service config when bearer is on the model
alextwoods May 1, 2025
14682dc
Support sourcing token from jvm settings + env variable.
alextwoods May 1, 2025
a6a1346
Refactor + use namingStrategy + add tests
alextwoods May 2, 2025
60165c1
Set business metric using an interceptor
alextwoods May 2, 2025
8feab9e
Add ability to override token provider on request
alextwoods May 2, 2025
fc6d3e6
Adding functionality to config preferred authschemeProvider
RanVaknin May 2, 2025
f18fcc2
adding test coverage
RanVaknin May 5, 2025
72b1386
fix formatting checkstyle
RanVaknin May 5, 2025
39dc9e4
Added changelog
RanVaknin May 5, 2025
345bb93
Fix checkstyle on prefered auth scheme provider
alextwoods May 5, 2025
e55fe52
Add validation of service+customization
alextwoods May 5, 2025
1849a83
Refactor env token customizaiton logic + add more tests
alextwoods May 5, 2025
e834b54
Testing and cleanup
alextwoods May 9, 2025
f31aa2a
Adding test coverage
RanVaknin May 19, 2025
2090d04
Merge branch 'master' into alexwoo/env-token-provider
alextwoods May 21, 2025
c20030b
Merge branch 'master' into rvaknin/auth-schem-preference-config
alextwoods May 23, 2025
17240db
Merge branch 'master' into rvaknin/auth-schem-preference-config
alextwoods May 23, 2025
a0094ed
Use SdkSystemSetting for both env and system
alextwoods May 23, 2025
badc138
Merge branch 'master' into alexwoo/env-token-provider
alextwoods May 23, 2025
a85dace
Merge branch 'rvaknin/auth-schem-preference-config' into alexwoo/env-…
alextwoods May 23, 2025
c4b6fbd
Add changelog
alextwoods May 23, 2025
f46a773
Add profiles to service pom
alextwoods May 23, 2025
48784f5
Minor cleanups
alextwoods May 23, 2025
6ae2294
Fix test
alextwoods May 23, 2025
4ae9a44
Fix protocol test dependencies
alextwoods May 23, 2025
a9e1a08
Remove dependency on profiles (use option get in generated code instead)
alextwoods May 23, 2025
430e335
Merge branch 'rvaknin/auth-schem-preference-config' into alexwoo/env-…
alextwoods May 23, 2025
73e0564
Fix checkstyle
alextwoods May 23, 2025
e20c041
Move AuthSchemePreferenceProvider out of internal module
alextwoods May 23, 2025
cb2d5a0
More checkstyle fixes
alextwoods May 23, 2025
ca2faac
Refactor and cleanup - move anon classes to full codegen classes.
alextwoods May 27, 2025
f3992dc
Update docs
alextwoods May 27, 2025
7a8bfa0
Additional codegen tests
alextwoods May 28, 2025
10c5e4f
Update core/aws-core/src/main/java/software/amazon/awssdk/awscore/int…
alextwoods May 28, 2025
e9afd67
Add codegen tset for base client builder w/ env bearer token
alextwoods May 28, 2025
fa17dcf
Fixing comments, adding fixture file, renaming provider to resolver, …
RanVaknin May 29, 2025
da1a81b
Add async client test
alextwoods May 29, 2025
39882e8
Move metric interceptor logic from beforeExecute to beforeMarshall
alextwoods May 29, 2025
cb799bb
Move metrics logic into auth scheme interceptor
alextwoods May 29, 2025
9c0980b
Remove unused import
alextwoods May 29, 2025
141b9d6
Add codegen test for preferred auth scheme provider
alextwoods May 29, 2025
491e3cf
Check for empty string
alextwoods May 29, 2025
e915d3e
Add more documentation
alextwoods May 29, 2025
729abbb
Revert "Add codegen test for preferred auth scheme provider"
alextwoods May 29, 2025
9abd847
Replace authprovider builder with overridden defaultProvider method
alextwoods Jun 2, 2025
1dc5f58
Update core/aws-core/src/main/java/software/amazon/awssdk/awscore/aut…
alextwoods Jun 3, 2025
22dbae7
Update core/aws-core/src/main/java/software/amazon/awssdk/awscore/aut…
alextwoods Jun 3, 2025
584c77c
Fix import from suggested changes
alextwoods Jun 3, 2025
1e85d4f
move test to aws-core module
RanVaknin Jun 4, 2025
c106168
Merge branch 'rvaknin/auth-schem-preference-config' into alexwoo/env-…
alextwoods Jun 5, 2025
7fdcb84
Merge branch 'master' into alexwoo/env-token-provider
alextwoods Jun 5, 2025
04596cf
Update codegen tests
alextwoods Jun 5, 2025
8ecc9dc
Fix checkstyle
alextwoods Jun 5, 2025
23c5b58
Fix checkstyle
alextwoods Jun 5, 2025
fec46b0
Improve test coverage
alextwoods Jun 5, 2025
a1a3c20
Fix docs
alextwoods Jun 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-1932d6a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Adds support for configuring bearer auth using a token sourced from the environment for services with the `enableEnvironmentBearerToken` customization flag."
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@

package software.amazon.awssdk.codegen.emitters.tasks;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import software.amazon.awssdk.codegen.emitters.GeneratorTask;
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
import software.amazon.awssdk.codegen.emitters.PoetGeneratorTask;
import software.amazon.awssdk.codegen.poet.client.EnvironmentTokenSystemSettingsClass;
import software.amazon.awssdk.codegen.poet.client.SdkClientOptions;
import software.amazon.awssdk.codegen.poet.common.UserAgentUtilsSpec;

Expand All @@ -33,7 +34,13 @@ public CommonInternalGeneratorTasks(GeneratorTaskParams params) {

@Override
protected List<GeneratorTask> createTasks() throws Exception {
return Arrays.asList(createClientOptionTask(), createUserAgentTask());
List<GeneratorTask> tasks = new ArrayList<>();
tasks.add(createClientOptionTask());
tasks.add(createUserAgentTask());
if (params.getModel().getCustomizationConfig().isEnableEnvironmentBearerToken()) {
tasks.add(createEnvironmentTokenSystemSettingTask());
}
return tasks;
}

private PoetGeneratorTask createClientOptionTask() {
Expand All @@ -46,6 +53,11 @@ private PoetGeneratorTask createUserAgentTask() {
new UserAgentUtilsSpec(params.getModel()));
}

private GeneratorTask createEnvironmentTokenSystemSettingTask() {
return new PoetGeneratorTask(clientOptionsDir(), params.getModel().getFileHeader(),
new EnvironmentTokenSystemSettingsClass(params.getModel()));
}

private String clientOptionsDir() {
return params.getPathProvider().getClientInternalDirectory();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,13 @@ public class CustomizationConfig {
*/
private boolean enableFastUnmarshaller;

/**
* A boolean flag to indicate if support for configuring a bearer token sourced from the environment should be added to the
* generated service. When enabled, the generated client will use bearer auth with the token sourced from the
* `AWS_BEARER_TOKEN_[SigningName]` environment variable.
*/
private boolean enableEnvironmentBearerToken = false;

private CustomizationConfig() {
}

Expand Down Expand Up @@ -924,4 +931,12 @@ public boolean getEnableFastUnmarshaller() {
public void setEnableFastUnmarshaller(boolean enableFastUnmarshaller) {
this.enableFastUnmarshaller = enableFastUnmarshaller;
}

public boolean isEnableEnvironmentBearerToken() {
return enableEnvironmentBearerToken;
}

public void setEnableEnvironmentBearerToken(boolean enableEnvironmentBearerToken) {
this.enableEnvironmentBearerToken = enableEnvironmentBearerToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,22 @@ private boolean isDisallowedNameForShape(String name, Shape parentShape) {
}
}

@Override
public String getSigningName() {
return Optional.ofNullable(serviceModel.getMetadata().getSigningName())
.orElseGet(() -> serviceModel.getMetadata().getEndpointPrefix());
}

@Override
public String getSigningNameForEnvironmentVariables() {
return screamCase(getSigningName());
}

@Override
public String getSigningNameForSystemProperties() {
return pascalCase(getSigningName());
}

@Override
public void validateCustomerVisibleNaming(IntermediateModel trimmedModel) {
Metadata metadata = trimmedModel.getMetadata();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ public interface NamingStrategy {
*/
String getExistenceCheckMethodName(String memberName, Shape parentShape);

/**
* Retrieve the service's signing name that should be used based on the model.
*/
String getSigningName();

/**
* Retrieve the service's signing name that should be used for environment variables.
*/
String getSigningNameForEnvironmentVariables();

/**
* Retrieve the service's signing name that should be used for system properties.
*/
String getSigningNameForSystemProperties();

/**
* Verify the customer-visible naming in the provided intermediate model will compile and is idiomatic to Java.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public ClassName getUserAgentClass() {
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "UserAgentUtils");
}

public ClassName getEnvironmentTokenSystemSettingsClass() {
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "EnvironmentTokenSystemSettings");
}

/**
* @param operationName Name of the operation
* @return A Poet {@link ClassName} for the response type of a paginated operation in the base service package.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.core.internal.util.MetricUtils;
import software.amazon.awssdk.core.metrics.CoreMetric;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.endpoints.EndpointProvider;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme;
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
Expand All @@ -70,8 +72,10 @@
public final class AuthSchemeInterceptorSpec implements ClassSpec {
private final AuthSchemeSpecUtils authSchemeSpecUtils;
private final EndpointRulesSpecUtils endpointRulesSpecUtils;
private final IntermediateModel intermediateModel;

public AuthSchemeInterceptorSpec(IntermediateModel intermediateModel) {
this.intermediateModel = intermediateModel;
this.authSchemeSpecUtils = new AuthSchemeSpecUtils(intermediateModel);
this.endpointRulesSpecUtils = new EndpointRulesSpecUtils(intermediateModel);
}
Expand Down Expand Up @@ -99,9 +103,42 @@ public TypeSpec poetSpec() {
.addMethod(generateTrySelectAuthScheme())
.addMethod(generateGetIdentityMetric())
.addMethod(putSelectedAuthSchemeMethodSpec());
if (intermediateModel.getCustomizationConfig().isEnableEnvironmentBearerToken()) {
builder.addMethod(generateEnvironmentTokenMetric());
}
return builder.build();
}

private MethodSpec generateEnvironmentTokenMetric() {
return MethodSpec
.methodBuilder("recordEnvironmentTokenBusinessMetric")
.addModifiers(Modifier.PRIVATE)
.addTypeVariable(TypeVariableName.get("T", Identity.class))
.addParameter(ParameterSpec.builder(
ParameterizedTypeName.get(ClassName.get(SelectedAuthScheme.class),
TypeVariableName.get("T")),
"selectedAuthScheme").build())
.addParameter(ExecutionAttributes.class, "executionAttributes")
.addStatement("$T tokenFromEnv = executionAttributes.getAttribute($T.TOKEN_CONFIGURED_FROM_ENV)",
String.class, SdkInternalExecutionAttribute.class)
.beginControlFlow("if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals($T"
+ ".SCHEME_ID) && selectedAuthScheme.identity().isDone())", BearerAuthScheme.class)
.beginControlFlow("if (selectedAuthScheme.identity().getNow(null) instanceof $T)", TokenIdentity.class)

.addStatement("$T configuredToken = ($T) selectedAuthScheme.identity().getNow(null)",
TokenIdentity.class, TokenIdentity.class)
.beginControlFlow("if (configuredToken.token().equals(tokenFromEnv))")
.addStatement("executionAttributes.getAttribute($T.BUSINESS_METRICS)"
+ ".addMetric($T.BEARER_SERVICE_ENV_VARS.value())",
SdkInternalExecutionAttribute.class, BusinessMetricFeatureId.class)
.endControlFlow()
.endControlFlow()
.endControlFlow()
.build();


}

private MethodSpec generateBeforeExecution() {
MethodSpec.Builder builder = MethodSpec.methodBuilder("beforeExecution")
.addAnnotation(Override.class)
Expand All @@ -116,6 +153,11 @@ private MethodSpec generateBeforeExecution() {
.addStatement("$T selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes)",
wildcardSelectedAuthScheme())
.addStatement("putSelectedAuthScheme(executionAttributes, selectedAuthScheme)");

if (intermediateModel.getCustomizationConfig().isEnableEnvironmentBearerToken()) {
builder.addStatement("recordEnvironmentTokenBusinessMetric(selectedAuthScheme, "
+ "executionAttributes)");
}
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.credentials.TokenUtils;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.auth.token.credentials.StaticTokenProvider;
import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider;
import software.amazon.awssdk.auth.token.signer.aws.BearerTokenSigner;
import software.amazon.awssdk.awscore.auth.AuthSchemePreferenceResolver;
Expand All @@ -53,6 +54,7 @@
import software.amazon.awssdk.codegen.model.service.AuthType;
import software.amazon.awssdk.codegen.model.service.ClientContextParam;
import software.amazon.awssdk.codegen.poet.ClassSpec;
import software.amazon.awssdk.codegen.poet.PoetExtension;
import software.amazon.awssdk.codegen.poet.PoetUtils;
import software.amazon.awssdk.codegen.poet.auth.scheme.AuthSchemeSpecUtils;
import software.amazon.awssdk.codegen.poet.auth.scheme.ModelAuthSchemeClassesKnowledgeIndex;
Expand All @@ -71,7 +73,9 @@
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.endpointdiscovery.providers.DefaultEndpointDiscoveryProviderChain;
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.Protocol;
Expand Down Expand Up @@ -102,6 +106,8 @@ public class BaseClientBuilderClass implements ClassSpec {
private final AuthSchemeSpecUtils authSchemeSpecUtils;
private final ServiceClientConfigurationUtils configurationUtils;
private final EndpointParamsKnowledgeIndex endpointParamsKnowledgeIndex;
private final PoetExtension poetExtensions;


public BaseClientBuilderClass(IntermediateModel model) {
this.model = model;
Expand All @@ -112,6 +118,7 @@ public BaseClientBuilderClass(IntermediateModel model) {
this.authSchemeSpecUtils = new AuthSchemeSpecUtils(model);
this.configurationUtils = new ServiceClientConfigurationUtils(model);
this.endpointParamsKnowledgeIndex = EndpointParamsKnowledgeIndex.of(model);
this.poetExtensions = new PoetExtension(model);
}

@Override
Expand Down Expand Up @@ -266,24 +273,24 @@ private MethodSpec serviceNameMethod() {
}

private MethodSpec mergeServiceDefaultsMethod() {
boolean crc32FromCompressedDataEnabled = model.getCustomizationConfig().isCalculateCrc32FromCompressedData();

MethodSpec.Builder builder = MethodSpec.methodBuilder("mergeServiceDefaults")
.addAnnotation(Override.class)
.addModifiers(PROTECTED, FINAL)
.returns(SdkClientConfiguration.class)
.addParameter(SdkClientConfiguration.class, "config")
.addCode("return config.merge(c -> c");
.addParameter(SdkClientConfiguration.class, "config");

builder.addCode(".option($T.ENDPOINT_PROVIDER, defaultEndpointProvider())", SdkClientOption.class);
boolean crc32FromCompressedDataEnabled = model.getCustomizationConfig().isCalculateCrc32FromCompressedData();

builder.beginControlFlow("return config.merge(c -> ");
builder.addCode("c.option($T.ENDPOINT_PROVIDER, defaultEndpointProvider())", SdkClientOption.class);

if (authSchemeSpecUtils.useSraAuth()) {
builder.addCode(".option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config))", SdkClientOption.class);
builder.addCode(".option($T.AUTH_SCHEMES, authSchemes())", SdkClientOption.class);
} else {
if (defaultAwsAuthSignerMethod().isPresent()) {
builder.addCode(".option($T.SIGNER, defaultSigner())\n", SdkAdvancedClientOption.class);
if (!model.getCustomizationConfig().isEnableEnvironmentBearerToken()) {
builder.addCode(".option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config))", SdkClientOption.class);
}
builder.addCode(".option($T.AUTH_SCHEMES, authSchemes())", SdkClientOption.class);
} else if (defaultAwsAuthSignerMethod().isPresent()) {
builder.addCode(".option($T.SIGNER, defaultSigner())\n", SdkAdvancedClientOption.class);
}
builder.addCode(".option($T.CRC32_FROM_COMPRESSED_DATA_ENABLED, $L)\n",
SdkClientOption.class, crc32FromCompressedDataEnabled);
Expand All @@ -302,11 +309,47 @@ private MethodSpec mergeServiceDefaultsMethod() {
builder.addCode(".option($T.TOKEN_SIGNER, defaultTokenSigner())", SdkAdvancedClientOption.class);
}
}
builder.addStatement("");

builder.addCode(");");
if (model.getCustomizationConfig().isEnableEnvironmentBearerToken()) {
configureEnvironmentBearerToken(builder);
}
builder.endControlFlow(")");
return builder.build();
}

private void configureEnvironmentBearerToken(MethodSpec.Builder builder) {
if (!authSchemeSpecUtils.useSraAuth()) {
throw new IllegalStateException("The enableEnvironmentBearerToken customization requires SRA Auth.");
}
if (!AuthUtils.usesBearerAuth(model)) {
throw new IllegalStateException("The enableEnvironmentBearerToken customization requires the service to model and "
+ "support smithy.api#httpBearerAuth.");
}

builder.addStatement("$T tokenFromEnv = new $T().getStringValue()",
ParameterizedTypeName.get(Optional.class, String.class),
poetExtensions.getEnvironmentTokenSystemSettingsClass());

builder
.beginControlFlow("if (tokenFromEnv.isPresent() && config.option($T.AUTH_SCHEME_PROVIDER) == null && config.option($T"
+ ".TOKEN_IDENTITY_PROVIDER) == null)",
SdkClientOption.class, AwsClientOption.class)
.addStatement("c.option($T.AUTH_SCHEME_PROVIDER, $T.defaultProvider($T.singletonList($S)))",
SdkClientOption.class, authSchemeSpecUtils.providerInterfaceName(), Collections.class,
"httpBearerAuth")
.addStatement("c.option($T.TOKEN_IDENTITY_PROVIDER, $T.create(tokenFromEnv::get))",
AwsClientOption.class, StaticTokenProvider.class)
.addStatement("c.option($T.EXECUTION_ATTRIBUTES, "
+ "$T.builder().put($T.TOKEN_CONFIGURED_FROM_ENV, tokenFromEnv.get()).build())",
SdkClientOption.class, ExecutionAttributes.class, SdkInternalExecutionAttribute.class)
.endControlFlow()
.beginControlFlow("else")
.addStatement("c.option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config))", SdkClientOption.class)
.endControlFlow();

}

private Optional<MethodSpec> mergeInternalDefaultsMethod() {
String userAgent = model.getCustomizationConfig().getUserAgent();
RetryMode defaultRetryMode = model.getCustomizationConfig().getDefaultRetryMode();
Expand Down
Loading
Loading