Jonathan Wexler/吉川 邦夫 翔泳社 2019年09月25日頃
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トークンを生成する
- rand-tokenパッケージでトークンを簡単に生成することができる。
npm install rand-token
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とはログイン時度に生成されるAPIトークンのようなもので、セッションの代わりとして使うことができる。
- 以下の3つの要素から成る。
- ヘッダ:ハッシュ方式を示すJSONオブジェクト
- ペイロード:格納するデータ(通常はユーザーID)
- 署名:ヘッダとペイロードの値から生成されるハッシュ
- ログイン成功時にサーバはエンコードされたJWTをレスポンスに含める。
その後のリクエストにJWTを付与、サーバでJWTをデコードしてユーザーを確認する。
- jsonwebtokenパッケージのインストール
npm install jsonwebtoken
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"
});
}
}
コメント