'How to inform single page application that email was verified?

I have vue js application running on localhost:3000 and express js restful api running on localhost:4000. Whenever new user registers, the request is sent to /api/v1/signup endpoint and after successfull registration verification email is sent with url including localhost:4000/api/v1/verify-email/:token

After user click link in the email how to inform vue js application that email validated? use socket or ...?

Register controller

async store(req, res) {
    let errors = {}
    let valid = false

    await signupSchema.validate({
      first_name: req.body.first_name?.trim(),
      last_name: req.body.last_name?.trim(),
      email: req.body.email?.trim(),
      password: req.body.password?.trim(),
      password_confirmation: req.body.password_confirmation?.trim(),
    }, { abortEarly: false })
      .then(() => valid = true)
      .catch((err) => {
        err.inner.map((inn) => errors[inn.path] = inn.errors)
      })

    if (!valid) return res.status(400).json({ errors: errors, message: 'Validation errors' }) 

    const data = {
      first_name: req.body.first_name.trim(),
      last_name: req.body.last_name?.trim(),
      email: req.body.email.trim(),
      password: bcrypt.hashSync(req.body.password, 12)
    }

    let id = null
    
    // create user
    await knex('users').insert(data)
      .then((res) => id = res[0])
      .catch((err) => {
        logger.log(err)
        return res.status(500).json({ error: err.message })
      })

    // create verification token
    const verificationToken = jwt.sign({ id: id }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: 3600 })

    await (new VerificationEmail(verificationToken, { id })).queue(data.email)

    const user = await knex('users')
      .select([ 'id', 'email', 'first_name', 'last_name', 'verified_at', 'deleted_at', 'created_at', 'updated_at' ])
      .where('id', id)
      .first()
      .then((res) => res)

    // sign jwt tokens
    const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: 60 })
    const refreshToken = jwt.sign(user, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '1h' })

    return res.json({ id: id, message: 'Successfully registered', accessToken, refreshToken })
  }

VerifyEmailController

class VerifyEmailController {
  /**
   * 
   * @param {*} req 
   * @param {*} res 
   */
  async store(req, res) {
    const user = await knex('users').where('id', req.params.id).first().then(res)
    if (!user) res.status(404).json({ message: 'User not found' })
    // validate token
    jwt.verify(req.params.token, process.env.ACCESS_TOKEN_SECRET, async (err, data) => {
      if (err) return res.status(401).json({ message: 'Invalid token' })
      if (data.id !== user.id) res.status(401).json({ message: 'Invalid token' })
      await knex('users').where('id', req.params.id)
        .update({ verified_at: new Date() })
      return res.json({ message: 'Your account verified. Thanks' })
    })
  }
}

VerificationEmail

class VerificationEmail {
  token;
  user;

  constructor(token, user) {
    this.token = token
    this.user = user
  }

  layout(content) {
    const str = fs.readFileSync(path.join(__dirname, '../../views/vendor/mail/default.ejs'), 'utf-8')
    return ejs.render(str, { content })
  }

  build() {
    const url = `${process.env.APP_URL}/api/v1/verify-email/${this.user.id}/${this.token}`
    const str = fs.readFileSync(path.join(__dirname, '../../views/email/verification.ejs'), 'utf-8')
    const content = ejs.render(str, { url })
    return this.layout(content)
  }

  async queue(to) {
    const html = this.build()
    return emailQueue.add({ to, subject: 'Verify email address', html })
  }
}

Signup page

enter image description here

enter image description here



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source