Express: JWT認証

こちらのページと同じことを行いました。
[【Express + JWT】ExpressでJWTを使った認証機能を作る](https://www.dailyupblog.com/backend_development/1117/)
データペースは、PostgreSQL を使っています。

## PostgreSQL の用意

>user: scott
>password: tiger123
>database: expressdb

テーブルを作成

```bash:go_create.sh
psql -U scott expressdb < create_table.sql
```

```sql:create_table.sql
create table users (id text primary key, name text, email text, password text);
```

## express の用意

```bash
mkdir ex04
cd ex04
npx express-generator
npm install
```

この段階でサーバーを動かしてみます。

```bash
DEBUG=ex04:* npm start
```

クライアントでアクセス

```bash
http http://localhost:3000
```

必要なライブラリーのインストール

```bash
npm install bcrypt
npm install pg
npm install jsonwebtoken
```

データベースの設定ファイルの作成

```bash
mkdir db
touch db/pool.js
```

この時点でのフォルダーの構造

```text
$ tree -L 1
.
├── app.js
├── bin
├── db
├── node_modules
├── package-lock.json
├── package.json
├── public
├── routes
└── views
```

## プログラム

次の２つのプログラムを修正

```js:db/pool.js
const { Pool } = require("pg");

const pool = new Pool({
  user: "scott",
  host: "127.0.0.1",
  database: "expressdb",
  password: "tiger123",
  port: 5432,
});

module.exports = pool;
```

```js:routes/index.js
// ---------------------------------------------------------------
//	routes/index.js
//
//					May/31/2023
//
// ---------------------------------------------------------------
const express = require("express")
const router = express.Router()
const jwt = require("jsonwebtoken")

const pool = require("../db/pool")

const bcrypt = require("bcrypt")
const saltRounds = 10

// ---------------------------------------------------------------
function users_proc (req, res, next) {
	console.error("*** users_proc *** start ***")
	pool.query("SELECT * FROM users", function (error, results) {
		if (error) {
			throw error
		}
		res.status(200).json({
			data: results.rows,
		})
	})
}

// ---------------------------------------------------------------
function register_proc (req, res, next) {
	console.error("*** register_proc *** start ***")
	const idLength = 10
	const idSource = "abcdefghijklmnopqrstuvwxyz0123456789"
	let id = ""
	for (let i = 0; i < idLength; i++)
		id += idSource[Math.floor(Math.random() * idSource.length)]
	const { name, email, password } = req.body
	const hassedPassword = bcrypt.hashSync(password, saltRounds)
	pool.query(
		"INSERT INTO users VALUES ($1, $2, $3, $4)",
		[id, name, email, hassedPassword],
		function (error, results) {
			if (error) {
				res.status(500).json({
					status: "500 Internal Server Error",
					error: error,
				})
			}
			res.status(201).json({
				status: "success",
			})
		}
	)
}

// ---------------------------------------------------------------
function delete_proc (req, res, next) {
	console.error("*** delete_proc *** start ***")
	const id = req.params.id
	pool.query("DELETE FROM users WHERE id = $1", [id], function (
		error,
		results
	) {
		if (error) {
			res.status(500),
				json({
					status: "500 Internal Server Error",
					error: error,
				})
		}
		if (results.rowCount === 0) {
			res.status(400).json({
				status: "400 Bad Request",
				message: "データが存在しません。",
			})
		} else {
			res.status(200).json({
				status: "success",
				message: "データを削除しました。",
			})
		}
	})
}

// ---------------------------------------------------------------
function login_proc (req, res, next) {
	console.error("*** login_proc *** start ***")
	const { email, password } = req.body
	pool.query("SELECT * FROM users WHERE email = $1", [email], function (
		error,
		user
	) {
		if (error) {
			res.status(400).json({
				status: "400 Bad Request",
				error: error,
			})
		}
		if (user.rowCount === 0) {
			return res.json({
				message: "email not found",
			})
		}
		bcrypt.compare(password, user.rows[0].password, function (error, results) {
			if (error) {
				return res.status(400).json({
					error: error.message,
				})
			}
			if (!results) {
				return res.json({
					message: "password is not correct",
				})
			}
			//Tokenの発行　書き換え
			const payload = {
				id: user.rows[0].id,
				name: user.rows[0].name,
				email: user.rows[0].email,
			}
			const token = jwt.sign(payload, "secret")
			return res.json({ token })
		})
	})
}

// ---------------------------------------------------------------
function verify_proc (req, res, next) {
	console.error("*** verify_proc *** start ***")
	const bearToken = req.headers["authorization"]
	const bearer = bearToken.split(" ")
	const token = bearer[1]

	jwt.verify(token, "secret", (error, user) => {
		if (error) {
			return res.sendStatus(403)
		} else {
			return res.json({
				user,
			})
		}
	})
}

// ---------------------------------------------------------------
// ユーザー情報取得API
router.get("/users", users_proc)

// ---------------------------------------------------------------
// ユーザー情報登録API
router.post("/auth/register", register_proc)

// ---------------------------------------------------------------
// ユーザー情報削除API
router.delete("/delete/:id", delete_proc)

// ---------------------------------------------------------------
// ログインAPI
router.post("/auth/login", login_proc)

// ---------------------------------------------------------------
// Token確認API
router.get("/auth/user", verify_proc)

// ---------------------------------------------------------------
module.exports = router
// ---------------------------------------------------------------
```

## サーバーの起動

```bash
npm start
```

## テストスクリプト

ユーザーの登録

```bash:go_register.sh
http POST http://localhost:3000/auth/register  < in01.json
```

```json:in01.json
{
    "name": "steeve",
    "email": "steeve@test.com",
    "password": "123456"
}
```

ユーザーの一覧

```bash:go_users.sh
http http://localhost:3000/users
```

ユーザーの削除
>ID は変更してください。

```bash:go_delete.sh
ID="falf021wvz"
http DELETE http://localhost:3000/delete/${ID}
```

ログイン　(JWT トークンの取得)

```bash
URL="http://localhost:3000/auth/login"
#
http POST $URL email=steeve@test.com password="123456" > token.json
```

トークンを使ってユーザー情報の取得

```bash:go_verify.sh
#
token=`jq .token token.json | sed 's/"//g'`
echo ${token}
#
URL="http://localhost:3000/auth/user"
#
http $URL "Authorization: Bearer ${token}"
```