Automatically update secrets on an interval with support for seamless secret rotation.
Reference docs: https://electrovir.github.io/updating-secrets
npm i updating-secrets
First, define your set of secrets:
import {defineSecrets, rotatableSecretShape} from 'updating-secrets';
// example collection of secrets
export const mySecrets = defineSecrets({
databaseCredentials: {
description: 'All credentials and access information needed for accessing the database.',
whereToFind:
'These values are automatically generated by RDS and only stored in AWS Secrets Manager.',
adapterConfig: {
aws: {
rootOf: 'prod/DatabaseCredentials',
},
},
shape: {
password: '',
dbname: '',
port: -1,
host: '',
username: '',
},
},
stripeSecret: {
description: 'Keys for accessing and authenticating with Stripe.',
whereToFind: 'Navigate to Developers > API keys > Standard keys > Secret key.',
adapterConfig: {
aws: {
keyIn: 'prod/BackendSecrets',
},
},
},
adminPassword: {
description:
'Password required by the admin to access sensitive information on the website.',
whereToFind: 'This is randomly generated and stored in AWS Secrets Manager.',
adapterConfig: {
aws: {
keyIn: 'prod/BackendSecrets',
},
},
shape: rotatableSecretShape,
},
});
Second, choose your secrets adapters:
AwsSecretsManagerAdapter: loads secrets from AWS Secrets Manager.SecretsJsonFileAdapter: loads secrets from a JSON file.StaticSecretsAdapter: allows you to define all secrets values in-place.Or create your own:
import {BaseSecretsAdapter, type ProcessedSecretDefinitions} from 'updating-secrets';
export class MyCustomSecretsAdapter extends BaseSecretsAdapter {
constructor() {
super('MyCustomSecretsAdapter');
}
public override loadSecrets(secrets: Readonly<ProcessedSecretDefinitions>) {
// load secrets here
return {};
}
}
Lastly, create an instance of UpdatingSecrets (with createUpdatingSecrets):
import {SecretsManager} from '@aws-sdk/client-secrets-manager';
import {AwsSecretsManagerAdapter, createUpdatingSecrets} from 'updating-secrets';
import {mySecrets} from './define-secrets.example.js';
const updatingSecrets = await createUpdatingSecrets(mySecrets, [
new AwsSecretsManagerAdapter(
new SecretsManager({
region: 'us-east-1',
}),
),
]);
To create a seamlessly rotatable secret, use rotatableSecretShape in the secret definition's shape property, either as the root (shape: rotatableSecretShape) or as a sub-property (secret: {id: '', secret: rotatableSecretShape}).
Use this secret in the following way:
{current: 'latest-value'}legacy property: {current: 'new-value', legacy: 'old-value'}.currentUpdatingSecrets.compareRotatableSecret() to allow both the current and the legacy secret to pass comparisons:// assuming the `apiKey` secret is defined with `shape: rotatableSecretShape`
updatingSecrets.compareRotatableSecret(request.headers.apiKey, updatingSecrets.get.apiKey);