UNPKG

@middy/secrets-manager

Version:

Secrets Manager middleware for the middy framework

138 lines (123 loc) 3.49 kB
// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors. // SPDX-License-Identifier: MIT import { DescribeSecretCommand, GetSecretValueCommand, SecretsManagerClient, } from "@aws-sdk/client-secrets-manager"; import { canPrefetch, catchInvalidSignatureException, createClient, createPrefetchClient, getCache, getInternal, jsonSafeParse, modifyCache, processCache, } from "@middy/util"; const defaults = { AwsClient: SecretsManagerClient, awsClientOptions: {}, awsClientAssumeRole: undefined, awsClientCapture: undefined, fetchData: {}, fetchRotationDate: false, // true: apply to all or {key: true} for individual disablePrefetch: false, cacheKey: "secrets-manager", cacheKeyExpiry: {}, cacheExpiry: -1, // ignored when fetchRotationRules is true/object setToContext: false, }; const secretsManagerMiddleware = (opts = {}) => { const options = { ...defaults, ...opts, cacheKeyExpiry: { ...defaults.cacheKeyExpiry, ...opts.cacheKeyExpiry }, }; const fetchDataKeys = Object.keys(options.fetchData); const fetchRequest = (request, cachedValues = {}) => { const values = {}; for (const internalKey of fetchDataKeys) { if (cachedValues[internalKey]) continue; const fetchRotation = options.fetchRotationDate === true || options.fetchRotationDate?.[internalKey]; const rotationPromise = fetchRotation ? client .send( new DescribeSecretCommand({ SecretId: options.fetchData[internalKey], }), ) .catch((e) => catchInvalidSignatureException( e, client, new DescribeSecretCommand({ SecretId: options.fetchData[internalKey], }), ), ) .then((resp) => { if (options.cacheExpiry < 0) { options.cacheKeyExpiry[internalKey] = resp.NextRotationDate * 1000; } else { options.cacheKeyExpiry[internalKey] = Math.min( Math.max( resp.LastRotationDate ?? 0, resp.LastChangedDate ?? 0, ) * 1000 + options.cacheExpiry, resp.NextRotationDate * 1000, ); } }) : undefined; const fetchSecret = () => { const command = new GetSecretValueCommand({ SecretId: options.fetchData[internalKey], }); return client .send(command) .catch((e) => catchInvalidSignatureException(e, client, command)) .then((resp) => jsonSafeParse(resp.SecretString)); }; values[internalKey] = ( rotationPromise ? rotationPromise.then(fetchSecret) : fetchSecret() ).catch((e) => { const value = getCache(options.cacheKey).value ?? {}; value[internalKey] = undefined; modifyCache(options.cacheKey, value); throw e; }); } return values; }; let client; if (canPrefetch(options)) { client = createPrefetchClient(options); processCache(options, fetchRequest); } const secretsManagerMiddlewareBefore = async (request) => { if (!client) { client = await createClient(options, request); } const { value } = processCache(options, fetchRequest, request); Object.assign(request.internal, value); if (options.setToContext) { const data = await getInternal(fetchDataKeys, request); Object.assign(request.context, data); } }; return { before: secretsManagerMiddlewareBefore, }; }; export default secretsManagerMiddleware; // used for TS type inference (see index.d.ts) export function secretsManagerParam(name) { return name; }