I have used a lot of third-party APIs for the last four years. Most of them provided keys and secrets (API key/Client ID, API/Client secret) to access the API. I was also planning to develop some services where I would provide APIs. So, I was wondering how to generate API keys and API secrets for my services.
I started researching and read a lot of articles and StackOverflow questions regarding this topic. I found out that there are some common rules. I will try to summarize what I have learned about generating API keys and secrets in this post.
API Key or Client ID
An API key or Client ID is a unique key to identify a user. Like a username or email, it must be unique. It should be unguessable too. We don’t have to encrypt it. We can generate an API key by using the crypto
package from Node.js. You can use the following code to generate an API key.
const { randomBytes } = require('crypto');
function generateKey(size = 32, format = 'base64') {
const buffer = crypto.randomBytes(size);
return buffer.toString(format);
}
console.log(generateKey());
// will generate something like:
// 0NY4IrzHgLnRZUCWpxSnXLpn+Qjb1YfEj0UmnQaJiTw=
API Secret or Client Secret
An API/Client secret is a secure key to provide secure access to an API. It must be unique and unguessable. We must store it in hashed form because it’s just like a password. It is a good practice to use different secret keys for different scopes in APIs. For example, we can use a key to Create
and Update
articles and another key to Read
user list and details.
We can generate a random unique key using our previous generateKey
method and hash the result using scryptSync
from the crypto
package. We will send the unhashed key to users and asked them to store the key safely. One of the shortcomings of this approach is that the user won’t see the key again if the user forgets the key. You can use the following code to generate a hashed version of the key.
const { scryptSync, randomBytes, timingSafeEqual } = require('crypto');
function generateSecretHash(key) {
const salt = randomBytes(8).toString('hex');
const buffer = scryptSync(key, salt, 64) as Buffer;
return `${buffer.toString('hex')}.${salt}`;
}
// used the previous function
const key = generateKey(); // send to user: Jj0fmQUis7xKJ6oge4r1fN4em7xJ+hILrgubKlG6PLA=
const secretHash = generateSecretHash(key); // save in db: c10c7e79fc496144ee245d9dcbe52d9d3910c2a514af1cfe8afda9ea655815efed5bd2a793b31bf923fe47d212bab7896cd527c720849678077e34cdd6fec0a2.2f717b397644fdcc
We can use the following code to compare and validate an API secret.
function compareKeys(storedKey, suppliedKey) {
const [hashedPassword, salt] = storedKey.split('.');
const buffer = scryptSync(suppliedKey, salt, 64) as Buffer;
return timingSafeEqual(Buffer.from(hashedPassword, 'hex'), buffer);
}
I hope you’ve got a general guideline about generating API keys and secrets. To know more about this, you can follow the following links:
Resources:
- The Client ID and Secret
- How services generate and use public and secret API keys?
- What’s the best approach for generating a new API key?
- Do I need to hash or encrypt API keys before storing them in a database?
- Need advices on API key & secret generation?
- Best practices for building secure API Keys
- Crypto