How do I generate a session ID in Node.js? - node.js

Im trying to learn Node.js without using any third party modules or frameworks. I'm getting to the point where I got to figure out how to give session ids to users who login...
So far I know I can set the session id by writing it in the headers, setting the cookies:
writeHead(200, {'set-cookie':'Math.random() ' } );
and then I can retrieve the session id and later compare it to the database.
request.headers.cookie(request.url);
But how do I GENERATE the session ID value? I am new to programming. The first thing I thought of was using Javascript's Math.random(); and using that value to set the cookie ( session id ). In my mind it feels stupid but that is how far I can think.
How am I suppose to generate the session id using Node.js, no third party modules, barebones please!

Note: You should probably use a session manager for whatever framework you go with.. be it connect, express, koa or any number of others.
This will give you a UUID version 4 (random) using crypto.randomBytes.
var crypto = require('crypto');
module.exports = genUuid;
function genUuid(callback) {
if (typeof(callback) !== 'function') {
return uuidFromBytes(crypto.randomBytes(16));
}
crypto.randomBytes(16, function(err, rnd) {
if (err) return callback(err);
callback(null, uuidFromBytes(rnd));
});
}
function uuidFromBytes(rnd) {
rnd[6] = (rnd[6] & 0x0f) | 0x40;
rnd[8] = (rnd[8] & 0x3f) | 0x80;
rnd = rnd.toString('hex').match(/(.{8})(.{4})(.{4})(.{4})(.{12})/);
rnd.shift();
return rnd.join('-');
}
You could also use the UUID module from npm. The crypto package is not an in-browser option, though you could use Browserify's crypto shim.

Related

google-spreadsheet.js (npm) read only access to cell not working with API KEY - need OAuth?

