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.jsconst 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