Skip to content

Commit a6aeed2

Browse files
authored
enhance(aws-sigv4): support promises/credentials providers (#1368)
1 parent bfe2ac7 commit a6aeed2

File tree

3 files changed

+113
-76
lines changed

3 files changed

+113
-76
lines changed

.changeset/two-ladybugs-fold.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
'@graphql-hive/gateway': patch
3+
'@graphql-hive/plugin-aws-sigv4': patch
4+
---
5+
6+
Support `Promise` as a result of `outgoing`;
7+
8+
So you can use credentials providers from `@aws-sdk/credential-providers` package.
9+
[See more](https://www.npmjs.com/package/@aws-sdk/credential-providers#fromnodeproviderchain).
10+
11+
```ts
12+
import { defineConfig } from '@graphql-hive/gateway';
13+
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
14+
15+
const config = defineConfig({
16+
plugins: [
17+
useAWSSigv4({
18+
outgoing: fromNodeProviderChain({
19+
// This provider accepts any input of fromEnv(), fromSSO(), fromTokenFile(),
20+
// fromIni(), fromProcess(), fromInstanceMetadata(), fromContainerMetadata()
21+
// that exist in the default credential chain.
22+
23+
// Optional client overrides. This is passed to an inner credentials client
24+
// that may be STS, SSO, or other instantiated to resolve the credentials.
25+
// Region and profile are inherited from the upper client if present
26+
// unless overridden, so it should not be necessary to set those.
27+
//
28+
// Warning: setting a region here may override the region set in
29+
// the config file for the selected profile if profile-based
30+
// credentials are used.
31+
clientConfig: {},
32+
}),
33+
}),
34+
],
35+
});
36+
```

packages/plugins/aws-sigv4/src/plugin.ts

Lines changed: 76 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -363,83 +363,84 @@ export function useAWSSigv4<TContext extends Record<string, any>>(
363363
if (!isBufferOrString(options.body)) {
364364
return;
365365
}
366-
const factoryResult = outgoingOptionsFactory({
367-
url,
368-
options,
369-
subgraphName,
370-
});
371-
if (factoryResult === false) {
372-
return;
373-
}
374-
let signQuery = false;
375-
let accessKeyId: string | undefined =
376-
getEnvStr('AWS_ACCESS_KEY_ID') || getEnvStr('AWS_ACCESS_KEY');
377-
let secretAccessKey: string | undefined =
378-
getEnvStr('AWS_SECRET_ACCESS_KEY') || getEnvStr('AWS_SECRET_KEY');
379-
let sessionToken: string | undefined = getEnvStr('AWS_SESSION_TOKEN');
380-
let service: string | undefined;
381-
let region: string | undefined;
382-
let roleArn: string | undefined = getEnvStr('AWS_ROLE_ARN');
383-
let roleSessionName: string | undefined = getEnvStr(
384-
'AWS_IAM_ROLE_SESSION_NAME',
385-
);
386-
if (typeof factoryResult === 'object' && factoryResult != null) {
387-
signQuery = factoryResult.signQuery || false;
388-
accessKeyId =
389-
factoryResult.accessKeyId ||
390-
getEnvStr('AWS_ACCESS_KEY_ID') ||
391-
getEnvStr('AWS_ACCESS_KEY');
392-
secretAccessKey =
393-
factoryResult.secretAccessKey ||
394-
getEnvStr('AWS_SECRET_ACCESS_KEY') ||
395-
getEnvStr('AWS_SECRET_KEY');
396-
sessionToken =
397-
factoryResult.sessionToken || getEnvStr('AWS_SESSION_TOKEN');
398-
roleArn = factoryResult.roleArn;
399-
roleSessionName =
400-
factoryResult.roleSessionName ||
401-
getEnvStr('AWS_IAM_ROLE_SESSION_NAME');
402-
service = factoryResult.serviceName;
403-
region = factoryResult.region;
404-
}
405366
return handleMaybePromise(
406-
() =>
407-
roleArn && roleSessionName
408-
? new STS({ region }).assumeRole({
409-
RoleArn: roleArn,
410-
RoleSessionName: roleSessionName,
411-
})
412-
: undefined,
413-
(stsResult) => {
414-
accessKeyId = stsResult?.Credentials?.AccessKeyId || accessKeyId;
415-
secretAccessKey =
416-
stsResult?.Credentials?.SecretAccessKey || secretAccessKey;
417-
sessionToken = stsResult?.Credentials?.SessionToken || sessionToken;
418-
const parsedUrl = new URL(url);
419-
const aws4Request: AWS4Request = {
420-
host: parsedUrl.host,
421-
method: options.method,
422-
path: `${parsedUrl.pathname}${parsedUrl.search}`,
423-
body: options.body as Buffer,
424-
headers: options.headers,
425-
signQuery,
426-
service,
427-
region,
428-
};
429-
const modifiedAws4Request = aws4.sign(aws4Request, {
430-
accessKeyId,
431-
secretAccessKey,
432-
sessionToken,
433-
});
434-
setURL(
435-
`${parsedUrl.protocol}//${modifiedAws4Request.host}${modifiedAws4Request.path}`,
367+
() => outgoingOptionsFactory({ url, options, subgraphName }),
368+
(factoryResult) => {
369+
if (factoryResult === false) {
370+
return;
371+
}
372+
let signQuery = false;
373+
let accessKeyId: string | undefined =
374+
getEnvStr('AWS_ACCESS_KEY_ID') || getEnvStr('AWS_ACCESS_KEY');
375+
let secretAccessKey: string | undefined =
376+
getEnvStr('AWS_SECRET_ACCESS_KEY') || getEnvStr('AWS_SECRET_KEY');
377+
let sessionToken: string | undefined = getEnvStr('AWS_SESSION_TOKEN');
378+
let service: string | undefined;
379+
let region: string | undefined;
380+
let roleArn: string | undefined = getEnvStr('AWS_ROLE_ARN');
381+
let roleSessionName: string | undefined = getEnvStr(
382+
'AWS_IAM_ROLE_SESSION_NAME',
383+
);
384+
if (typeof factoryResult === 'object' && factoryResult != null) {
385+
signQuery = factoryResult.signQuery || false;
386+
accessKeyId =
387+
factoryResult.accessKeyId ||
388+
getEnvStr('AWS_ACCESS_KEY_ID') ||
389+
getEnvStr('AWS_ACCESS_KEY');
390+
secretAccessKey =
391+
factoryResult.secretAccessKey ||
392+
getEnvStr('AWS_SECRET_ACCESS_KEY') ||
393+
getEnvStr('AWS_SECRET_KEY');
394+
sessionToken =
395+
factoryResult.sessionToken || getEnvStr('AWS_SESSION_TOKEN');
396+
roleArn = factoryResult.roleArn;
397+
roleSessionName =
398+
factoryResult.roleSessionName ||
399+
getEnvStr('AWS_IAM_ROLE_SESSION_NAME');
400+
service = factoryResult.serviceName;
401+
region = factoryResult.region;
402+
}
403+
return handleMaybePromise(
404+
() =>
405+
roleArn && roleSessionName
406+
? new STS({ region }).assumeRole({
407+
RoleArn: roleArn,
408+
RoleSessionName: roleSessionName,
409+
})
410+
: undefined,
411+
(stsResult) => {
412+
accessKeyId = stsResult?.Credentials?.AccessKeyId || accessKeyId;
413+
secretAccessKey =
414+
stsResult?.Credentials?.SecretAccessKey || secretAccessKey;
415+
sessionToken =
416+
stsResult?.Credentials?.SessionToken || sessionToken;
417+
const parsedUrl = new URL(url);
418+
const aws4Request: AWS4Request = {
419+
host: parsedUrl.host,
420+
method: options.method,
421+
path: `${parsedUrl.pathname}${parsedUrl.search}`,
422+
body: options.body as Buffer,
423+
headers: options.headers,
424+
signQuery,
425+
service,
426+
region,
427+
};
428+
const modifiedAws4Request = aws4.sign(aws4Request, {
429+
accessKeyId,
430+
secretAccessKey,
431+
sessionToken,
432+
});
433+
setURL(
434+
`${parsedUrl.protocol}//${modifiedAws4Request.host}${modifiedAws4Request.path}`,
435+
);
436+
setOptions({
437+
...options,
438+
method: modifiedAws4Request.method,
439+
headers: modifiedAws4Request.headers as Record<string, string>,
440+
body: modifiedAws4Request.body,
441+
});
442+
},
436443
);
437-
setOptions({
438-
...options,
439-
method: modifiedAws4Request.method,
440-
headers: modifiedAws4Request.headers as Record<string, string>,
441-
body: modifiedAws4Request.body,
442-
});
443444
},
444445
);
445446
},

packages/plugins/aws-sigv4/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export interface AWSSignv4PluginOutgoingOptionsFactoryOptions {
6969

7070
export type AWSSignv4PluginOutgoingOptionsFactory = (
7171
factoryOptions: AWSSignv4PluginOutgoingOptionsFactoryOptions,
72-
) => AWSSignv4PluginOutgoingOptions | undefined | false | true;
72+
) => MaybePromise<AWSSignv4PluginOutgoingOptions | undefined | false | true>;
7373

7474
export interface AWSSignv4PluginIncomingPayload {
7575
/**

0 commit comments

Comments
 (0)