くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Express.js(Node.js)で認証(ハッシュ化/JWT)

Expressで認証周りについて調べたときの備忘録。

Passport.jsという便利なのもあるけど、
パスワードのハッシュ化やJWTトークン周りは自前で用意する必要がある感じ。
(ソーシャルは不要なので、使わない形の例)

※この例のCryptoのpbkdf2よりもBcryptのほうがアルゴリズムが強固なので、Bcryptのほうがよい

使い方

まずはインストール。CryptoはNode.jsのライブラリなので、インストール不要。

$ npm i jsonwebtoken

使い方はこんな感じ。

import { Request, Response, Router } from "express";
import crypto from "crypto";
import jwt from "jsonwebtoken";
import { User } from "./model"; // DBなどにアクセスするモデル


export type JwtPayload = {
  uid: number;
};

const JWT_SECRET_KEY = "YOUR_SECRET_KEY";
  
// ログイン: email/passwordを受け取り、JWTを返す
export async function login(req: Request, res: Response) {
  const { email, password } = req.body;
  
  // ユーザの取得
  const user = await User.findByEmail(email);
  if (!user) throw new Error("user not found");
  
  // ハッシュ化したパスワードを取得
  const salt = "...your_salt...";
  const iterations = 100000;
  const hashKey = crypto.pbkdf2Sync(password, salt, iterations, 64, "sha512");
  const hashedPass = hash.toString("base64");
  
  if (user.password != hash) throw new Error("user not found");

  // JWTの生成
  const payload: JwtPayload = {
    uid: user.id,
  };
  const option = {};
  const token = jwt.sign(payload, JWT_SECRET_KEY, option);
  return res.json({ token: token }).end();
}

// ログインユーザの情報取得
export async function me(req: Request, res: Response) {
  // JWTの検証: トークンからuidの取得
  const auth = req.headers.authorization?.split(" ");
  if (!auth || auth[0] != "JWT" || !auth[1]) throw new Error("JWT not found");

  const payload = jwt.verify(token, SECRET_KEY) as JwtPayload;
  if (!payload) throw new Error("user not found");

  // ユーザの取得
  const user = await User.findByUid(payload.uid);
  if (!user) throw new Error("user not found");
  
  return res.json({ user: { uid: user.id, username: user.username } });
}

Bcryptでのハッシュ化

まずはインストール。ライブラリはこれ。
kelektiv/node.bcrypt.js: bcrypt for NodeJs

$ npm i bcrypt

使い方はこんな感じ。

import bcrypt from "bcrypt";
// ハッシュ化したパスワードの取得
const password = "...YOUR_PASSOWRD...";
const saltRounds = 10;
const hashedPassword = bcrypt.hashSync(password, saltRounds);

// 検証
const plaintextPassword = "...YOUR_PASSOWRD...";
const user = "...";
const match:boolean = bcrypt.compareSync(plaintextPassword, user.passwordHash);

以上!!

参考にしたサイト様