Node.js : ระบบ Authentication แบบพื้นฐานด้วยมอดูล Passport-local
Tags: node, javascript, ejs
Published: 2019-10-19 02:01:33

บทความนี้จะขอแนะนำการเขียนสคริปต์ JavaScript เพื่อทำระบบ Authenticationแบบพื้นฐาน ด้วยมอดูล Passport จะช่วยให้ระบบสามารถตรวจสอบสมาชิกก่อนที่เข้าใช้งานระบบสมาชิกได้ โดยมอดูล passport จะช่วยตรวจสอบ Username และ Password ได้ทั้งแบบพื้นฐาน ผ่าน Account ของฐานข้อมูลของเรา ผ่าน Account ของ Facebook ผ่าน Account ของ Twitter ฯลฯ 

ซึ่งในตัวอย่างนี้จะแนะนำการเขียนสคริปต์ทั้งแบบพื้นฐาน คือ นำชื่อผู้ใช้และรหัสผ่านใส่ไว้ในสคริปต์ เพื่อทำความเข้าใจหลักการใช้งานมอดูล Passport 

โดยเริ่มจากติดตั้งมอดูล express, ejs, express, express-session, passport, passport-local

npm i cookie-parser
npm i ejs
npm i express
npm i express-session
npm i passpost
npm i passport-local


**สคริปต์ views/login.ejs** สร้างฟอร์มรับข้อมูล login ฟอร์ม login

```html
<!DOCTYPE html>
<html>
  <head>
    <title>:: Login ::</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
      <div class="container">
          <div class="page-header">
              <h1>:: Login ::</h1>
              <hr>
          </div>
          <div class="row">
            <div class="col">
              <form method="post" action="login">
                <div class="form-group">
                  <label for="user">Username</label>
                  <input type="text" class="form-control" name="username" 
                    placeholder="Username" required>
                </div>
                <div class="form-group">
                  <label for="pass">Password</label>
                  <input type="text" class="form-control"name="password" 
  placeholder="Password" required>
                </div> 
                <button type="submit" class="btn btn-primary">Login</button>
                <button type="reset" class="btn btn-primary">Reset</button>
              </form>
            </div>
          </div>
        </div>
    <script src="./jquery.min.js"></script>
    <script src="./bootstrap/js/bootstrap.min.js"></script>
  </body>
</html>
```

**สคริปต์ views/home.ejs** หน้าหลักหลังจากผ่านการ login เข้ามาแล้ว
```html
<!DOCTYPE html>
<html>
  <head>
    <title>:: Home ::</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
      <div class="container">
          <div class="row">
            <h1>:: Home ::</h1>
          </div>
          <div class="row">
             Login Ok.<br/><br/> 
             Users -> <%=_id%> <%=fname%> <%=lname%> <br/><br/>
          </div>
          <div class="row">
            <a href='./logout'>Logout</a>
          </div>
        </div>
    <script src="./jquery.min.js"></script>
    <script src="./bootstrap/js/bootstrap.min.js"></script>
  </body>
</html>
```

สคริปต์ index.js เป็นสคริปต์หลักของงานนี้
```javascript
const express = require('express')
const app = express()
app.use(express.static(__dirname + '/public'))
app.set('view engine', 'ejs')

const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy

const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(cookieParser())

const session = require('express-session')
app.use(session({ secret: 'I am Anakin.', resave: true, saveUninitialized: true }))
app.use(passport.initialize())
app.use(passport.session())

passport.use(new LocalStrategy((username, password, done) => {
    if(username =="admin" && password=="1234") {
        user = {    
            _id : 1,
            fname : "Anakin ",
            lname : "Skywalker"
        }
        console.log('Correct Password.')
        return done(null,user)
    } else {
        console.log('Incorrect password.')
        return done(null, false, { message: 'Incorrect password.' })
    }
}))

passport.serializeUser((user, done) => {
    console.log('SerializeUser')
    done(null, user) 
})

passport.deserializeUser((user, done) => {
    console.log('DeserializeUser')
    done(null, user) 
})

app.get('/', (req, res) =>  {
    res.render('login')
})

app.post('/login', passport.authenticate('local', { 
    successRedirect: '/home',
    failureRedirect: '/' 
}))

function isLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next()
    } else {
        res.redirect('/')
    }
}

app.get('/home',isLoggedIn,(req,res) => {
    res.render('home',req.user)
})

app.get('/logout',(req,res) => {
    req.logout()
    res.redirect('/')
})

app.listen(3000, () => {
	console.log('Server Started on localhost:3000...')
})

```

เราต้องเรียกใช้มอดูล passport เก็บไว้ในตัวแปร passport และเรียกใช้มอดูล passport-local เก็บไว้ในตัวแปร LocalStrategy สำหรับใช้ตรวจสอบผู้ใช้และรหัสผ่านแบบพื้นฐานได้
```javascript
const passport = require('passport') 
const LocalStrategy = require('passport-local').Strategy
```
เรียกใช้มอดูล express-session เก็บไว้ในตัวแปร session สำหรับเก็บข้อมูลที่เก็บหลังจากตรวจสอบผู้ใช้และรหัสผ่านเรียบร้อยแล้ว
```javascript
const session = require('express-session')
app.use(session({ secret: 'I am Anakin.', resave: true, saveUninitialized: true }))
app.use(passport.initialize())
app.use(passport.session())
```

