LoginSignup
0
1

More than 3 years have passed since last update.

JSONトークンで認証ができるようにする

Posted at

JSONトークンでできること

ある操作が、他の人からはできないようにする
例)
「削除」ができるのは「作成者」のみ、に制限する

トークン生成のためのnpmパッケージをインストール

・「jsonwebtoken」https://www.npmjs.com/package/jsonwebtoken
というパッケージを利用

$ npm install jsonwebtoken

でインストール

・jsonトークンを作ってみる
<公式ドキュメントより>

使い方
jwt.sign(payload, secretOrPrivateKey, [options, callback])

(非同期) コールバックが与えられた場合は、 err または JWT でコールバックが呼び出されます。

(同期) JsonWebToken を文字列で返します。

(中略)

デフォルトの同期処理(HMAC SHA256)

var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');

const jwt = require('jsonwentoken')

const myFunction = async() => {
     const token = jwt.sign({_id: 'abc123'}, 'thisismykeyword') //第一引数:データベースの中の一意な値・第二引数:トークン作成の署名となる好きな文字列
     console.log(token) //作成したトークンを表示
}

myFunction() //jsonトークンを作成する同期処理を実行

jsonトークンが作成されていたら、console.logで確認できるはず

※jsonトークンの構成:base64でエンコードされている(BASE64でデコード可能)
※デコードすると、トークンの中の情報に、トークン化したid({_id: 'abc123'})と、トークン作成時点のタイムスタンプが含まれているのがわかる

トークンを認証に使う


const jwt = require('jsonwebtoken')

const myFunction = async() => {
     const token = jwt.sign({_id: 'abc123'}, 'thisismykeyword',{exporeIn: 'days'}) //第三引数:何日でトークンの効果が切れるか 
     console.log(token) 

     const data = jwt.veryfy(token, 'thisismykeyword')//tokenと、トークン作成の署名となる文字列とで認証
     console.log(data)
}

myFunction()

・トークン作成の署名となる文字列
・トークンの効果が切れる期間

でエラーを判定

トークンをアプリに組み込む

・アプリのファイル構成

└── src
    ├── index.js           //アプリのメイン処理を記述
    ├── models             //データベースのためのフィールド設定
    ├── middleware         //ミドルウェアを設定
    └── routers           //ルーティング処理

●models/user.js
データベースにtokenを保存するよう、データベースのスキーマを設定


const jwt = require('jsonwebtoken')
const userSchima = new mongoose.Schema({  //mongooseを用いてデータベースのスキーマを定義」
name: {
      type: String,  //データ型の設定
      required: true,
      trim: true
  },
  password:{
      type: String,
      required: true,
      trim: true,
      minlength: 7,
      validate(value){
        //toLowerCaseによって、小文字も大文字も小文字と認識される
        if(value.toLowerCase().includes('password')){
          throw new Error ('Do not use "password" to password')
        }
      }
  },
  tokens: [{   //tokensの項目を新たに設定
    token: {
      type: String,
      require: true
    }
  }],
})


//インスタンスにはメソッドを使って実装。generateAuthToken関数で、トークンを作成しDBへ保存
userSchema.methods.generateAuthToken = async function () {  //generateAuthToken関数を定義
  const user = this  //thisとは、userSchimaで定義したもの
  const token = jwt.sign({ _id: user._id.toString() }, 'thisismykeyword')

  user.tokens = user.tokens.concat({ token: token })//concatで、tokensの配列と、入力したtokenがuserのtokenと一致するものを結合する

  await user.save()//新しいuserをデータベースに保存

  return token //返り値にtoken変数を設定
}

//モデルにはクラスを使って実装。findbyCredentials関数で、Eメールとパスワードを元にDBを検索
userSchema.statics.findByCredentials = async (email, password) => {
  const user = await User.findOne({ email }) //emailの値が、引数のemailに一致

  if(!user){  //入力したEメールがデータベースにない
    throw new Error('Unable to login')
  }

  const isMatch = await bcrypt.compare(password, user.password)

  if(!isMatch){ //入力したパスワードがデータベースにない
    throw new Error('Unable to login')
  }

  return user
}

●routes/users.js
ルーティング処理時にJSONトークンを利用する

・JSONトークンの作成と保存
→user作成時の処理

router.post('/users', async (req, res) => {
  const user = new User(req.body)

  try {
      await user.save()

      const token = await user.generateAuthToken() //「models/user.js」で定義したgenerateAuthToken関数を実行
      res.status(201).send({user, token})
  } catch (e) {
      res.status(400).send(e)
  }
})

・JSONトークンを用いて認証する
→userログイン時の処理

router.post('/users/login', async (req, res) => {
    try {
        const user = await User.findByCredentials(req.body.email, req.body.password) //User.findByCredentials()はEメール、パスワードを引数にとり、userをemailによって検索し、パスワードをverifyする
        const token = await user.generateAuthToken()
        res.send({ user, token }) //ログインに成功した時の処理  
    }catch(e){
        res.status(400).send()
    }
})

ミドルウェアを使ってJSONトークン認証を設定する

・ファイル構成
└── src
    ├── index.js           //アプリのメイン処理を記述
    ├── models             //データベースのためのフィールド設定
    ├── middleware         //ミドルウェアを設定
    └── routers           //ルーティング処理

●「middleware/auth.js」
に、ミドルウェアとなるコードを記述しモジュールをエクスポート

・ミドルウェアの設定

●middleware/auth.js

const auth = async (req, res, next) => {
  console.log('認証ミドルウェアが動いています')
 next()
}

module.exports = auth

モジュールの雛形を作成

●routes/user.js
モジュールの読み込み

const auth = require('../middleware/auth') //ミドルウェアモジュールの読み込み


router.get('/users/me', auth, async (req, res) => { //第二引数にミドルウェア・第三引数にルーティング処理
    res.send(req.user)
})

GETリクエスト時に「認証ミドルウェアが動いています」とcosole.logされていればOK

●middleware/auth.js
・作成されたJSONトークンをコード内に取り込む
HTTPのヘッダーに送られているJSONトークン(key=Authorization)を読み込む

const auth = async(req, res, next) => {

     try{
          const token = req.header('Authorization')
          console.log(token)
     }catch(e){
          res.status(401).send({error: '認証してください'})
   }
}

●middleware/auth.js
認証させる


const jwt = require('jsonwebtoken')  //jwtモジュールの読み込み
const User = require('../models/user') //userデータベースの読み込み

const auth = async (req, res, next) => {  //HTTPヘッダーからJSONトークンの読み込み
  try {
    const token = req.header('Authorization').replace('Bearer ', '')
    const decoded = jwt.verify(token, process.env.JWT_SECRET) //jwtを作るのに使った秘密の文字列を用いてデコード
    const user = await User.findOne({ _id: decoded._id, 'tokens.token': token }) //userデータベースのidが、decodeしたJSONトークンの中に含まれるidと一致し、userデータベースのtokens配列の中のtokenと、tokenが一致

  if(!user){
    throw new Error()
  }

  req.token = token
  req.user = user
  next()

  } catch (e) {
    res.status(401).send({error: 'please authenticate'})
  }
}

module.exports = auth

0
1
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
0
1