0

I was curious when I been using Django and found that the user data and authentication credentials were in different tables. Unfortunately I never understood how this worked, but I imagine through relationships.

My database is in MySQL and I have created the users table with the user data, then I have created another table called auth with its respective field, id, salt and hash which is what I am interested in saving. I have also created a field called user_id to relate to the user in the users table with the id (I don't know if it's fine like that).

Now, I have a function in my NodeJS code that is responsible for saving the data received by the network layer:

function insert (table, data) {
  return new Promise((resolve, reject) => {
    connection.query(`INSERT INTO ${table} SET ?`, data, (err, data) => {
      if (err) return reject(err)
      resolve(data)
    })
  })
}

has that structure so that it is reusable at all times.

On the other hand I have the controller that handles the business logic of the data received by the network layer:

function insert (user) {
     if (!user) {
          throw dataMissed
      }

        bcrypt.genSalt(1000, function(err, salt) {
            if (err) throw err
            bcrypt.hash(user.password, salt, function (err, hash) {
                if (err) throw err

                user = {
                    name: user.name,
                    email: user.email,
                    position: user.position,
                    hash,
                    salt
                }
                return store.insert(collection, user)
            })
        })
    }

Unlike the function that is dedicated to saving the data in the database, this one is unique since the logic will depend on the data, obviously.

And the problem I have is that I don't know how to save the generated salt and the hash in the corresponding table. As you can see, I use the same function to save both but it gives me an error, since it is a logic that does not work.

How do I save a value that will later be referenced by another table?

Diesan Romero
  • 1,292
  • 4
  • 20
  • 45

1 Answers1

1

I think the problem is that your 2nd insert function expect user and it also expects user to have password when in reality user should not have password. You need to have 2 DALs, one to control user table and the other to control the credentials Then in your service layer you do your logic using waterfall since credentials DAL depends on user's id

Here is pseudo code of what I mean

Userdal.js

/*
 * User should look something like 
 * {
 *   name: "xyz",
 *   age: 40,
 *   email: "test@test.com"
 *   etc...
 * } 
 * Basically it has non sensitive information nothing has to do with credentials.
 */
function insert(user) {
 /// does insert logic
 return store.insert(collection, user) //or resolve, whatever works  for you
}

CredentialsDal.js

/*
 * creds should look something like
 * {
 *   username: "aaaa",
 *   password: "asfa",
 *   userId: "1234567"
 * }
*/
function insert(creds) {
    // Do your logic here for creds, for example
    bcrypt.genSalt(1000, function(err, salt) {
       if (err) throw err
       bcrypt.hash(creds.password, salt, function (err, hash) {
           if (err) throw err
           const sanitizedCreds = {
                username: creds.username,
                salt: salt,
                hash: hash
                // Literally anything you can credential goes here.
           }
           return store.insert(collection, sanitizedCreds)
       })
    })
}

UserService.js

// This is a nice library this gives things such as waterfall, series, etc... (you dont need to use it though)
const asyncx = require('async')
const credsDals = require('./dals/CredentialsDal')
const userDal = require('./dals/UserDal')

function register(userData, credsData) {
    asyncx.waterfall([
       // Create user
       (callback) => {
          let user = userDal.insert(userData)
          callback(null, user.id)
       },
      
       // Create credentials
       (userId, callback) => {
          let creds = credsDal.insert(credsData)
          callback(null, true)
       }   
    ], (err, res) => {
       // Handle your result here
    })

}

Controller.js

const UserService = require('./Services/UserService')
function register(req, res) {
    const userData = req.body.user
    const credsData = req.body.credentials

    UserService.register(userData, credsData)

    res.status(200).end()
}

finally your router

const controllers = require('./controllers')
router.post('/user', controllers.register)

The above code is missing some pieces I left for you to fill, such as actual logic implementation, callbacks and returns, promises, etc...

Note: such separation is really important and nice to have since it create a secure code. whenever you request a specific user you don't want to send all of their sensitive information with the response. For example, imagine you tap on your friend's user profile on facebook and all of the sudden that facebook frontend request will bring back even the user's credentials even if they are not rendered they are still being requested and can be sniffed! so these sensitive information are stored on a separate table if not separate server all together. In cases such as credit cards, etc... they have different servers that can only accept requests from the same network and bans all other requests, this way it makes it harder for anyone to make any request to those servers, basically you have to be inside the server's network to make that request.

Link to repl.it: https://repl.it/repls/BlindCuddlyUserinterface#index.js

Muhand Jumah
  • 1,726
  • 1
  • 11
  • 26
  • Thank you very much, I haven't tried it yet but I guess it will work. That async library looks interesting and is just what I was looking for to make several queries and keep the same structure. I was only thinking of saving the hash and salt, but it seems that I will also have to save the user. But I still don't know how to carry out the 1 - 1 relationship. What references should I make? – Diesan Romero Oct 24 '20 at 04:41
  • I think https://medium.com/@emekadc/how-to-implement-one-to-one-one-to-many-and-many-to-many-relationships-when-designing-a-database-9da2de684710 this article describes it better than I would ^^ but basically having a FK to the PK of the user table should do the trick just fine. A user doesn't need to reference the creds ID but the Creds needs to know who do they belong to. If however, you want them to reference each other then you would create a 3rd table that would have 2 FKs one for user and one for creds, or if you want a user to have multiple creds then you would also go this route. – Muhand Jumah Oct 24 '20 at 04:45
  • And yup that `async` library is very powerful library. It serves many purposes, check it out here: https://www.npmjs.com/package/async – Muhand Jumah Oct 24 '20 at 04:48
  • Thank you to much, let me implement the solution and I will let you know. – Diesan Romero Oct 24 '20 at 04:49
  • No problem, good luck and yes do let me know if there are any issues I will be happy to explore it further with you. – Muhand Jumah Oct 24 '20 at 04:50
  • Problem trying to install async > npm ERR! code ENOENT > npm ERR! syscall rename > npm ERR! path K:\blog\node_modules\async > npm ERR! dest K:\blog\node_modules\.async.DELETE > npm ERR! errno -4058 > npm ERR! enoent ENOENT: no such file or directory, rename > 'K:\blog\node_modules\async' -> 'K:\blog\node_modules\.async.DELETE' > npm ERR! enoent This is related to npm not being able to find a file. > npm ERR! enoent I did't with the npm command I seem in npmjs.com/package/async – Diesan Romero Oct 24 '20 at 05:05
  • This is a weird problem with NPM, see if this help: https://stackoverflow.com/questions/12594541/npm-global-install-cannot-find-module – Muhand Jumah Oct 24 '20 at 05:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/223547/discussion-between-diesan-romero-and-muhand-jumah). – Diesan Romero Oct 24 '20 at 10:52