FullStackOpen Part4-d Token Authentication メモ

トークン認証は以下のような仕組み


Token-based authentication diagram

トークン機能を実装するためにjsonwebtokenを使用する
npm install jsonwebtoken

login用のエンドポイントを用意する。
./controller/login.js

const jwt = require('jsonwebtoken')
const bcrypt = require('bcrypt')
const loginRouter = require('express').Router()
const User = require('../models/user')

loginRouter.post('/', async (request, response) => {
    const { username, password } = request.body

    const user = await User.findOne({ username })
    const passwordCorrect = user === null
        ? false
        : await bcrypt.compare(password, user.passwordHash)

    if (!(user && passwordCorrect)) {
        return response.status(401).json({
            error: 'Invalid username or password'
        })
    }

    const userForToken = {
        username: user.username,
        id: user._id,
    }

    const token = jwt.sign(userForToken, process.env.SECRET)

    response
        .status(200)
        .send({ token, username: user.username, name: user.name })
})

module.exports = loginRouter

.envファイルにSECRET環境変数を設定する

ルーターをapp.jsに入れると機能する

Limiting creating new notes to logged-in users

ノート作成をログイン済みユーザーのみにする場合はAuthorizationヘッダを使用する
今回はベアラー認証を使用し、Authorizationヘッダは以下のような形に

Bearer eyJhbGciOiJIUzI1NiIsInR5c2VybmFtZSI6Im1sdXVra2FpIiwiaW

notes.jsを以下のように変更し、トークンによってポストできるかどうか決める
getTokenFromヘルパー関数を設定しておく

const getTokenFrom = request => {
    const authorization = request.get('authorization')
    if (authorization && authorization.startsWith('Bearer ')) {
        return authorization.replace('Bearer ', '')
    }
    return null
}

notesRouter.post('/', async (request, response, next) => {
    const body = request.body
    const decodedToken = jwt.verify(getTokenFrom(request), process.env.SECRET)
    if (!decodedToken.id) {
        return response.status(401).json({ error: 'Invalid token!' })
    }
    const user = await User.findById(decodedToken.id)

    const note = new Note({
        content: body.content,
        important: body.important || false,
        user: user.id
    })

    const savedNote = await note.save()
    user.notes = user.notes.concat(savedNote._id)
    await user.save()

    response.status(201).json(savedNote)
})

エラーハンドラー(./utils/middleware.jsで無効なトークンだった時の処理'JsonWebTokenError'を施す

const errorHandler = (error, request, response, next) => {
  logger.error(error.message)

  if (error.name === 'CastError') {
    return response.status(400).send({ error: 'malformatted id' })
  } else if (error.name === 'ValidationError') {
    return response.status(400).json({ error: error.message })
  } else if (error.name ===  'JsonWebTokenError') {    return response.status(400).json({ error: error.message })  }

  next(error)
}

Postmanからリクエストする際はヘッダにAuthorizationを指定する

HeaderにAuthorizationキーを追加してBearerキーを指定する

Problems of Token-based authentication

現在の設定だとトークンが無期限で使えてしまい危ない
トークンの有効期限をlogin.jsで設定する

...
const userForToken = {
        username: user.username,
        id: user._id,
}

//Token expires in 60*60 seconds
const token = jwt.sign(userForToken, process.env.SECRET, {
        expiresIn: 60 * 60
})

response
    .status(200)
    .send({ token, username: user.username, name: user.name })
...

middleware.jsにトークン失効エラーを記述

const errorHandler = (error, request, response, next) => {
    logger.error(error.message)

    if (error.name === 'CastError') {
        return response.status(400).send({ error: 'Malformatted id' })
    } else if (error.name === 'ValidationError') {
        return response.status(400).json({ error: error.message })
    } else if (error.name === 'JsonWebTokenError') {
        return response.status(400).json({ error: error.message })
    } else if (error.name === 'TokenExpiredError') {
        return response.status(401).json({
            error: 'token expired'
        })
    }

    next(error)
}


この記事が気に入ったらサポートをしてみませんか?