ログイン機能
スキーマの作成
import mongoose from 'mongoose'
const Schema = mongoose.Schema;
const ItemSchema = new Schema({
title: String,
image: String,
price: String,
description: String,
email: String
});
// 追加
const UserSchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
});
export const ItemModel = mongoose.models.Item || mongoose.model("Item", ItemSchema);
// 追加
export const UserModel = mongoose.models.Item || mongoose.model("User", UserSchema);
ユーザー登録APIの作成
touch pages/api/user/register.js
import connectDB from '../../../utils/database'
import { UserModel } from "../../../utils/schemaModel";
const registerUser = async (req, res) => {
try {
await connectDB();
await UserModel.create(req.body)
return res.status(200).json({ msg: '登録しました。' })
} catch (err) {
return res.status(400).json({ msg: '失敗しました。' })
}
}
export default registerUser;
ユーザー登録フォームの作成
touch register.html
<form action="http://localhost:3000/api/user/register" method="POST">
<p>name: <input type="text" name="name"></p>
<p>email: <input type="text" name="email"></p>
<p>password: <input type="text" name="password"></p>
<p><button type="submit">登録</button></p>
</form>ユーザー登録とログインを追加
ログインAPIの作成
touch pages/api/user/login.js
import connectDB from '../../../utils/database'
import { UserModel } from "../../../utils/schemaModel";
const loginUser = async (req, res) => {
try {
await connectDB();
const savedUser = await UserModel.findOne({email: req.body.email});
if (savedUser && req.body.password === savedUser.password) {
return res.status(200).json({ msg: 'ログインに成功しました。' });
} else {
return res.status(400).json({ msg: 'ログインに失敗しました。' })
}
} catch (err) {
return res.status(400).json({ msg: 'エラーが発生しました。' })
}
}
export default loginUser;
ログインフォームの作成
touch login.html
<h2>ログイン</h2>
<form action="http://localhost:3000/api/user/login" method="POST">
<p>email: <input type="text" name="email"></p>
<p>password: <input type="text" name="password"></p>
<p><button type="submit">登録</button></p>
</form>
トークンの発行
- ログイン状態の維持にはセッション方式とトークン方式が使われれるが、ここではJWT(Json Web Token)を使う。
npm install jsonwebtoken - JWTではsignメソッドでトークンを発行する。
jwt.sign(ペイロード, シークレットキー, 有効期限) - トークンの中にはペイロードと有効期限が含まれていて、以下のサイトでデコードすることができる。
https://jwt.io/
import jwt from 'jsonwebtoken' // 追加
import connectDB from '../../../utils/database'
import { UserModel } from "../../../utils/schemaModel";
const loginUser = async (req, res) => {
try {
await connectDB();
const savedUser = await UserModel.findOne({email: req.body.email});
if (savedUser && req.body.password === savedUser.password) {
// 追加
const token = jwt.sign({email: req.body.email}, 'hogehogehoge', {expiresIn: '23h'});
// tokenを追加
return res.status(200).json({ msg: 'ログインに成功しました。', token: token });
} else {
return res.status(400).json({ msg: 'ログインに失敗しました。' })
}
} catch (err) {
return res.status(400).json({ msg: 'エラーが発生しました。' })
}
}
export default loginUser;
ログイン状態の判定
touch utils/auth.js
このファイルはリクエストを処理するjsファイルが実行される前にログイン状態を調べるものである。
このようなファイルをミドルウェアと呼ぶ。
import jwt from 'jsonwebtoken'
const middleware = (handler) => {
return async (req, res) => {
if (req.method === 'GET') {
return handler(req, res);
}
const token = req.headers.authorization.split(' ')[1]; // headersへのセットは後編の書籍
if (!token) {
return res.status(401).json({ msg: 'トークンがありません。' })
}
try {
const decoded = jwt.verify(token, 'hogehogehoge');
return handler(req, res);
} catch (err) {
return res.status(401).json({ msg: 'トークンが正しくありません。' })
}
};
};
export default middleware;
ミドルウェアの適用
- ミドルウェアを適用する処理には、exportの際にミドルウェア関数を通す。
import connectDB from '../../../utils/database'
import { ItemModel } from "../../../utils/schemaModel";
import auth from "../../../utils/auth"; // 追加
const createItem = async (req, res) => {
try {
await connectDB();
await ItemModel.create(req.body)
return res.status(200).json({ msg: '作成しました。' })
} catch (err) {
return res.status(400).json({ msg: '失敗しました。' })
}
}
export default auth(createItem); // auth()をコール
import connectDB from '../../../../utils/database'
import { ItemModel } from "../../../../utils/schemaModel";
import auth from "../../../utils/auth"; // 追加
const updateItem = async (req, res) => {
try {
await connectDB();
await ItemModel.updateOne({ _id: req.query.id }, req.body);
return res.status(200).json({ msg: '更新成功' });
} catch (err) {
return res.status(400).json({ msg: '更新失敗' });
}
}
export default auth(updateItem); // auth()をコール
import connectDB from '../../../../utils/database'
import { ItemModel } from "../../../../utils/schemaModel";
import auth from "../../../utils/auth"; // 追加
const deleteItem = async (req, res) => {
try {
await connectDB();
await ItemModel.deleteOne({_id: req.query.id});
return res.status(200).json({ msg: '削除成功' });
} catch (err) {
return res.status(400).json({ msg: '削除失敗' })
}
}
export default auth(deleteItem); // auth()をコール
投稿者のみ投稿修正・削除可能にする
ミドルウェアでreq.bodyにemailを追加する。
import jwt from 'jsonwebtoken'
const middleware = (handler) => {
return async (req, res) => {
if (req.method === 'GET') {
return handler(req, res);
}
const token = req.headers.authorization.split(' ')[1];
if (!token) {
return res.status(401).json({ msg: 'トークンがありません。' })
}
try {
const decoded = jwt.verify(token, 'hogehogehoge');
req.body.email = decoded.email; // 追加
return handler(req, res);
} catch (err) {
return res.status(401).json({ msg: 'トークンが正しくありません。' })
}
};
};
export default middleware;
呼び出し側で投稿者かどうかをチェック。
import connectDB from '../../../../utils/database'
import { ItemModel } from "../../../../utils/schemaModel";
import auth from "../../../utils/auth";
const updateItem = async (req, res) => {
try {
await connectDB();
const singleItem = await ItemModel.findById(req.query.id); // 追加
if (singleItem.email === req.body.email) { // 追加
await ItemModel.updateOne({ _id: req.query.id }, req.body);
return res.status(200).json({ msg: '更新成功' });
} else {
throw new Error();
}
} catch (err) {
return res.status(400).json({ msg: '更新失敗' });
}
}
export default auth(updateItem);
import connectDB from '../../../../utils/database'
import { ItemModel } from "../../../../utils/schemaModel";
import auth from "../../../utils/auth";
const updateItem = async (req, res) => {
try {
await connectDB();
const singleItem = await ItemModel.findById(req.query.id); // 追加
if (singleItem.email === req.body.email) { // 追加
await ItemModel.updateOne({ _id: req.query.id }, req.body);
return res.status(200).json({ msg: '更新成功' });
} else {
throw new Error();
}
} catch (err) {
return res.status(400).json({ msg: '更新失敗' });
}
}
export default auth(updateItem);