サイトアイコン 上尾市のWEBプログラマーによるブログ

「入門Node.jsプログラミング 」の感想・備忘録11

「入門Node.jsプログラミング 」の感想・備忘録10の続き

Lesson28

APIトークンよる簡単なセキュリティの実装

const token = process.env.TOKEN || "recipeT0k3n";
// および以下のメソッドを追加
verifyToken: (req, res, next) => {
  if (req.body.apiToken === token) {
    next();
  } else {
    next(new Error("Invalid API token."));
  }
},
router.use(usersController.verifyToken);

// ルート定義の最後にAPI用エラー処理を追加
router.use((error, req, res, next) => {
  let errorObject;
  if (error) {
    errorObject = {
      status: 500,
      message: error.message
    };
  } else {
    errorObject = {
      status: 500,
      message: "Unknown Error."
    };
  }
  res.json(errorObject);
});

ユーザー毎のAPIトークンを生成する

const randToken = require("rand-token");

// createメソッドで以下のようにする。
newUser.apiToken = randToken.generate(32);

// verifyTokenメソッドで以下のようにする。
User.findOne({apiToken: req.body.apiToken}).then(user => {
  if (user) {
    next();
  } else {
    next(new Error("Invalid API token."));
  }
}).catch(error => {
  next(new Error(error.message));
});

JWTを使う

JWTトークン発行処理

let signedToken = jsonWebToken.sign(
  {
    data: user._id,
    exp: new Date().setDate(new Date().getDate() + 1)
  },
  "secret_encoding_passphrase"
);
res.json({
  success: true,
  token: signedToken
});

JWTトークンでの認証処理

const jsonWebToken = require("jsonwebtoken");
if (req.headers.token) {
  jsonWebToken.verify(req.headers.token), "secret_encoding_passphrase", (errors, payload) => {
    if (payload) {
      // payload.dataに格納されたユーザーIDと一致するユーザーを探す
    }
  }
}

実装例

router.post("/login", usersController.apiAuthenticate);
router.use(usersController.verifyJWT); // この行はログイン以外の経路より先に記述する
const jsonWebToken = require("jsonwebtoken");
// および以下のメソッド追加
apiAuthenticate: (req, res, next) => {
  passport.authenticate("local", (errors, user) => {
    if (user) {
      let signedToken = jsonWebToken.sign(
        {
          data: user._id,
          exp: new Date().setDate(new Date().getDate() + 1)
        },
        "secret_encoding_passphrase"
      );
      res.json({
        success: true,
        token: signedToken
      });
    } else
      res.json({
        success: false,
        message: "Could not authenticate user."
      });
  })(req, res, next);
},
verifyJWT: (req, res, next) => {
  if (req.headers.token) {
    jsonWebToken.verify(req.headers.token, "secret_encoding_passphrase", (errors, payload) => {
      if (payload) {
        User.findById(payload.data).then(user => {
          if (user) {
            next();
          } else {
            res.status(httpStatus.FORBIDDEN).json({
              error: true,
              message: "No User account found."
            });
          }
        });
      } else {
        res.status(httpStatus.UNAUTHORIZED).json({
          error: true,
          message: "Cannot verify API token."
        });
        next();
      }
    });
  } else {
    res.status(httpStatus.UNAUTHORIZED).json({
      error: true,
      message: "Provide Token"
    });
  }
}
モバイルバージョンを終了