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

feat(experimentalIdentityAndAuth): enable identity and auth by default #1352

Merged
merged 9 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/large-trainers-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/experimental-identity-and-auth": patch
---

set identity&auth SRA active by default
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ under development:

Experimental Feature | Flag | Description
---------------------|-------------------------------|------------
Identity & Auth | `experimentalIdentityAndAuth` | Standardize identity and auth integrations to match the Smithy specification (see [Authentication Traits](https://smithy.io/2.0/spec/authentication-traits.html)). Newer capabilities include support for multiple auth schemes, `@optionalAuth`, and standardized identity interfaces for authentication schemes both in code generation and TypeScript packages. In `smithy-typescript`, `@httpApiKeyAuth` will be updated to use the new standardized interfaces. In `aws-sdk-js-v3` (`smithy-typescript`'s largest customer), this will affect `@aws.auth#sigv4` and `@httpBearerAuth` implementations, but is planned to be completely backwards-compatible.
N/A | N/A | N/A

## Reporting Bugs/Feature Requests

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ By default, the Smithy TypeScript code generators provide the code generation fr
|`private`|No|Whether the package is `private` in `package.json`. The default value is `false`.|
|`requiredMemberMode`|No|**NOT RECOMMENDED DUE TO BACKWARD COMPATIBILITY CONCERNS.** Sets whether members marked with the `@required` trait are allowed to be `undefined`. See more details on the risks in `TypeScriptSettings.RequiredMemberMode`. The default value is `nullable`.|
|`createDefaultReadme`|No|Whether to generate a default `README.md` for the package. The default value is `false`.|
|`experimentalIdentityAndAuth`|No|Experimental feature that standardizes identity and auth integrations to match the Smithy specification (see [Authentication Traits](https://smithy.io/2.0/spec/authentication-traits.html)). See [the experimental features section for more details](CONTRIBUTING.md#experimental-features).|
|`useLegacyAuth`|No|**NOT RECOMMENDED, AVAILABLE ONLY FOR BACKWARD COMPATIBILITY CONCERNS.** Flag that enables using legacy auth. When in doubt, use the default identity and auth behavior (not configuring `useLegacyAuth`) as the golden path.|

#### `typescript-client-codegen` plugin artifacts

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { requireRequestsFrom } from "@smithy/util-test";

describe("@httpApiKeyAuth integration tests", () => {
// TODO(experimentalIdentityAndAuth): should match `HttpApiKeyAuthService` `@httpApiKeyAuth` trait
// Match `HttpApiKeyAuthService` `@httpApiKeyAuth` trait
const MOCK_API_KEY_NAME = "Authorization";
const MOCK_API_KEY_SCHEME = "ApiKey";
const MOCK_API_KEY = "APIKEY_123";
Expand Down
91 changes: 40 additions & 51 deletions scripts/build-generated-test-packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,59 +9,39 @@ const { spawnProcess } = require("./utils/spawn-process");

const root = path.join(__dirname, "..");

const testProjectDir = path.join(
root,
"smithy-typescript-codegen-test",
);
const testProjectDir = path.join(root, "smithy-typescript-codegen-test");

const codegenTestDir = path.join(
testProjectDir,
"build",
"smithyprojections",
"smithy-typescript-codegen-test",
);
const codegenTestDir = path.join(testProjectDir, "build", "smithyprojections", "smithy-typescript-codegen-test");

const weatherClientDir = path.join(
codegenTestDir,
"source",
"typescript-client-codegen"
);
const weatherClientDir = path.join(codegenTestDir, "source", "typescript-client-codegen");

const releasedClientDir = path.join(
testProjectDir,
"released-version-test",
"build",
"smithyprojections",
"released-version-test",
"source",
"typescript-codegen"
testProjectDir,
"released-version-test",
"build",
"smithyprojections",
"released-version-test",
"source",
"typescript-codegen"
);

// TODO(experimentalIdentityAndAuth): build generic client for integration tests
const weatherExperimentalIdentityAndAuthClientDir = path.join(
codegenTestDir,
"client-experimental-identity-and-auth",
"typescript-client-codegen"
);
// Build generic legacy auth client for integration tests
const weatherLegacyAuthClientDir = path.join(codegenTestDir, "client-legacy-auth", "typescript-client-codegen");

const weatherSsdkDir = path.join(
codegenTestDir,
"ssdk-test",
"typescript-server-codegen"
)
const weatherSsdkDir = path.join(codegenTestDir, "ssdk-test", "typescript-server-codegen");

// TODO(experimentalIdentityAndAuth): add `@httpApiKeyAuth` client for integration tests
// Build `@httpApiKeyAuth` client for integration tests
const httpApiKeyAuthClientDir = path.join(
codegenTestDir,
"identity-and-auth-http-api-key-auth",
"typescript-client-codegen"
codegenTestDir,
"identity-and-auth-http-api-key-auth",
"typescript-client-codegen"
);

// TODO(experimentalIdentityAndAuth): add `@httpBearerAuth` client for integration tests
// Build `@httpBearerAuth` client for integration tests
const httpBearerAuthClientDir = path.join(
codegenTestDir,
"identity-and-auth-http-bearer-auth",
"typescript-client-codegen"
codegenTestDir,
"identity-and-auth-http-bearer-auth",
"typescript-client-codegen"
);

const nodeModulesDir = path.join(root, "node_modules");
Expand All @@ -82,10 +62,14 @@ const buildAndCopyToNodeModules = async (packageName, codegenDir, nodeModulesDir
await spawnProcess("rm", ["-rf", packageName], { cwd: nodeModulesDir });
await spawnProcess("mkdir", ["-p", packageName], { cwd: nodeModulesDir });
const targetPackageDir = path.join(nodeModulesDir, packageName);
await spawnProcess("tar", ["-xf", "package.tgz", "-C", targetPackageDir, "--strip-components", "1"], { cwd: codegenDir });
await spawnProcess("tar", ["-xf", "package.tgz", "-C", targetPackageDir, "--strip-components", "1"], {
cwd: codegenDir,
});
}
} catch (e) {
console.log(`Building and copying package \`${packageName}\` in \`${codegenDir}\` to \`${nodeModulesDir}\` failed:`)
console.log(
`Building and copying package \`${packageName}\` in \`${codegenDir}\` to \`${nodeModulesDir}\` failed:`
);
console.log(e);
process.exit(1);
}
Expand All @@ -94,12 +78,17 @@ const buildAndCopyToNodeModules = async (packageName, codegenDir, nodeModulesDir
(async () => {
await buildAndCopyToNodeModules("weather", weatherClientDir, nodeModulesDir);
await buildAndCopyToNodeModules("weather-ssdk", weatherSsdkDir, nodeModulesDir);
// TODO(experimentalIdentityAndAuth): build generic client for integration tests
await buildAndCopyToNodeModules("@smithy/weather-experimental-identity-and-auth", weatherExperimentalIdentityAndAuthClientDir, nodeModulesDir);
// TODO(experimentalIdentityAndAuth): add `@httpApiKeyAuth` client for integration tests
await buildAndCopyToNodeModules("@smithy/identity-and-auth-http-api-key-auth-service", httpApiKeyAuthClientDir, nodeModulesDir);
// TODO(experimentalIdentityAndAuth): add `@httpBearerAuth` client for integration tests
await buildAndCopyToNodeModules("@smithy/identity-and-auth-http-bearer-auth-service", httpBearerAuthClientDir, nodeModulesDir);
// Test released version of smithy-typescript codegenerators, but
await buildAndCopyToNodeModules("released", releasedClientDir, undefined);
await buildAndCopyToNodeModules("@smithy/weather-legacy-auth", weatherLegacyAuthClientDir, nodeModulesDir);
await buildAndCopyToNodeModules(
"@smithy/identity-and-auth-http-api-key-auth-service",
httpApiKeyAuthClientDir,
nodeModulesDir
);
await buildAndCopyToNodeModules(
"@smithy/identity-and-auth-http-bearer-auth-service",
httpBearerAuthClientDir,
nodeModulesDir
);
// TODO(released-version-test): Test released version of smithy-typescript codegenerators, but currently is not working
// await buildAndCopyToNodeModules("released", releasedClientDir, undefined);
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package example.weather;

import java.util.Optional;
import java.util.function.Consumer;
import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.codegen.core.SymbolReference;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.typescript.codegen.ApplicationProtocol;
import software.amazon.smithy.typescript.codegen.LanguageTarget;
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
import software.amazon.smithy.typescript.codegen.auth.http.ConfigField;
import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthOptionProperty;
import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme;
import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthSchemeParameter;
import software.amazon.smithy.typescript.codegen.auth.http.integration.HttpAuthTypeScriptIntegration;
import software.amazon.smithy.utils.SmithyInternalApi;

@SmithyInternalApi
public final class SupportWeatherSigV4Auth implements HttpAuthTypeScriptIntegration {
static final Symbol AWS_CREDENTIAL_IDENTITY = Symbol.builder()
.name("AwsCredentialIdentity")
.namespace(TypeScriptDependency.SMITHY_TYPES.getPackageName(), "/")
.addDependency(TypeScriptDependency.SMITHY_TYPES)
.build();
static final Symbol AWS_CREDENTIAL_IDENTITY_PROVIDER = Symbol.builder()
.name("AwsCredentialIdentityProvider")
.namespace(TypeScriptDependency.SMITHY_TYPES.getPackageName(), "/")
.addDependency(TypeScriptDependency.SMITHY_TYPES)
.build();
static final ConfigField CREDENTIALS_CONFIG_FIELD = ConfigField.builder()
.name("credentials")
.type(ConfigField.Type.MAIN)
.docs(w -> w.write("The credentials used to sign requests."))
.inputType(Symbol.builder()
.name("AwsCredentialIdentity | AwsCredentialIdentityProvider")
.addReference(AWS_CREDENTIAL_IDENTITY)
.addReference(AWS_CREDENTIAL_IDENTITY_PROVIDER)
.build())
.resolvedType(Symbol.builder()
.name("AwsCredentialIdentityProvider")
.addReference(AWS_CREDENTIAL_IDENTITY)
.addReference(AWS_CREDENTIAL_IDENTITY_PROVIDER)
.build())
.configFieldWriter(ConfigField::defaultMainConfigFieldWriter)
.build();
private static final Consumer<TypeScriptWriter> AWS_SIGV4_AUTH_SIGNER = w -> {
w.addDependency(TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH);
w.addImport("SigV4Signer", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH);
w.write("new SigV4Signer()");
};
private static final SymbolReference PROVIDER = SymbolReference.builder()
.symbol(Symbol.builder()
.name("Provider")
.namespace(TypeScriptDependency.SMITHY_TYPES.getPackageName(), "/")
.addDependency(TypeScriptDependency.SMITHY_TYPES)
.build())
.alias("__Provider")
.build();

@Override
public boolean matchesSettings(TypeScriptSettings settings) {
return !settings.useLegacyAuth();
}

@Override
public Optional<HttpAuthScheme> getHttpAuthScheme() {
return Optional.of(HttpAuthScheme.builder()
.schemeId(ShapeId.from("aws.auth#sigv4"))
.applicationProtocol(ApplicationProtocol.createDefaultHttpApplicationProtocol())
.putDefaultSigner(LanguageTarget.SHARED, AWS_SIGV4_AUTH_SIGNER)
.addConfigField(CREDENTIALS_CONFIG_FIELD)
.addConfigField(ConfigField.builder()
.name("region")
.type(ConfigField.Type.AUXILIARY)
.docs(w -> w.write("The AWS region to which this client will send requests."))
.inputType(Symbol.builder()
.name("string | __Provider<string>")
.addReference(PROVIDER)
.build())
.resolvedType(Symbol.builder()
.name("__Provider<string>")
.addReference(PROVIDER)
.build())
.configFieldWriter(ConfigField::defaultAuxiliaryConfigFieldWriter)
.build())
.addHttpAuthSchemeParameter(HttpAuthSchemeParameter.builder()
.name("region")
.type(w -> w.write("string"))
.source(w -> {
w.addDependency(TypeScriptDependency.UTIL_MIDDLEWARE);
w.addImport("normalizeProvider", null, TypeScriptDependency.UTIL_MIDDLEWARE);
w.openBlock("await normalizeProvider(config.region)() || (() => {", "})()", () -> {
w.write("throw new Error(\"expected `region` to be configured for `aws.auth#sigv4`\");");
});
})
.build())
.addHttpAuthOptionProperty(HttpAuthOptionProperty.builder()
.name("name")
.type(HttpAuthOptionProperty.Type.SIGNING)
.source(s -> w -> {
w.write("$S", s.trait().toNode().expectObjectNode().getMember("name"));
})
.build())
.addHttpAuthOptionProperty(HttpAuthOptionProperty.builder()
.name("region")
.type(HttpAuthOptionProperty.Type.SIGNING)
.source(t -> w -> {
w.write("authParameters.region");
})
.build())
.propertiesExtractor(s -> w -> w
.write("""
(config, context) => {
return {
/**
* @internal
*/
signingProperties: {
...config,
...context,
},
};
},"""))
.build());
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
example.weather.ExampleWeatherCustomEndpointsRuntimeConfig
example.weather.SupportWeatherSigV4Auth
2 changes: 1 addition & 1 deletion smithy-typescript-codegen-test/model/weather/main.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ service Weather {
GetCurrentTime
// util-stream.integ.spec.ts
Invoke
// experimentalIdentityAndAuth
// Identity and Auth
OnlyHttpApiKeyAuth
OnlyHttpApiKeyAuthOptional
OnlyHttpBearerAuth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

// TODO(released-version-test): Test released version of smithy-typescript codegenerators, but currently is extremely flaky
/*
plugins {
java
id("software.amazon.smithy.gradle.smithy-base")
Expand All @@ -27,3 +29,4 @@ dependencies {
}

tasks["jar"].enabled = false
*/
20 changes: 9 additions & 11 deletions smithy-typescript-codegen-test/smithy-build.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
}
}
},
"client-experimental-identity-and-auth": {
"client-identity-and-auth": {
"transforms": [
{
"name": "includeServices",
Expand All @@ -41,17 +41,16 @@
"plugins": {
"typescript-client-codegen": {
"service": "example.weather#Weather",
"package": "@smithy/weather-experimental-identity-and-auth",
"package": "weather",
"packageVersion": "0.0.1",
"packageJson": {
"license": "Apache-2.0",
"private": true
},
"experimentalIdentityAndAuth": true
}
}
}
},
"control-experimental-identity-and-auth": {
"client-legacy-auth": {
"transforms": [
{
"name": "includeServices",
Expand All @@ -63,12 +62,13 @@
"plugins": {
"typescript-client-codegen": {
"service": "example.weather#Weather",
"package": "weather",
"package": "@smithy/weather-legacy-auth",
"packageVersion": "0.0.1",
"packageJson": {
"license": "Apache-2.0",
"private": true
}
},
"useLegacyAuth": true
}
}
},
Expand All @@ -89,8 +89,7 @@
"packageJson": {
"license": "Apache-2.0",
"private": true
},
"experimentalIdentityAndAuth": true
}
}
}
},
Expand All @@ -111,8 +110,7 @@
"packageJson": {
"license": "Apache-2.0",
"private": true
},
"experimentalIdentityAndAuth": true
}
}
}
}
Expand Down
Loading
Loading