From a node.js application (a discord bot)
I try to acess to a public googlesheet using the npm package google-spreadsheet
I followed each step carefully, but I would like to use only the API key authentification method instead of a more risky Oauth identification
(my discord bot is public, on heroku and I don't want to mess around with too much sensitive information even though i use environment variables)
On the documentation of google-spreadsheet.js it mentions that :
// OR use API key -- only for read-only access to public sheets
doc.useApiKey('YOUR-API-KEY');
I sucessfully could connect to the
spreadsheet
and read the title of it and get the titles of each sheet but when I call
await sheet.loadCells();
it gives me the following error
Google API error - [401]
Request is missing required authentication credential.
Expected OAuth 2 access token,
login cookie or other valid authentication credential.
See https://developers.google.com/identity/sign-in/web/devconsole-project.
What would be the right way or READING ONLY cells, if possible using only the API KEY authentification ?
here is my full code :
const sheetId = "1Bny-ZsCG_oUuS0nTbR-7tBBZu47_ncS9qGYaMpuprWU"
var loaded = {}
if (message) {
message.reply("je me connecte à Google Sheets...")
}
const doc = new GoogleSpreadsheet(sheetId);
doc.useApiKey(process.env.GOOGLE_API_KEY);
await doc.loadInfo();
loaded.docTitle = doc.title;
loaded.sheets = {};
if (message) {
message.reply("...connection réussie, je récupère les infos...")
}
// get the spreadsheets
for (let s = 0; s < doc.sheetCount; ++s ) {
const sheet = doc.sheetsByIndex[s];
loaded.sheets[sheet.title] = {sheetReference:sheet};
loaded.sheets[sheet.title].data = []
await sheet.loadCells(); // <---------it seems to block here
for (let row= 0; row < sheet.rowCount; ++row) {
loaded.sheets[sheet.title].data.push([])
for (let col = 0; col < sheet.columnCount; ++col) {
let cell = sheet.getCell(row, col).value;
loaded.sheets[sheet.title].data[row].push(cell)
}
}
Thank you very much !
You want to retrieve the values from Google Spreadsheet using the API key.
The Google Spreadsheet is publicly shared.
You want to achieve this using google-spreadsheet.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
When I saw the source script of google-spreadsheet, it seems that sheet.loadCells() requests with the POST method using the API key. Ref Unfortunately, the API key cannot use the POST method. So such error occurred. I think that the reason of this issue is due to this. For example, when the access token from OAuth2 and service account is used, I could confirm that sheet.loadCells() worked. From this situation, this might be a bug or the specification of the library.
Fortunately, the values can be retrieved from the publicly shared Google Spreadsheet with the API key. So as one of several workarounds, in this answer, googleapis for Node.js is used as a simple method. This is the official library.
Sample script:
At first, please install googleapis. And please set the variables of spreadsheetId and APIKey.
const { google } = require("googleapis");
const spreadsheetId = "1Bny-ZsCG_oUuS0nTbR-7tBBZu47_ncS9qGYaMpuprWU"; // This is from your script.
const APIKey = "### your API key ###";
const sheets = google.sheets({version: "v4", auth: APIKey});
sheets.spreadsheets.get({ spreadsheetId: spreadsheetId }, (err, res) => {
if (err) {
console.error(err);
return;
}
sheets.spreadsheets.values.batchGet(
{
spreadsheetId: spreadsheetId,
ranges: res.data.sheets.map(e => e.properties.title)
},
(err, res) => {
if (err) {
console.error(err);
return;
}
console.log(JSON.stringify(res.data));
}
);
});
When you run the script, the all values from all sheets in the publicly shared Spreadsheet are retrieved.
In above sample script, there are 2 methods of spreadsheets.get and spreadsheets.values.batchGet were used.
References:
google-api-nodejs-client
Method: spreadsheets.get
Method: spreadsheets.values.batchGet
If I misunderstood your question and this was not the direction you want, I apologize.
Author of google-spreadsheet here.
I've just released an update that should fix this problem. It was a very subtle difference in google's API docs that I missed. The loadCells method now will default to the regular get endpoint if using an API key only. The interface for loadCells is the same, but will only support A1 ranges in this mode.
Cheers!

Azure keyvault, request for multiple secrets

Im making use of the following node library azure-keyvault to get retrieve stored secrets from azure keyvault. Ive only found the client.getSecret api exposed to retrieve a secret value. Im searching for a way to retrieve multiple secret values in one call. I hav'nt found one yet. Is there a way to do this that i'm missing or its simply not supported.
const { SecretClient } = require('#azure/keyvault-secrets')
const client = new SecretClient(
`https://${KEYVAULT_NAME}.vault.azure.net`,
new DefaultAzureCredential()
)
const [secret1, secret2] = await Promise.all([
client.getSecret(`secret1`),
client.getSecret(`secret2`)
])
Here is the complete code for getting the multiple client secret at once:
var credentials = new KeyVault.KeyVaultCredentials(authenticator);
var client = new KeyVault.KeyVaultClient(credentials);
client.setSecret(vaultUri, 'mysecret', 'my password', options, function (err, secretBundle) {
// List all secrets
var parsedId = KeyVault.parseSecretIdentifier(secretBundle.id);
client.getSecrets(parsedId.vault, parsedId.name, function (err, result) {
if (err) throw err;
var loop = function (nextLink) {
if (nextLink !== null && nextLink !== undefined) {
client.getSecretsNext(nextLink, function (err, res) {
console.log(res);
loop(res.nextLink);
});
}
};
console.log(result);
loop(result.nextLink);
});
});
You can find the complete reference for azure key vault using node js below:
http://azure.github.io/azure-sdk-for-node/azure-keyvault/latest/KeyVaultClient.html#getSecrets
http://azure.github.io/azure-sdk-for-node/azure-keyvault/latest/
Hope it helps.
You can use read-azure-secrets npm package which will return all secrets to you.
E.g.
const secretClient = require('read-azure-secrets');
async function loadKeyVaultValues() {
let applicationID = '';
let applicationSecret = '';
let vaultURL = 'https://<your-key-vault-name>.vault.azure.net/';
let secrets = await secretClient.getSecrets(applicationID, applicationSecret, vaultURL);
secrets.forEach(secret => {
console.log(secret);
});
}
loadKeyVaultValues();
You can try using client.getSecrets(..) method exposed by the REST Api.
Kindly go through the following useful blog, in which all methods have been implemented.
LINK: https://www.red-gate.com/simple-talk/cloud/platform-as-a-service/using-azure-keyvault-with-node-js/
You haven't specified what information about the secret you want to fetch so I am going to assume that you are looking for the secret's value. I am also going to assume you are looking to minimize network traffic for fetching multiple secrets (either for costs or for performance).
Looking at the Azure REST API documentation while there is a route to list multiple secrets it only provides the secret identifier and metadata about the secret (attributes, tags, etc). So if you want to get the secret's value (the actual secret) you will need to make individual calls although get-secrets route can be used to find all the secrets stored in the Key Vault.
As far as the client library, #azure/keyvault-secrets maps pretty closely to the REST API it supports so it will not provide a method that fetches multiple secrets. Even if it did, it would just be a facade over multiple network calls so it would not help reduce the number of network trips.
So to answer your question - it does not look possible today unless all you want is metadata about the secret and not the secret value itself.

Node.js and mongoose - prevent salt and password to return to client

I have a node.js app with mongoose. my problem is that when returning entities with populated user, the user data includes secure data like salt, tokens and password. I used the following method to prevent private data from going back to client:
User.methods.toJSON = function() { // Do not change to lambda expression
const SERVER_ONLY_PROPERTIES = ['tokens', 'salt', 'password'];
var user = this.toObject();
SERVER_ONLY_PROPERTIES.forEach(propKey => {delete user[propKey]});
return user;
}
Everything worked great, until I used the lean() function in my code.
when using lean the toJson method isn't being called and the private data returns to client. what are my options?
Instead of taking out the stuff that you don't want to return, you should instead have a process that builds a new object to return that explicitly includes only the necessary pieces of data. That's the best practice as far as security, if I'm not mistaken.

generate random unique token for user id

I want to generate token as user id and store in database , but how to generate unique one?
should I add timestamp var currentUnixTimestamp = (new Date().getTime() / 1000); as salt? how to do with crypto?
var generateToken = function() {
return new Promise(function (fulfill, reject){
crypto.randomBytes(8, function(error, buf) {
if (error) {
reject(error);
} else {
var token = buf.toString('hex');
fulfill(token);
}
});
});
};
Eight random bytes from a properly seeded crypto library has a low chance of a collision, so you don't usually need to concern yourself with duplicates. In fact, increase that to 16 bytes, and your code is on par with UUID version 4. This is considered a standard for UUIDs. The chances of a collision are so remote it is not usually worth considering.
If you are going that far though, consider using a standard format UUID, such as the node package "uuid". There are also database-side uuid functions which you can add as default to schemas e.g. in Postgres. The advantage is a standardised and well-understood format for your ids, and you won't need to spend any time justifying or maintaining your code for this, just point developers to the standard docs.
If you want this token for authentication purposes you should use json web token instead. It will manage for you and its quite efficient.
Only have to include as a middleware .
app.use(expressJWT({
secret: new Buffer("Your-secret-key").toString('base64')
}).unless({
//# pass api without validating
path: unlessRoutes
}));
You could specify which routes you don't want to to skip in jwt middleware by giving an array in unlessRoutes.
var unlessRoutes = [
'/',
/\/login/,
/\/register/,
/\/customers/,
/\/customer$/,
/\/addCustomer/,
/\/just/,
/\/search/,
/\/dynamic/,
/\/favicon.ico/
]
This is what i think we can do for generating the random token using the crypto:
var passwordResetToken = createRandomToken(data.body.email);
exports.createRandomToken = function (string) {
var seed = crypto.randomBytes(20);
return crypto.createHash('abcde').update(seed + string).digest('hex');
};

Creating a JSON file(MongoDb Collection) of User Info for a Node.js application that uses Bcrypt and passport for login

I'll try and word this question as short and clear as possible. I have a node.js project that connects to a non local instance of MongoDB using mongoose. I have written code for a user to login using passport module. I create a JSON file as a users collection. When I go to sign in I get this error
throw "Not a valid BCrypt hash."
So If I wanted to just create a JSON file of Users to work with this how would I go about it? My end goal is to convert RDBMS to MongoDB. Just starting with the users table. I understand that if I was registering new users I could just make a function that would hash their password like
newUser.password= createHash(password)
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
or something along those lines. Summary: How do I make a JSON file of user info for MongoDB that'll work with bcrypt hash and passport?
var fs = require('fs');
var bc = require('bcrypt-nodejs')
fs.readFile('users.txt',function(err,data){
var x = [];
if(err) throw err;
var buf = new Buffer(data);
x = buf.toString();
x = JSON.parse(x);
//console.log(x);
for(var a in x){
x[a].password=createHash(x[a].password);
}
x = JSON.stringify(x);
fs.writeFile('users.txt',x,callback()); //or just push directly to db?
});
var createHash = function(password){
return bc.hashSync(password, bc.genSaltSync(10), null);
}
function callback(){
console.log('finished');
}
Just read in my User Info , Bcrypted the passwords and made a new file. Put it in Mongo. Hopefully this helps someone else. If anyone has a better solution for me I'd appreciate it.

Resources