【TypeScript × Node.js(express) × ChatGPT】JWTトークンを発行する方法

# はじめに
本記事は、自己学習用に作成中のアプリの機能としてタイトルにある内容を実装したものです。
一部コードは話題の**ChatGPT**が生成したものになるので、おまけとして**ChatGPTとのやりとり**も掲載いたします。

# 実装内容
※最初にコードを記載して、その後、解説となります

```typescript:index.ts
import express from "express";
import mongoose from "mongoose";
import "dotenv/config";
import userApis from "./src/v1/api/userApi";

// Express FWによるローカルサーバーの立ち上げ
const app: express.Express = express();
const PORT = 4000;
const url = process.env.MONGODB_URL ? process.env.MONGODB_URL : "";

// jsonオブジェクトを扱うため
app.use(express.json());

// DB接続
try {
	mongoose.set("strictQuery", true);
	mongoose.connect(url);
	console.log("DB接続");
} catch (e) {
	console.log(e);
}

// ユーザーのAPI呼び出し
userApis(app);

app.listen(PORT, () => {
	console.log("ローカルサーバー起動中");
});
```
ここは、サーバーの立ち上げとDB接続して、APIを呼び出すだけの処理を書いています。

```typescript:user.ts
import mongoose from "mongoose";

// userModel作成
const userSchema = new mongoose.Schema({
	username: {
		type: String,
		required: true,
		unique: true,
	},
	password: {
		type: String,
		required: true,
	},
});

module.exports = mongoose.model("User", userSchema);
```
これはModelで、`MongoDB`に`username`と`password`を格納するようなスキーマにしています。


```typescript:userApi.ts
import { register } from "../service/userService";
import express from "express";

const userApis = (app: express.Express) => {
	// ユーザー新規登録API
	app.post("/register", (req: express.Request, res: express.Response) => {
		register(req, res);
	});
};

export default userApis;
```

ここで、ユーザーに関するAPIを記述しています。
実際のビジネスロジックは`register()`に記述しています。
以下、その内容です。

```typescript:userService.ts
import express from "express";
import CryptoJS from "crypto-js";
import jwt from "jsonwebtoken";
import "dotenv/config";

const User = require("../models/user");

const register = async (req: express.Request, res: express.Response) => {
	// パスワードの受け取り
	const password = req.body.password;
	try {
		// パスワードの暗号化
		const key = process.env.SECRET_KEY ? process.env.SECRET_KEY : "";
		req.body.password = CryptoJS.AES.encrypt(password, key);

		// ユーザー新規作成
		const user = await User.create(req.body);

		// JWTの発行
		const secret = process.env.TOKEN_SECRET_KEY
			? process.env.TOKEN_SECRET_KEY
			: "";
		const token = jwt.sign({ id: user._id }, secret, {
			expiresIn: "24h",
		});

		return res.status(200).json({ user, token });
	} catch (e) {
		return res.status(500).json(e);
	}
};

export { register };
```

ポイントは、パスワードを一度、変数化し、その後に暗号化するところです。
三項演算子にしているのは、`CryptoJS`の`encrypt()`に渡す際に`string型`のみ許可されているためです。
`dotenv`の`process`を介すると`undefined`の可能性があるため、暗号化したパスワードを`encrypt()`に渡すことができません。
そのため、上記のように三項演算子としています。

ユーザーの登録に関しては、`mongoose`の`model`にある`create()`で行います。
その際に、引数にリクエストの`body`を渡すことで`MongoDB`に登録されます。

最後に、**JWTの発行**ですが、こちらもパスワードと同様、`string型`のみ引数として許可されいるため、三項演算子を用いて変数にしています。
実際にトークンを発行する処理は、`jwt`の`sign()`で行い、第一引数に、一意の値としてidを渡し、第二引数にsecret_keyを、第三引数にいつまで有効かという情報を渡します。

これにより、JWTの発行によるユーザー登録が行えます。

# おまけ
JWTによるトークン発行に関する簡易的な記事が見つけられなかったので、話題の`chatGPT`に質問してみました。

使った感想としては、個人的には、ググる能力より質問力であったり、課題を見つけ、言語化する能力がこれからのエンジニアにとっては重要なのかなと思いました。

### chatGPTによるコードの生成
![スクリーンショット 2023-02-18 21.22.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/de40ac27-cf40-b001-69c0-5442f159ed6e.png)
![スクリーンショット 2023-02-18 21.22.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/07f22547-e4ca-8134-8b6e-eb0a8d609e94.png)

![スクリーンショット 2023-02-18 21.22.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/670c7ddf-0f02-5429-c20d-4144a6ee4692.png)