กำหนดให้มอดูล password ใช้งานการตรวจสอบแบบ Local โดยนำชื่อผู้ใช้ที่อยู่ในตัวแปร username และ รหัสผ่านในตัวแปร password นำมาตรวจสอบแบบง่าย ซึ่งหากผู้ใช้ป้อนมาไม่ถูกต้องก็แสดงข้อความไม่ถูกต้อง และหากตรวจสอบแล้วผู้ใช้ป้อนข้อมูลมาถูกต้อง ก็กำหนดข้อมูลที่จะเก็บไว้ใน Session เพื่อเรียกใช้งานภายหลังได้ โดยในตัวอย่างนี้จะเก็บข้อมูล _id,fname,lname เก็บไว้ในตัวแปร user 

```javascript
passport.use(new LocalStrategy((username, password, done) => {
    if(username =="admin" && password=="1234") {
        user = {    
            _id : 1,
            fname : "Anakin ",
            lname : "Skywalker"
        }
        console.log('Correct Password.')
        return done(null,user)
    } else {
        console.log('Incorrect password.')
        return done(null, false, { message: 'Incorrect password.' })
    }
}))

```

เป็นการกำหนดว่าจะนำข้อมูลอะไรไปเก็บไว้ใน Session หลังจากผ่าน Login มาเรียบร้อบแล้ว
```javascript
passport.serializeUser((user, done) => {
    console.log('SerializeUser')
    done(null, user) 
})
```
เป็นการกำหนดว่าจะนำข้อมูลออะไรใน Session ออกมาใช้งาน ซึ่งจะใช้ในหน้าเว็บเพจต่างๆ ที่สมาชิกเข้าถึงได้ 
```javascript
passport.deserializeUser((user, done) => {
    console.log('DeserializeUser')
    done(null, user) 
})
```

ที่กล่าวไว้ข้างต้นจะเป็นการกำหนดค่าสำหรับใช้งานมอดูล passport-local ซึ่งรันหน้าแรก / ก็จะแสดงฟอร์ม Login เพื่อรับ Username และ Password จากผู้ใช้ ซึ่งข้อมูลทั้งหมดก็จะส่งมายัง /login เพื่อทำการตรวจสอบข้อมูล หากข้อมูลถูกต้องก็จะส่งไปหน้า /home และหากข้อมูลไม่ถูกต้องก็ย้อนกลับไปหน้าฟอร์ม login 

```javascript
app.get('/', (req, res) =>  {
    res.render('login')
})

app.post('/login', passport.authenticate('local', { 
    successRedirect: '/home',
    failureRedirect: '/' 
}))
```

ในระบบสมาชิกก็จะมีส่วนที่สมาชิกเข้าถึงได้หลายหน้า แต่ละหน้าก่อนจะเข้าไปใช้งานได้ต้องผ่านการตรวจสอบมาก่อนว่า ได้ผ่านการ Login มาแล้วหรือไม่ โดยเราจะสร้างฟังก์ชันชื่อ isLoggedIn()เอาไว้ตรวจสอบ โดย method ชื่อ isAuthenticated() เป็นตัวหลักในการตรวจสอบการ login ซึ่งหากผ่านการตรวจสอบแล้วก็ไปในขั้นตอนต่อไป แต่หากไม่ผ่านการตวจสอบก็จะกลับไปยังฟอร์มหน้า login

```javascript
function isLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next()
    } else {
        res.redirect('/')
    }
}
```

ไปหน้าหลักของสมาชิก แล้วแสดงข้อมูลสมาชิกออก user

```javascript
app.get('/home',isLoggedIn,(req,res) => {
    res.render('home',req.user)
})
```

หากต้องการออกจากระบบก็จะต้องทำการ logout() และกลับไปหน้า Login ต่อไป

```javascript
app.get('/logout',(req,res) => {
    req.logout()
    res.redirect('/')
})
```

**สรุป**

อย่างที่กล่าวไว้ข้างต้น บทความนี้แนะนำทำระบบ Authentication แบบพื้นฐานด้วยมอดูล Passport-local เพื่อที่จะทำให้เข้าใจหลักการตรวจสอบ Username และ Password อย่างง่ายของมอดูล Passport โดยเราจะใส่ Username และ Password เอาไว้ในสคริต์เลย 

ซึ่งหากนำไปใช้งานจริงแนะนำว่าควรนำข้อมูล User เก็บไว้ในฐานข้อมูลจะทำให้ User จะใช้งานระบบได้มากกว่า 1 คนและเพื่อเพิ่มความปลอดภัยของระบบสมาชิกได้ 

และเราสามารถหาข้อมูลเพิ่มเติมของมอดูล passport ได้จาก https://www.npmjs.com/package/passport