LoginSignup
1
0

More than 3 years have passed since last update.

[MERN②]User API Routes & JWT Authentication

Last updated at Posted at 2020-12-02

~~~~~~~~~~ (Contents) MERN ~~~~~~~~~~~
[MERN①] Express & MongoDB Setup
https://qiita.com/niyomong/private/3281af84486876f897f7
[MERN②]User API Routes & JWT Authentication
https://qiita.com/niyomong/private/c11616ff7b64925f9a2b
[MERN③] Profile API Routes
https://qiita.com/niyomong/private/8cff4e6fa0e81b92cb49
[MERN④] Post API
https://qiita.com/niyomong/private/3ce66f15375ad04b8989
[MERN⑤] Getting Started With React & The Frontend
https://qiita.com/niyomong/private/a5759e2fb89c9f222b6b
[MERN⑥] Redux Setup & Alerts
https://qiita.com/niyomong/private/074c27259924c7fd306b
[MERN⑦] React User Authentication
https://qiita.com/niyomong/private/37151784671eff3b92b6
[MERN⑧] Dashboard & Profile Management
https://qiita.com/niyomong/private/ab7e5da1b1983a226aca
[MERN⑨] Profile Display
https://qiita.com/niyomong/private/42426135e959c7844dcb
[MERN⑩] Posts & Comments
https://qiita.com/niyomong/private/19c78aea482b734c3cf5
[MERN11] デプロイ
https://qiita.com/niyomong/private/150f9000ce51548134ad
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. User Modelを作成

Modelフォルダ作成
Userモデルファイル作成

models/User.js
const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
  },
  avatar: {
    type: String,
  },
  date: {
    type: Date,
    default: Date.now,
  },
});

module.exports = User = mongoose.model('user', UserSchema);

2. リクエスト & Bodyバリデーション

server.js
...
connectDB();

+ // Init Middleware
+ app.use(express.json({ extended: false }));

app.get('/', (req, res) => res.send('API Running'));
...

express-validator

routes/api/users.js
const express = require('express');
const router = express.Router();
const { body, validationResult } = require('express-validator');

+ // @route  POST api/users
// @desc   Register user
// @access Public
router.post(
  '/',
+   [
+     body('name', 'Name is required').not().isEmpty(),
+     body('email', 'Please include a valid email').isEmail(),
+     body(
+       'password',
+       'Please enter a password with 6 or more characters'
+     ).isLength({ min: 6 }),
+   ],
  (req, res) => {
+     const errors = validationResult(req);
+     if (!errors.isEmpty()) {
+       return res.status(400).json({ errors: errors.array() });
+     }

    res.send('User route');
  }
);

module.exports = router;

POSTMANによるチェック

(nameのみ入力の場合)-> "400 Bad Request"(失敗の返答)
スクリーンショ!ット 2020-09-06 0.12.52.png

(name、 email、6桁passすべて入力した場合)-> "200 OK"(成功の返答)
スクリーンショット 2020-09-06 0.15.03.png

User Registration

-> gravatarを追加
-> bcryptを追加

routes/api/user.js
const express = require('express');
const router = express.Router();
+ const gravatar = require('gravatar');
+ const bcrypt = require('bcryptjs');
const { body, validationResult } = require('express-validator');

const User = require('../../models/User');

// @route  POST api/users
// @desc   Register user
// @access Public
router.post(
  '/',
  [
    body('name', 'Name is required').not().isEmpty(),
    body('email', 'Please include a valid email').isEmail(),
    body(
      'password',
      'Please enter a password with 6 or more characters'
    ).isLength({ min: 6 }),
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

+     const { name, email, password } = req.body;
+     try {
+       let user = await User.findOne({ email });

+      if (user) {
+         // See if user exists
+         return res.status(400).json({ erros: [{ msg: 'User already exists' }] });
+       }
+       // Get users gravatar
+       const avatar = gravatar.url(email, {
+         s: '200',
+         r: 'pg',
+         d: 'm',
+       });
+       user = new User({ name, email, avatar, password });
+       // Encrypt password
+       const salt = await bcrypt.genSalt(10);
+       user.password = await bcrypt.hash(password, salt);
+       await user.save();
+       // Return jsonwebtoken
+       res.send('User registered');
+     } catch (err) {
+       console.error(err.message);
+       res.status(500).send('Server error');
+     }
+   }
);

module.exports = router;

POSTMANでPost

スクリーンショット 2020-09-06 0.42.57.png

MongoDB Atlasに格納された!

スクリーンショット 2020-09-06 0.48.19.png

登録した後、再度Postをすると

スクリーンショット 2020-09-06 0.47.58.png

