Is Jwt Authentication still valid?

Published at: 2020-09-06 16:00:00

Updated at: 2023-10-01 13:59:37

jwt

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!

What is JWT

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:-

  1. a secret key (you can come up on your own or just use randomize string),
  2. a time frame or expiration time to be exact,
  3. and other specific data (user roles, permission and other resources)

With this, you can make your authentication process more secure and safe. To know more about JWT itself, you can go here.

Why use JWT

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:-

  1. Token are stateless - The token is self-contained and contains all the information it needs for authentication. This is great for scalability as it frees your server from having to store session state like normal session-based authentication.
  2. Token can be generated from anywhere - Token generation is decoupled from token verification allowing you the option to handle the signing of tokens on a separate server or even through a different company(vast authentication flexibility).
  3. Fine-grained access control - Within the token payload you can easily specify user roles and permissions as well as resources that the user can access.

Implementation of JWT

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:-

  1. create an accessToken and refreshToken
  2. save accessToken and refreshToken in database
  3. redirect user to home page with accessToken as query
  4. verify accessToken first
  5. render home page

Since 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.