18
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Node(Express)でJWT

Last updated at Posted at 2019-11-19

はじめに

ユーザー認証を実装したい。
スマホアプリ向けのAPI作っているときにユーザー認証が必要となり、JWTが良さそうだったので採用した。

以下、注意点

  • 本記事の目的はあくまでJWTの導入なので、脆弱性については触れていない。
  • 入門者にわかりやすくするため、有識者には誤解される表現を使っている可能性がある。

JWTとは

「Json Web Token」の略で、URLに埋め込めるJSON文字列のこと。
電子署名によってJSONが改ざんされていないかをチェックできるため、ユーザー認証などに使うことができる。
筆者は詳しく理解してないので、JWT使えばログイン機能作れる!、みたいに思っている。

JWTのしくみ

  1. クライアントは、認証情報(ユーザーIDとパスワード)をサーバに送る(いわゆるログイン処理)
  2. サーバは、データベースに接続して認証情報が正しいか確認
  3. サーバは、ユーザーIDと有効期限といった情報を持つJSONを秘密鍵で暗号化し、JWTの形でクライアントに返す
  4. サーバは、ユーザーIDと有効期限といった情報を持つJSONに秘密鍵で署名し、JWTの形でクライアントに返す
  5. 以降、クライアントは、渡されたJWTを使ってサーバと通信する(いわゆるログイン状態)

JWTの有効期限が切れたら、再び1に戻って新しくJWTを発行する必要がある。

コード

command
npm install jsonwebtoken
index.js
var express = require("express");
var app = express();
var jwt = require("jsonwebtoken");

// 秘密鍵設定、本当は環境変数使うべき
app.set("superSecret", hogehoge);

var apiRoutes = express.Router();

// ユーザー情報、本当はデータベースにあるのをとってくるべき
var users = [
  {id: "user0", pass: "huga0"},
  {id: "user1", pass: "huga1"},
  {id: "user2", pass: "huga2"}
];

// クライアントから送られたIDとパスワード確認してtoken(jwt)発行
// "/api/authenticate"
apiRoutes.post("/authenticate", (req, res) =>{
  var post_id = req.body.postId;
  var post_pass = req.body.postPass;
  for (var i = 0; i < users.length; i++) {
    // IDとパスワードが正しかったらtoken発行
    if (post_id == users[i].id && post_pass == users[i].pass) {
      var token = jwt.sign(user_id, app.get("superSecret"), {algorithm: HS256, expiresIn: 120});
      res.json({
        success: true,
        msg: "Authentication successfully finished",
        token: token
      });
      return;
    }
  }
  // IDとパスワードが正しくなかった場合
  res.json({
    success: false,
    msg: "Authentication failed"
  });
});

// 一度クライアントに返したtokenが改ざんされずにクライアントから送られてきたか確認
apiRoutes.use((req, res, next) => {
  var token = req.body.token;
  // tokenがない場合、アクセスを拒否
  if (!token) {
    return res.status(403).send({
      success: false,
      msg: "No token provided"
    });
  }
  // tokenが改ざんされていないかチェック
  jwt.verify(token, app.get(superSecret), (err, decoded) => {
    // tokenが不正なものだった場合、アクセス拒否
    if (err) {
      console.log(err);
      return res.json({
        success: false,
        msg: "Invalid token"
      });
    }
    // 正しいtokenの場合、認証OKする
    req.decoded;
    next();
  });
});

// 認証後、これ以降のURIにアクセス可能となる
// "/api/private"
apiRoutes.get("/private", (req, res) => {
  res.json({
    msg: "Hello world!"
  });
});

app.use("/api", apiRoutes);

解説

jwt.sing(user_id, app.get("superSecret"), {algorithm: HS256, expiresIn: 120});
  • アルゴリズム
    • algorithm: HS256 デフォルト値がこれになっており、記述しなくても問題ない
  • 有効期限
    • expiresIn: 120 120ミリ秒
    • expiresIn: 3h 3時間
    • expiresIn: 2days 2日
apiRoutes.post("/authenticate", (req, res) => {...});
apiRoutes.get("/private", (req, res) => {...});

アクセスする際のURIは、"/authenticate""/private"ではなく、
/api/authenticate"/api/private"となる。
理由は、コードの一番下でapp.use("/api", apiRoutes);としているから。

さいごに

脆弱性については今後別の記事でまとめる予定。
間違いなどあれば是非コメントにお願いします。

参考サイト

18
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?