Published at: 2020-09-06 16:00:00
Updated at: 2023-10-01 13:59:37
Honestly speaking, I don't know much. Since this is my first time implementing it into my own Portfolio Version 3 website. Before this, using PHP-based framework like Yii2, a simple credential cross checking with database would be enough. Maybe I might also be mistaken here. Since most of the time, my employer gave me tasks that don't really have direct relation with authentication itself. That's why I don't really know much about authentication and how to properly code them. To make sure our system or website is safe and secure.
With that being said, this article might not be the one you want to read and follow. But, for learning sake, for me, I would be really appreciate if you can leave a comment below if you find something that can be improve or change. Anyway, let's get started!
JWT is a jsonwebtoken. Simply just a way of you holding a data (might also be sensitive data. Sometimes not at all) in a token-based. You will be combining your data with:-
With this, you can make your authentication process more secure and safe. To know more about JWT itself, you can go here.
There's a bunch of reasons why people go for token-based authentication. But the most popular ones according to the article mentioned above are:-
If you searched for this, you would find a lot of ways how to do this. But like I said earlier. I am too, still new to this token-based authentication. So this is my way of how to implement JWT-based authentication.
Below, I'm using Node JS as my back-end framework of choice.
List of steps:-
accessToken
and refreshToken
accessToken
and refreshToken
in databaseaccessToken
as queryaccessToken
firstrender
home pageSince I will be using MongoDB as my database of choice. Then below are the scheme for the model:-
All.js
/* Dependencies */
// 1. Mongoose
const mongoose = require('mongoose')
// Data Scheme
const allScheme = new mongoose.Schema({
// User's Credential
credentials: {
// User's Email
email: { type: String, required: true, min: 6 },
// User's password
password: { type: String, required: true, min: 6 }
},
// User's Blacklist Token
blacklist: [{
// User's Access Token
accessToken: { type: String, required: true },
// User's Refresh Token
refreshToken: { type: String, required: true },
// User's Status Token
status: { type: Number, required: true, default: 0 },
// User's Date&Time Token Creation
lastUpdate: { type: String, required: true }
}],
// User's Information
infos: // user informations
})
// Data Scheme Export
module.exports = mongoose.model('All', allScheme)
Now we will create the main javascript file in Node JS:-
server.js
/* Dependencies */
// 1. Express
const express = require('express')
const app = express()
// 2. Cors
const cors = require('cors')
// 3. Mongoose
const mongoose = require('mongoose')
// 4. Routes
const authRoutes = require('../routes/auth')
const homeRoutes = require('../routes/home')
/* Global Middlewares */
// EJS Application
app.set('view engine', 'ejs')
// Cors
app.use(cors())
// JSON body-parser
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
// Static files (Images, CSS and JavaScript)
app.use(express.static('web'))
/* Routes Middleware */
// AUTH Routes
app.use('/auth', authRoutes)
// HOME Routes
app.use('/', homeRoutes)
/* Database Connection & Server Startup */
mongoose.connect(
'mongodb+srv://username:password@clustername-lvnpm.mongodb.net/dbname?retryWrites=true&w=majority',
{ useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }
)
// if connection SUCCESS, then START the Server
.then(() => {
console.log('Successfully connected to database!')
app.listen(3000, console.log('Server is up and running at PORT 3000!'))
})
// if connection UNSUCCESSFUL, then DON'T START the Server
.catch(err => {
console.log(err)
})
This is where we will do the 1st till the 3rd steps mentioned above.
auth.js
/* Dependencies */
// 1. Express & Routes
const router = require('express').Router()
// 2. Mongoose
const mongoose = require('mongoose')
// 3. Model
const All = require('../model/All')
// 4. JWT
const jwt = require('jsonwebtoken')
// 5. Hashing Password
const bcrypt = require('bcryptjs')
/* Routes */
// LOGIN Post Router
router.post('/', async(req, res) => {
// check email existance
const userCredential = await All.findOne({ 'credentials.email': req.body.email })
if(!userCredential) return res.json({ message: `Email deosn't exist!` })
// check password match
const validPassword = await bcrypt.compare(req.body.password, userCredential.credentials.password)
if(!validPassword) return res.json({ message: `Invalid password! Make sure you insert the correct password.` })
// 1st STEP: assign token for user
let secretKey = 'youCanPutAnyStringOrRandomStringInHere'
// - access token: will automatically expire in 24 hours
const accessToken = jwt.sign({ _id: userCredential._id}, secretKey, { expiresIn: '24h' })
// - refresh token
const refreshToken = jwt.sign({ _id: userCredential._id}, secretKey)
// 2nd STEP: save user token in DB - blacklist
let dateTime = new Date()
const query = { _id: userCredential._id }
const update = { $push: { blacklist: { accessToken: accessToken, refreshToken: refreshToken, status: 0, lastUpdate: dateTime.toUTCString() } } }
const options = { upsert: true, new: true }
All.updateOne(query, update, options)
.then(response => {
// 3rd STEP
return res.redirect('/?accesstoken=' + accessToken)
})
.catch(err => res.json({ message: 'trouble saving tokens in DB!', data: err }))
// res.json({ message: 'Everything works fine!', data: userCredential._id })
})
/* Auth Routes Export */
module.exports = router
Next, in Home routes javascript file. We will first verify user existance(ex: router.get('/', verifyUser, async(req, res) => {})
) then only after that we will render the Home page.
home.js
/* Dependencies */
// 1. Express & Routes
const router = require('express').Router()
// 2. Mongoose
const mongoose = require('mongoose')
// 3. Model
const All = require('../model/All')
// 4. JWT
const jwt = require('jsonwebtoken')
// 5. Verification
const { verifyUser } = require('../controller/verification')
/* Routes */
// HOME get Router
router.get('/', verifyUser, async(req, res) => {
// get User's info data
const userInfo = await All.findOne({ '_id': req.user._id })
if(!userInfo) return res.json({ message: `User doesn't exist!` })
// STEP 5: render home page
res.render('home', { userInfo: userInfo })
})
/* Auth Routes Export */
module.exports = router
Below are the javascript file that handles the verification process:-
verification.js
/* Dependencies */
// 1. JWT
const jwt = require('jsonwebtoken')
// STEP 4: verify user before tasking
function verifyUser(req, res, next) {
const accessToken = req.query.accesstoken
if(!accessToken) return res.json({ message: 'Access Denied!' })
try {
let secretKey = 'youCanPutAnyStringOrRandomStringInHere'
// verify the exist token
const varified = jwt.verify(accessToken, secretKey)
req.user = varified
next()
} catch(err) {
return res.json({ message: 'Invalid token!' })
}
}
module.exports = {
verifyUser
}
That sum up everything! So with this, you can basically check or verify user access first before allowing any user to access your website content. You just need to specify verifyUser
in any routes (Ex: router.get('/', verifyUser, async(req, res) => {})
) that you deem need to have the authentication of the said user. Simple as that.
By the way, if you find it hard to follow through this article. I also did provide the video version of it(much detailed). You can go to this link here, (How to implement JWT-based Authentication.
Anyway, I hope this helps you a little bit in understanding token-based authentication and how to implement them.
As always, thanks for being here. Until next time, Chao! Salam.