【Node.js】ユーザーアカウント登録→ログインを実装する

投稿者:

前提

環境はこの人です。
Express, express-session, Sequelize が入っている前提で進めます。

https://github.com/mayarin/NodejsMySQLDocker

作業内容

以下のテーブルを作りました。

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `mailaddress` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `password` text COLLATE utf8mb4_bin,
  `active` tinyint(1) DEFAULT NULL,
  `createdAt` datetime NOT NULL,
  `updatedAt` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

アカウント登録時には
・既存メルアド確認
・アカウント作成後自動ログイン
とします。

既存メルアド確認には、SequelizeのfindAndCountAllを利用します。

  const { count, rows } = await db.Users.findAndCountAll({
    where: {
      mailaddress: req.body.mailaddress
    },
    offset: 0,
    limit: 1
  });
  if(count == 0){
    // 行がない場合のみアカウント登録処理
  } else {
    // アカウントが重複している旨エラーを返す
  }

アカウント登録の実作業は、Sequelizeのcreateを利用します。

  let hashed_password = bcrypt.hashSync(req.body.password, 10);
  // パスワードを平文で持たず暗号化します

  const newUser = await db.Users.create({
      name: req.body.name,
      mailaddress: req.body.mailaddress,
      password : hashed_password,
      active: true
    });
    if (newUser){
      req.session.user = {id: newUser.id};
      signup_failed_reason = '';
    } else {
      signup_failed_reason = 'アカウント登録に失敗しました。';
    }

パスワードの暗号化にはbcryptを利用しました。

https://www.npmjs.com/package/bcrypt

https://qiita.com/tatsumi44/items/83ac5c18f213e22ed322

パスワード照合でハマりました

一方ログイン時にパスワードを照合するところでハマりました。

router.post('/login', async function(req, res) {

  login_mailaddress = req.body.mailaddress;
  let hashed_password = bcrypt.hashSync(req.body.password, 10);

  const { count, rows } = await db.Users.findAndCountAll({
    where: {
      mailaddress: req.body.mailaddress,
      password: hashed_password,
    },
    offset: 0,
    limit: 1
  });

いつもはCI4では以下の様に書いていて(多分めちゃくちゃ自己流)。

$password_query = sprintf("select HEX(AES_ENCRYPT('%s', '%s')) as enc_password", $this->request->getVar('update_password'), LOGIN_ENCRYPT_KEY);
$enc_password = $this->db->query($password_query)->getRowArray()['enc_password'];
// こいつを作ってアカウント検索時のwhere句に入れる

これと同じノリで照合しようと思ったんですが、生成するたびに違う暗号文が吐き出されるんです。
これbcryptでは考え方がそもそも誤っていて、compareSyncで照合するんです。
そうなると、メルアドの有無確認→パスワードの照合という流れになります。
完成品はこうなりました。

router.post('/login', async function(req, res) {

  login_mailaddress = req.body.mailaddress;

  const { count, rows } = await db.Users.findAndCountAll({
    raw: true,
    where: {
      mailaddress: req.body.mailaddress,
    },
    offset: 0,
    limit: 1
  });
  if(count == 0){
    login_failed_reason = 'アカウントがありません。';
    res.redirect('/');
  } else {
    var row = rows[0];
    if (bcrypt.compareSync(req.body.password, row.password) ) {
      req.session.user = {id: row.id};
    } else {
      login_failed_reason = 'パスワードが誤っています。';
    }
    res.redirect('/');
  }
});

参考サイト

https://qiita.com/tatsumi44/items/83ac5c18f213e22ed322

なおこの段階のgithubはこちらになります。

https://github.com/mayarin/NodejsMySQLDocker/releases/tag/20201102

コメントを残す