adonisjs, adonis, credentials
AdonisJS Credentials is created to help you manage multiple environment secrets, share them securely and even keep them inside your repo.
Inspired by Rails Credentials.
Let's say you decided to build a real world app and your app will connect to a database, a mail provider, it will have some social authentication, you may upload files to a cloud storage and here you are, with +/- 15-20 secret keys that you'll have to manage in your .env
file, share with your team and somehow sync with he server.
That's a tedious task, trust me. One of my apps has 100+ secrets and all of them had to be set up by hand.
Here comes this package, it allows you to take all of these secrets and keys, pack them into an easily readable .yaml
file, encrypt its content into a very long secure string and keep it inside you git repo.
This way you, your team and your server can get access to all of these secrets just by reading this string kept in a .credentials
file and all what they need is a .key
file or its content set in a single .env
variable APP_CREDENTIALS_KEY
.
To install the provider run:
npm install @bitkidd/adonisjs-credentials
# or
yarn add @bitkidd/adonisjs-credentials
To configure credentials provider, we should proceed with 4 steps:
node ace configure @bitkidd/adonisjs-credentials
This will:
- Add two new commands to your app and will allow to create and edit credentials
- Add a new rule to your
.gitignore
file that will exclude all*.key
files from repository and will not allow to commit them - Add a new
credentials.ts
file inside/start
folder
As a next step you need to modify the bin/server.ts
file and add a new line inside it:
...
app.booting(async () => {
await import('#start/env')
await import('#start/credentials') // <--- Import credentials here
})
...
Next you need to modify the bin/console.ts
file and add a new line inside it:
...
app.booting(async () => {
await import('#start/env')
await import('#start/credentials') // <--- Import credentials here
})
...
This will allow commands and app that they will start get access to credentials.
As you configured the provider, you may now create your first credentials by running the command:
# node ace credentials:create
# ---
# Flags
# --env string Specify an environment for credentials file (default: development)
node ace credentials:create
This will create a new directory in your resources
folder, called credentials
and will add there two new files, development.key
and development.credentials
.
Obviously, the .key
file keeps your password to the credentials file, do not commit any .key files to your git repo, please check your .gitignore
for *.key
exclusion rule.
.key
should be kept somewhere in a secret place, the best spot I know is a sticky note on your laptop. Just NO, don't do this 🙈
Keep your secrets in a secure place and use password managers!
.credentials
file can be committed and shared as it is impossible to decrypt without the key.
These two files should always be kept in one folder while in development.
To edit a newly created file, you should run a command:
# node ace credentials:edit
# ---
# Flags
# --env string Specify an environment for credentials file (default: development)
# --editor string Specify an editor to use for edit
node ace credentials:edit --editor="code ---wait" --env=development
# or
node ace credentials:edit --editor=nano --env=development
You can also add EDITOR='code --wait'
to your .env
file to omit --editor
flag.
This will decrypt the credentials file, create a temporary one and open it in the editor you specified. As you finish editing, close the file (or tab inside your editor), this will encrypt it back again and remove the temporary file, to keep you safe and sound.
Credentials storage mimics core AdonisJS env
package and provides the same schema validation.
Your newly created #start/credentials
file looks like this:
import { Credentials } from '@bitkidd/adonisjs-credentials'
export default await Credentials.create({
HELLO: Credentials.schema.string(),
})
And you can easily include your database credentials inside it likewise:
import { Credentials } from '@bitkidd/adonisjs-credentials'
export default await Credentials.create({
HELLO: Credentials.schema.string(),
DB_HOST: Credentials.schema.string({ format: 'host' }),
DB_PORT: Credentials.schema.number(),
DB_USER: Credentials.schema.string(),
DB_PASSWORD: Credentials.schema.string.optional(),
DB_DATABASE: Credentials.schema.string(),
})
To get your secret from credentials storage you can import directly #starts/credentials
. Foe example:
import credentials from '#start/credentials'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: {
host: credentials.get('DB_HOST'),
port: credentials.get('DB_PORT'),
user: credentials.get('DB_USER'),
password: credentials.get('DB_PASSWORD'),
database: credentials.get('DB_DATABASE'),
},
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
debug: true,
},
},
})
export default dbConfig
You can have multiple credential files, the best way to work is to create one for each environment, for example: development, production, staging, test and etc.
As for development you can keep .key
files inside /credentials
folder, in a production environment this is not a great option.
For production you should set additional environment variable APP_CREDENTIALS_KEY
, that will be used to decrypt data and populate it to your app.
The provider uses node.js' native crypto library and encrypts everything using AES cipher with a random vector, which makes your secrets very secure, with a single key that can decrypt data.
Credentials while decrypted present themselves as simple files in YAML format, this allows you to keep variables in a very predictable and simple form:
google:
key: 'your_google_key'
secret: 'your_google_secret'
Which then is being transformed to something like this:
GOOGLE_KEY=your_google_key
GOOGLE_SECRET=your_google_secret
The new version introduced one main change you'll have to apply manually. You have to add v2.
to both of your .key
and .credentials
files. This will allow in the future change encryption algo without breaking things.
Another thing that has changed is the default file format for .credentials
, it will always be YAML from now. JSON files will still work, but YAML is just way easier to format and work with.