@middy/sts
Version:
STS (Security Token Service) credentials middleware for the middy framework
146 lines (130 loc) • 3.97 kB
JavaScript
// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors.
// SPDX-License-Identifier: MIT
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import {
assignSetToContext,
buildSetToContextSpec,
canPrefetch,
catchInvalidSignatureException,
createClient,
createPrefetchClient,
getCache,
modifyCache,
processCache,
validateOptions,
} from "@middy/util";
const name = "sts";
const pkg = `@middy/${name}`;
const defaults = {
AwsClient: STSClient,
awsClientOptions: {},
// awsClientAssumeRole: undefined, // Not Applicable, as this is the middleware that defines the roles
awsClientCapture: undefined,
fetchData: {}, // { contextKey: {RoleArn, RoleSessionName} }
disablePrefetch: false,
cacheKey: pkg,
cacheKeyExpiry: {},
cacheExpiry: -1,
setToContext: false,
};
const optionSchema = {
type: "object",
properties: {
AwsClient: { instanceof: "Function" },
awsClientOptions: { type: "object" },
awsClientAssumeRole: { type: "string" },
awsClientCapture: { instanceof: "Function" },
disablePrefetch: { type: "boolean" },
cacheKey: { type: "string" },
cacheKeyExpiry: {
type: "object",
additionalProperties: { type: "number", minimum: -1 },
},
cacheExpiry: { type: "number", minimum: -1 },
setToContext: { type: "boolean" },
fetchData: {
type: "object",
additionalProperties: {
type: "object",
required: ["RoleArn"],
properties: {
RoleArn: { type: "string" },
RoleSessionName: { type: "string" },
DurationSeconds: { type: "integer", minimum: 900, maximum: 43200 },
ExternalId: { type: "string" },
Policy: { type: "string" },
SerialNumber: { type: "string" },
TokenCode: { type: "string" },
TransitiveTagKeys: {
type: "array",
items: { type: "string" },
},
},
additionalProperties: true,
},
},
},
additionalProperties: false,
};
export const stsValidateOptions = (options) =>
validateOptions(pkg, optionSchema, options);
const stsMiddleware = (opts = {}) => {
const options = {
...defaults,
...opts,
fetchData: structuredClone({ ...defaults.fetchData, ...opts.fetchData }),
};
const fetchDataKeys = Object.keys(options.fetchData);
const contextSpec = buildSetToContextSpec(options);
const fetch = (request, cachedValues = {}) => {
const values = {};
for (const internalKey of fetchDataKeys) {
if (cachedValues[internalKey]) continue;
const assumeRoleOptions = options.fetchData[internalKey];
// Date cannot be used here to assign default session name, possibility of collision when > 1 role defined
assumeRoleOptions.RoleSessionName ??= `middy-sts-session-${Math.ceil(Math.random() * 99999)}`;
const command = new AssumeRoleCommand(assumeRoleOptions);
values[internalKey] = client
.send(command)
.catch((e) => catchInvalidSignatureException(e, client, command))
.then((resp) => ({
accessKeyId: resp.Credentials.AccessKeyId,
secretAccessKey: resp.Credentials.SecretAccessKey,
sessionToken: resp.Credentials.SessionToken,
}))
.catch((e) => {
const value = getCache(options.cacheKey).value ?? {};
value[internalKey] = undefined;
modifyCache(options.cacheKey, value);
throw e;
});
}
return values;
};
let client;
let clientInit;
if (canPrefetch(options)) {
client = createPrefetchClient(options);
processCache(options, fetch);
}
const stsMiddlewareBefore = async (request) => {
if (!client) {
clientInit ??= createClient(options, request);
client = await clientInit;
}
const { value } = processCache(options, fetch, request);
Object.assign(request.internal, value);
if (contextSpec) {
const pending = assignSetToContext(contextSpec, value, request);
if (pending) await pending;
}
};
return {
before: stsMiddlewareBefore,
};
};
export default stsMiddleware;
// used for TS type inference (see index.d.ts)
export function stsParam(name) {
return name;
}