Edited at

Node.js APIで認証付きのMockREST APIサーバの導入

More than 1 year has passed since last update.


はじめに

@ochiochi さんの記事に触発され、Node.jsでWebAPIの認証付きのMockWebAPI(JSON Server)を作成してみました。

http://qiita.com/ochiochi/items/966b884eb17045dfb929


JSON Server

JSON Serverは、Node.jsのモジュールで、簡単にREST APIサーバが導入できます。

@ochiochi さんの紹介)

https://github.com/typicode/json-server

JSON Serverは、Express.jsをベースにしているので、認証機能等を追加する場合には、Express.js用のプログラムを作成します。


JSON Server導入


1. node module

$ npm install json-server -g


2. Mock データベースファイルの作成

REST API によって、操作するデータベースとなるファイルをJSON形式で作成します。


books.json

{

"books": [
{
"id": 1,
"title": "Math",
"price": 1000
},
{
"id": 2,
"title": "Science",
"price": 1200
},
{
"id": 3,
"title": "Chemical",
"price": 1400
}
]
}


3. REST APIサーバ起動

$ json-server --watch ./books.json

デフォルトポート3000で、Webサーバが起動します。

method
url
request content type

GET
http://localhost:3000/books

GET
http://localhost:3000/books/1

POST
http://localhost:3000/books
application/json

PATCH
http://localhost:3000/books/1
application/json

PUT
http://localhost:3000/books/1
application/json

DELETE
http://localhost:3000/books/1

Webサーバサーバを停止する場合には、Ctrl + c


認証機能導入


1. node module

認証トークンの作成と、検証するためのモジュールを導入します。

$ npm install jsonwebtoken


2. Mock ユーザ情報データファイルの作成

ログインに必要なユーザ情報を用意します。このファイルは、認証時に使用する目的以外で公開しません。


users.json

{

"users": [
{
"id": 1,
"name": "hoge",
"email": "hoge@email.com",
"password": "hoge"
},
{
"id": 2,
"name": "foo",
"email": "foo@email.com",
"password": "foo"
},
{
"id": 3,
"name": "bar",
"email": "bar@email.com",
"password": "bar"
}
]
}


2. 認証REST APIプログラムの作成

プログラム内部で、JSON Serverを利用してREST APIサーバを利用します。


auth-api-server.js

//モジュール参照

const fs = require('fs')
const bodyParser = require('body-parser')
const jsonServer = require('json-server')
const jwt = require('jsonwebtoken')

//JSON Serverで、利用するJSONファイルを設定
const server = jsonServer.create()
const router = jsonServer.router('./books.json')

//JSONリクエスト対応
server.use(bodyParser.urlencoded({extended: true}))
server.use(bodyParser.json())

//署名作成ワードと有効期限(1時間)
const SECRET_WORD = 'SECRET1234'
const expiresIn = '1h'

//署名作成関数
const createToken = payload => jwt.sign(payload, SECRET_WORD, {expiresIn})

//署名検証関数(非同期)
const verifyToken = token =>
new Promise((resolve, reject) =>
jwt.verify(token, SECRET_WORD, (err, decode) =>
decode !== undefined ? resolve(decode) : reject(err)
)
)

//ユーザDBファイル読み込み
const userdb = JSON.parse(fs.readFileSync('./users.json', 'UTF-8'))

//ログイン関数 true:ok false:ng
const isAuth = ({email, password}) =>
userdb.users.findIndex(user => user.email === email && user.password === password) !== -1

//ログインRouter
server.post('/auth/login', (req, res) => {
const {email, password} = req.body

//ログイン検証
if (isAuth({email, password}) === false) {
const status = 401
const message = 'Incorrect email or password'
res.status(status).json({status, message})
return
}

//ログイン成功時に認証トークンを発行
const access_token = createToken({email, password})
res.status(200).json({access_token})
})

//認証が必要なRouter(ログイン以外全て)
server.use(/^(?!\/auth).*$/, async (req, res, next) => {

//認証ヘッダー形式検証
if (req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer') {
const status = 401
const message = 'Error in authorization format'
res.status(status).json({status, message})
return
}

//認証トークンの検証
try {
await verifyToken(req.headers.authorization.split(' ')[1])
next()
} catch (err) {
//失効している認証トークン
const status = 401
const message = 'Error access_token is revoked'
res.status(status).json({status, message})
}
})

//認証機能付きのREST APIサーバ起動
server.use(router)
server.listen(3000, () => {
console.log('Run Auth API Server')
})



3. 認証REST APIプログラムの起動

$ node auth-api-server.js


4. ログインAPI

ログインAPIを使用して、認証トークンを発行します。以降のREST APIアクセスは、認証トークンをヘッダーに付加します。

method
url
request content type

POST
http://localhost:3000/auth/login
application/json


  • Request Body

{

"email": "hoge@email.com",
"password":"hoge"
}


  • Response Body (200 OK)

{

"access_token": "XXXXXXXXXXXXXXXX"
}


  • Response Body (401 Unauthorized)

{

"status": 401,
"message": "Incorrect email or password"
}


5. 認証付きのAPI

REST APIアクセスは、認証トークンをヘッダーに付加します。


  • Request Header (required access_token)

Authorization: Bearer XXXXXXXXXXXXXXXX

認証トークンが失効していた場合


  • Response Body (401 Unauthorized)

{

"status": 401,
"message": "Error access_token is revoked"
}

認証ヘッダー形式エラーの場合


  • Response Body (401 Unauthorized)

{

"status": 401,
"message": "Error in authorization format"
}