- Nuxt.js Fundamental ตอนที่ 0 - พื้นฐานการเขียนเว็บด้วย Nuxt.js
- Nuxt.js Fundamental ตอนที่ 1 - เริ่มต้นกับ Nuxt.js
- Nuxt.js Fundamental ตอนที่ 2 - สร้าง Nuxt.js ด้วย create-nuxt-app
- Nuxt.js Fundamental ตอนที่ 3 - การกำหนด Routing
- Nuxt.js Fundamental ตอนที่ 4 - Nuxt.js Concept
- Nuxt.js Fundamental ตอนที่ 5 - Nuxt Content และ Async Data
- Nuxt.js Fundamental ตอนที่ 6 - การ Fetch ข้อมูลจาก API
- Nuxt.js Fundamental ตอนที่ 7 - การใช้งานร่วมกับ Vuex Store
- Nuxt.js Fundamental ตอนที่ 8 - การทำระบบ Authentication
- Nuxt.js Fundamental ตอนที่ 9 - การ Deploy Nuxt.js
- Nuxt.js Fundamental ตอนที่ 10 - การทำ Internal API และ Middleware
- Nuxt.js Fundamental ตอนที่ 11 - ทำ SEO และ Meta tags
- Nuxt.js Fundamental ตอนที่ 12 - ทำ Workshop เว็บ Portfolio
Nuxt.js Fundamental ตอนที่ 8 - การทำระบบ Authentication
เขียนวันที่ : Aug 24, 2020
(อัพเดท : Mar 19, 2022)
เชื่อว่าในเกือบทุกๆ Web Application ย่อมมีการทำระบบ Authentication แน่นอน มีระบบ Login ระบบสมัครสมาชิก และระบบเหล่านี้ ก็เป็นระบบเริ่มต้น ที่น่าศึกษา เพราะเป็นพื้นฐานของเว็บไซต์เกือบทั้งหมดต้องมี
รวมถึงคำถามยอดฮิต ว่าเราจะทำระบบ Login ระบบสมาชิก ด้วยภาษา X กับ Database Y ยังไง?
สำหรับตอนนี้จะเป็นการทำ Authentication บน Nuxt.js นะครับ ตัว Concept คล้ายๆ กับทุกๆ Framework ทุกๆ ภาษา เช่น ทำ Authen ด้วย Vue.js หรือ React.js ใช้ APIs หรือ Firebase เป็น API ตัว concept ก็ไม่ต่างกันมากครับ
เอาละ มาเริ่มเลยดีกว่า
รู้จักกับ Nuxt Auth Module
เราจะใช้ @nuxt/auth
ในการทำ Authentication นะครับ ข้อดีคือมี built-in หลายๆ อย่างให้เราใช้ (มองคล้ายๆ passport.js
ของ Node.js ครับ) เช่น
- การกำหนด middleware
- มี strategy ให้เลือก เช่น local หรือ oAuth หรือ Providers อื่นๆ
- มี built-in function ให้ใช้ เช่น
loginWith
,logout
,setToken
,setuser
เป็นต้น
ข้อควรรู้ในการใช้ @nuxtjs/auth
- ต้อง activate Vuex Store ก่อนนะครับ
เริ่มติดตั้ง Nuxt Auth กันเลย (แนะนำใช้ร่วมกับ Nuxt Axios)
npm install @nuxtjs/auth @nuxtjs/axios
ต่อมาที่ไฟล์ nuxt.config.js
เพิ่ม modules ลงไป
modules: [
'@nuxtjs/axios',
'@nuxtjs/auth'
],
auth: {
// Options
}
วิธีการใช้งาน Nuxt Auth
มาดู Concept คร่าวๆ กันก่อนนะครับ โดย Nuxt Auth เราจะใช้ผ่าน middleware หรือ global ด้วย nuxt.config.js
ก็ได้
- Config ค่าต่างๆ ผ่าน
nuxt.config.js
- ใช้ state ใน Nuxt Auth เพื่อเช็ค
loggedIn
ว่า login หรือยัง ถ้ายัง ก็ถูก redirect ไป/login
เป็นต้น - ใช้ Nuxt Auth Strategy แบบ local เพื่อ authenticate ด้วยการใช้ Email และ Password
- สามารถ handle หรือกำหนดว่า Page ไหนเข้าได้ เข้าไม่ได้ ผ่าน middleware
ตัวอย่าง การ config ไฟล์ nuxt.config.js
// optional
axios: {
baseURL: 'http://127.0.0.1:8888/api',
credentials: true
},
auth: {
strategies: {
local: {
endpoints: {
login: { url: 'login', method: 'post', propertyName: 'data.token' },
user: { url: 'me', method: 'get', propertyName: 'data' },
logout: false
}
}
},
redirect: {
login: '/login'
}
}
- กำหนด strategy เป็นแบบ
local
และ endpoints url ของ login คือ${BASE_API}/login
ซึ่ง BASE_API เรากำหนดผ่าน environment variable หรือกำหนดด้วย axios ก็ได้ - เราสามารถกำหนด redirect ได้เช่น กัน เช่น ถ้า
$auth.isLoggedIn
เป็น false ก็ redirect ไปหน้า login ที่เรากำหนด - ตัว
credentials: true
ใน axios คือกำหนดให้ทุกๆ request จะส่งAuthorization
header ไปด้วย
ทีนี้เวลาเราจะเข้าถึง Nuxt Auth ก็แค่ใช้
this.$auth;
เช่น เข้าถึง user หรือ isLoggedIn
this.$auth.user;
// มีค่าเท่ากับเข้าถึงผ่าน Vuex
this.$store.state.auth.user;
this.$auth.isLoggedIn;
// เท่ากับ
this.$store.state.auth.isLoggedIn;
หรืออยากเรียก authenticate เพื่อ login ก็แค่ใช้
const payload = {
data: {
username: '',
password: ''
}
};
this.$auth.loginWith('local', payload);
หรือ setUserToken
this.$auth.setUserToken(token);
middleware
ตัว Middleware เป็นตัวกำหนด ว่าแต่ละหน้า จะเข้าถึงได้ก็ต่อเมื่อ authenticated ผ่านแล้วหรือเปล่า หรือเราจะกำหนดเป็นแบบ global ก็ได้
// nuxt.config.js
router: {
middleware: ['auth'];
}
แบบนี้ ทุกๆ หน้าจะต้องผ่าน auth middleware ก่อน ถ้าไม่มี this.$auth.isLoggedIn
ก็ไม่สามารถเข้าหน้านี้ได้
แต่เราก็สามารถกำหนดแต่ละหน้าได้ เช่น หน้า /login
หรือ /register
ก็ไม่จำเป็นต้องมี auth middlware จริงมั้ย การกำหนดแต่ละหน้า ก็ทำแบบนี้เลย
<template> </template>
<script>
export default {
middleware: 'auth'
};
</script>
เริ่มลงมือทำ Authentication
เพื่อนๆ อาจจะงงๆ ตัวทฤษฎี หรือหลักการข้างบน มาลองลงมือทำดูดีกว่า จะได้เห็นภาพจริงๆ เอาแบบง่ายๆ ละกันเนาะ เราจะมีแค่
/login
- หน้าสำหรับกรอก email, password/
- หน้าหลักเข้าได้ทุกคน/private
- หน้านี้เข้าได้ เฉพาะผ่าน authenticated แล้วเท่านั้น ถ้า request แบบไม่มี authen ก็ redirect ไปหน้า login ทันที
ทีนี้ส่วน API ก็จะมีง่ายๆ แบบนี้
POST /api/login
- ส่งemail
และpassword
เป็น payload มาเพื่อ login (ได้ user และ token กลับไป)GET /me
- request เพื่อ ดึงข้อมูล currentUser โดยส่ง token แนบมากับ headers ด้วย
เมื่อเราได้ concept คร่าวๆ แล้ว ก็มาเริ่มเลย
ไฟล์ server.js
ด้านล่าง ก็อปไป แล้ว start server ด้วยคำสั่ง node server.js
ได้เลยครับ
const express = require('express');
const cors = require('cors');
const app = express();
const router = express.Router();
app.use(cors());
app.use(express.json());
const user = {
id: 1,
username: 'john',
email: 'john@doe.com',
name: 'John Doe'
};
router.get('/me', (req, res) => {
return res.json({
data: {
user
}
});
});
router.post('/login', (req, res) => {
const { email, password } = req.body;
// query db.
if (email === 'admin@admin.com' && password === '123456') {
return res.json({
data: {
user,
token: 'THIS_IS_TOKEN'
}
});
} else {
return res.status(401).json({
message: 'Invalid Password'
});
}
});
app.use('/api', router);
app.listen(12345, () => {
console.log('Mock API start on port 12345');
});
และ install
npm install express cors --save-dev
Note : ตัว Server เป็นแค่ Example เฉยๆนะครับ ไม่มี logic หรือ validate ใดๆ แค่รับ request และ response กลับไปเท่านั้น
สามารถใช้ Project เดิม หรือขึ้นใหม่ด้วย create-nuxt-app
ก็ได้ครับ
สร้างไฟล์ pages/login.vue
<template>
<form @submit="login">
<input type="email" name="email" v-model="email" />
<input type="password" name="password" v-model="password" />
<button type="submit">Login</button>
</form>
</template>
<script>
export default {
data() {
return {
email: '',
password: ''
};
},
methods: {
async login(e) {
e.preventDefault();
const payload = {
email: this.email,
password: this.password
};
try {
await this.$auth.loginWith('local', {
data: payload
});
this.$router.push('/');
} catch (e) {
this.$router.push('/login');
}
}
}
};
</script>
ต่อมากำหนด nuxt.config.js
ให้ตรง และกำหนด baseURL
เป็น port 12345 (ที่เรารัน server บน localhost)
axios: {
baseURL: 'http://localhost:12345/api'
},
auth: {
strategies: {
local: {
endpoints: {
login: { url: 'login', method: 'post', propertyName: 'data.token' },
user: { url: 'me', method: 'get', propertyName: 'data.user' },
logout: false
}
}
},
redirect: {
login: '/login'
}
}
ลองเปิดหน้าเว็บ http://localhost:3000/login และลอง login เข้าสู่ระบบ ดูครับ
อย่าลืม activate Vuex store ด้วยนะครับ หากนึกไรไม่ออก ก็สร้างไฟล์ store/index.js
ข้างในมีแค่นี้ได้
export const state = () => ({
myState: 'Hello'
});
ต่อมาสร้างไฟล์ pages/private.vue
ขึ้นมา กำหนด middleware: 'auth'
ให้มันซะ เข้าถึงได้เฉพาะ login user.
<template>
<div>
<h1>THiS IS PRIVATE</h1>
<nuxt-link to="/">Go to Home</nuxt-link>
</div>
</template>
<script>
export default {
middleware: 'auth'
};
</script>
หน้า Index ลองเพิ่ม logic นิดนึง ถ้า login เรียบร้อย ให้แสดง Hello และมีปุ่ม Logout แต่ถ้าไม่ได้ login ก็มีปุ่ม Login ให้กดไปหน้า Login นั่นเอง
<template>
<div class="container">
<div>
<Logo />
<h1 class="title">nuxt auth example</h1>
<div class="links">
<a
href="https://nuxtjs.org/"
target="_blank"
rel="noopener noreferrer"
class="button--green"
>
Documentation
</a>
<a
href="https://github.com/nuxt/nuxt.js"
target="_blank"
rel="noopener noreferrer"
class="button--grey"
>
GitHub
</a>
</div>
<hr class="divider" />
<div v-if="loggedIn">
<h1>Hello, {{ user.email }}</h1>
<button @click="logout" class="button--grey">Logout</button>
</div>
<div v-else>
<nuxt-link to="/login" class="button--grey">Login</nuxt-link>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
user: this.$auth.user,
loggedIn: this.$auth.loggedIn
};
},
methods: {
async logout() {
await this.$auth.logout();
this.$router.push('/login');
}
}
};
</script>
<style>
.divider {
margin: 2em 0;
}
.container {
margin: 0 auto;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.title {
font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, sans-serif;
display: block;
font-weight: 300;
font-size: 100px;
color: #35495e;
letter-spacing: 1px;
}
.subtitle {
font-weight: 300;
font-size: 42px;
color: #526488;
word-spacing: 5px;
padding-bottom: 15px;
}
.links {
padding-top: 15px;
}
</style>
ทดลองเล่นใหม่ เข้าเว็บ http://localhost:3000 และลองดู flow การทำงานดูครับ
เราก็จะได้ concept คร่าวๆ ของการทำ Authentication ฝั่ง Nuxt.js กันนะครับ
Hints & Questions?
- เพื่อนๆ ลองนำไปประยุกต์ใช้งานกันดูนะครับ อาจจะเห็นว่า Tutorial รวบรัดหรือไม่ครบ แต่จริงๆ อยากให้เพื่อนๆ ได้ลองเล่น ลองทำด้วยตัวเองดู ติดปัญหาตรงไหน ก็ค่อยๆ งม ค่อยๆทำนะครับ
- ลองทำ Server API เอง หรือ หา API อื่นๆ เช่น Firebase Auth หรือ Auth0 มาใช้แทน Server API ดูครับ
- อยากเก็บ token แบบมี accessToken, refreshToken ทำยังไงนะ?
- ลองใช้ strategy อื่นๆ ดูเช่น Facebook หรือ Github
- ถ้า Server API เราเป็นแบบ session เราจะกำหนดยังไง disable token ได้มั้ย?
- ลองสังเกต localStorage ว่า token เก็บไว้ชื่อว่าอะไร เปลี่ยนชื่อ หรือเปลี่ยน prefix ยังไงนะ?
- ในตัวอย่าง ตอน
GET /me
ไม่ได้ส่ง Authorization header ไปด้วย ถ้าจะส่งไปด้วย ต้องไปกำหนดnuxt.config.js
ยังไง?