AWS Secret Manager
AWS Secret Manager is a great tool to provide secure storage for your secrets like database passwords, JWT secret keys, API keys. Additionally it can be customized to store structures instead of just strings.
Ability to rotate the secrets is a great feature that allows to keep your secrets secure in case of a leak.
Installation
- npm
- Yarn
- pnpm
npm install @pallad/config-aws-secret-manager
yarn add @pallad/config-aws-secret-manager
pnpm add @pallad/config-aws-secret-manager
Retrieving secrets
You can retrieve entire secret by providing its name:
import { secretManagerProviderFactory } from '@pallad/config-aws-secret-manager';
const secretManager = secretManagerProviderFactory();
// retrieves entire "database" secret upon resolution
secretManager({name: 'database'})
Just a property from structured secret
import { secretManagerProviderFactory } from '@pallad/config-aws-secret-manager';
const secretManager = secretManagerProviderFactory();
// retrieves "database" secret and extracts property `username` from it
secretManager({name: 'database', property: 'username'})
Ensuring type safety
All values fetched from secret manager are treated as unknown
therefore extra validation is required to ensure type safety.
For property
import {type} from '@pallad/config';
// ensures that `username` property is a string
secretManager({name: 'database', property: 'username'}).transform(type.string);
// port to be an integer
secretManager({name: 'database', property: 'port'}).transform(type.int);
// custom validation
secretManager({name: 'database', property: 'password'}).transform((value) => {
if (someTypeCheck(value)) {
return value;
}
throw new Error('Invalid value');
});
For structure
Transform properties for type safety is easy but it can be cumbersome for complex structures therefore using transform
on entire secret might recommended.
import {type} from '@pallad/config';
import {z} from 'zod';
const databaseSchema = z.object({
username: z.string(),
password: z.string(),
port: z.number()
});
// ensures that entire secret is an object satisfying `databaseSchema`
secretManager({name: 'database'}).transform((value) => {
return databaseSchema.parse(value);
});
Customization
Prefix all secret names
import { secretManagerProviderFactory } from '@pallad/config-aws-secret-manager';
const secretManager = secretManagerProviderFactory({
prefix: '/env/prod/'
});
// retrieves "/env/prod/database" secret
secretManager('database')
Use custom instance of Secret Manager client
import { ssmProviderFactory } from '@pallad/config-aws-ssm';
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
const secretManager = secretManagerProviderFactory({
client: new SecretsManagerClient({region: 'us-east-1'})
});
// retrieves "database" secret from `us-east-1` region
secretManager('database')
Deserialization
By default, all secrets are stored as strings. If data looks like a JSON then it is being deserialized. If you need more control over deserialization you can provide custom deserializer.
import { SecretValueEntry } from "@aws-sdk/client-secrets-manager";
const secretManager = secretManagerProviderFactory({
deserialize(secret: SecretValueEntry) {
if (secret.SecretString) {
return customJSONDeserializer(secret.SecretString);
}
throw new Error('Unsupported secret format');
}
});