Skip to content

Commit

Permalink
feat(experimentalIdentityAndAuth): enable identity and auth by default (
Browse files Browse the repository at this point in the history
smithy-lang#1352)

* feat(codegen): add `mutateClientPlugins()` integration hook

* feat(experimentalIdentityAndAuth): enable identity and auth by default

`experimentalIdentityAndAuth` behavior is now the default auth behavior.

The `experimentalIdentityAndAuth` flag is now oppositely replaced with
`useLegacyAuth`, which enables legacy auth behavior for backward
compatibility concerns.

* additional cleanup

* disable released version test

* feat(codegen): add PreCommandClassCodeSection

* wip: writerConsumers for plugins

* feat: enable writers for plugin parameters

* code style

* use templating, use instance name for plugin

---------

Co-authored-by: Steven Yuan <[email protected]>
  • Loading branch information
kuhe and Steven Yuan authored Jul 30, 2024
1 parent c86fa19 commit a40e1e9
Show file tree
Hide file tree
Showing 37 changed files with 664 additions and 243 deletions.
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

0 comments on commit a40e1e9

Please sign in to comment.