3. JWT (jsonWebToken)を実行

JWTの説明:https://github.com/auth0/node-jsonwebtoken
-> jwtを設置
-> configを設置

routes/api/users.js
...
const bcrypt = require('bcryptjs');
+ const jwt = require('jsonwebtoken');
+ const config = require('config');
const { body, validationResult } = require('express-validator');

...
      // Encrypt password
      const salt = await bcrypt.genSalt(10);
      user.password = await bcrypt.hash(password, salt);
      await user.save();

      // Return jsonwebtoken
+       const payload = {
+         user: {
+           id: user.id,
+         },
+       };

+       jwt.sign(
+         payload,
+         config.get('jwtSecret'),
+         { expiresIn: 360000 },
+         (err, token) => {
+           if (err) throw err;
+           res.json({ token });
+         }
      );
    } catch (err) {
      console.error(err.message);
      res.status(500).send('Server error');
...

mongoDB -> _id
mongoose -> id

config/default.json
{
  "mongoURI": "xxx",
+  "jwtSecret": "xxxxxtoken"
}

3. Authミドルウェアをカスタマイズ & JWT証明

middleware/auth.jsファイル作成

middleware/auth.js
const jwt = require('jsonwebtoken');
const config = require('config');

module.exports = function (req, res, next) {
  //Get token from header
  const token = req.header('x-auth-token');

  // Check if not token
  if (!token) {
    return res.status(401).json({ msg: 'No token, authorization denied' });
  }

  //Verify token
  try {
    const decoded = jwt.verify(token, config.get('jwtSecret'));

    req.user = decoded.user;
    next();
  } catch (err) {
    res.status(401).json({ msg: 'Token is not valid' });
  }
};

routes/api/auth.js
const express = require('express');
const router = express.Router();
+ const auth = require('../../middleware/auth');

+ const User = require('../../models/User');

// @route  GET api/auth
// @desc   Test route
// @access Public
+ router.get('/', auth, async (req, res) => {
+   try {
+     const user = await User.findById(req.user.id).select('-password'); //passwordは除くUserデータを取得
+     res.json(user);
+   } catch (err) {
+     console.error(err.message);
+     res.status(500).send('Server Error');
+   }
});

module.exports = router;

POSTMAN
Headersに
-> KEY: x-auth-token
-> VALUE: POST /api/usersした時にJWTでレスのあったTokenをペースト

スクリーンショット 2020-09-06 16.30.36.png

4. ユーザー認証 & ログイン

routes/api/auth.js
const express = require('express');
const router = express.Router();
+ const bcrypt = require('bcryptjs');
const auth = require('../../middleware/auth');
+ const jwt = require('jsonwebtoken');
+ const config = require('config');
+ const { body, validationResult } = require('express-validator');

const User = require('../../models/User');

// @route  GET api/auth
// @desc   Test route
// @access Public
router.get('/', auth, async (req, res) => {
   try {
    const user = await User.findById(req.user.id).select('-password');
    res.json(user);
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }
});


//以下、routes/api/user.jsからコピペして修正

+ // @route  POST api/auth
+ // @desc   Authenticate user & get token
+ // @access Public
+ router.post(
+   '/',
+   [
+     body('email', 'Please include a valid email').isEmail(),
+     body('password', 'Password is required').exists(),
+   ],
+   async (req, res) => {
+     const errors = validationResult(req);
+     if (!errors.isEmpty()) {
+       return res.status(400).json({ errors: errors.array() });
+     }

+     const { email, password } = req.body;

+     try {
+       let user = await User.findOne({ email });

+       if (!user) {
+         // See if user exists
+         return res
+           .status(400)
+           .json({ erros: [{ msg: 'Invalid Credentials' }] });
+       }

+       const isMatch = await bcrypt.compare(password, user.password);

+       if (!isMatch) {
+         return res
+           .status(400)
+           .json({ erros: [{ msg: 'Invalid Credentials' }] });
+       }

+       // Return jsonwebtoken
+       const payload = {
+         user: {
+           id: user.id,
+         },
+       };

+       jwt.sign(
+         payload,
+         config.get('jwtSecret'),
+         { expiresIn: 360000 },
+         (err, token) => {
+           if (err) throw err;
+           res.json({ token });
+         }
+       );
+     } catch (err) {
+       console.error(err.message);
+       res.status(500).send('Server error');
+     }
+   }
);

module.exports = router;

POSTMANで登録済みの"email"と"password"をPOSTした場合

スクリーンショット 2020-09-06 16.51.51.png

誤った"email"と"password"をPOSTした場合

スクリーンショット 2020-09-06 16.52.11.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0