I'm trying to create an API that will receive requests from users without having to do a login request. They will just have a user id and a key. My intention is that they will use the key to create a signature. And the server will recalculate the signature to check message integrity and that the correct key was used.
The code that the client would use to calculate the signature for the request would be something like this:
import crypto from "crypto";
const USER_ID = 35345;
// Key that will be used to sign the requests
const USER_KEY = crypto.randomBytes(32).toString("base64"); // This should be securely stored by the user
const requestParams = {
id: 158787,
amount: 24
};
// Using the timestamp will allow the server to decline old requests
const timestamp = Date.now();
const paramsInBase64 = Buffer.from(JSON.stringify(requestParams), "utf8").toString("base64");
// The key is scrypted here because server will not store the plain key, only the result of the scrypt
const scryptedKey = crypto.scryptSync(USER_KEY, String(USER_ID), 64).toString("base64");
// Combining the key with the timestamp to prevent it being always the same key for the hmac
const combinedKey = scryptedKey + timestamp;
const hashedKey = crypto.createHash("sha512").update(combinedKey).digest("base64");
const signature = crypto
.createHmac("sha512", hashedKey)
.update(USER_ID + paramsInBase64 + timestamp)
.digest("base64");
const request = {
params: paramsInBase64,
signature: signature,
user: USER_ID,
timestamp: timestamp
};
console.log("request :", request);
The server side would receive the request and obtain the scrypted key from the database using the user id and repeat the same process to check if the signature is correct.
Is this secure enough for an API, or am I overlooking something?
https://
\$\endgroup\$