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

##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');


```javascript
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](https://www.base64decode.org/)でデコード可能）
※デコードすると、トークンの中の情報に、トークン化したid({_id: 'abc123'})と、トークン作成時点のタイムスタンプが含まれているのがわかる

##トークンを認証に使う
```javascript

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を保存するよう、データベースのスキーマを設定

```javascript

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作成時の処理

```javascript
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ログイン時の処理

```javascript
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トークン認証を設定する

<h6>・ファイル構成</h6>

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


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

<h6>・ミドルウェアの設定</h6>
●middleware/auth.js

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

module.exports = auth

```

モジュールの雛形を作成

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

```javascript
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）を読み込む

```javascript
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
認証させる


```javascript

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

```