How to verify email by 6-digit-code in Flutter, Node JS and MongoDB?

Viewed 1349

What I am trying to achieve is the following:

I have got a register screen in which the user enters his email and a password. When "register" ist pressed, I want the user to receive an email with a random 6-digit-code which allows him to enter this code on the next page which is the verification screen and with that verify the email.

Everything is set up, but I can only find ways via a generated verification link.

The way I would like to go is:

  1. User enters credentials (email, password)
  2. When pressed on "register" this data is saved to the database under the users collection with an ID and an email with the code is sent.
  3. When this is done, the code can be entered and is somehow compared to the code that is saved somewhere in the database?? Because just sending the generated code to the next screen and store it in a class variable wouldn't be the best approach of achieving this I guess.
  4. After success the users "verified" field is set to true in the database.

I don't really know the best way of programming this.

Please do not bother me for the naming of the routes ;) It's just like that for the moment for simplicity. Because of the same reason I only send response codes of 400 when something went wrong.

Flutter (registration screen)

child: RawMaterialButton(
  onPressed: () async {
    setState(() => isLoading = true);

    if (email.length < 6) setState(() => isLoading = false);

    else if (password.length < 6) setState(() => isLoading = false);

    else {
      var registerRes = await _auth.registerWithEmailAndPassword(email, password);

      if (registerRes == 200) {
        var code = randomNumeric(6);
        var sendRes = await _auth.sendCode(email, code);

        if (sendRes == 200) {
          Routes.sailor.navigate(
            '/confirmation',
            params: {
              'email': email,
              'password': password,
              'code': code
            }
          );
        } else {
          setState(() => isLoading = false);
        }
      } else {
        setState(() => isLoading = false);
      }
    }
  },

  ...
)

Flutter (Auth class)

import 'package:http/http.dart' as http;
import 'dart:convert';

class AuthService {

  Future<String> loginWithEmailAndPassword(String email, String password) async {

    Map data = {
      'email': email,
      'password': password
    };

    String body = json.encode(data);

    var res = await http.post(
      'http://10.0.2.2:3000/api/users/login',
      headers: { 'Content-Type' : 'application/json'},
      body: body
    );

    if (res.statusCode == 200) return res.body;

    return null;
  }

  Future<int> registerWithEmailAndPassword(String email, String password) async {

    Map data = {
      'email' : email,
      'password': password
    };

    String body = json.encode(data);

    var res = await http.post(
      'http://10.0.2.2:3000/api/users/register',
      headers: { 'Content-Type' : 'application/json'},
      body: body
    );

    return res.statusCode;  
  }

  Future<int> sendCode(String email, String code) async {

    Map data = {
      'email' : email,
      'code': code
    };

    String body = json.encode(data);

    var res = await http.post(
      'http://10.0.2.2:3000/api/users/send',
      headers: { 'Content-Type' : 'application/json'},
      body: body
    );

    return res.statusCode;  
  }
}

Node JS (auth.js)

const router = require('express').Router();
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const { registerValidation, loginValidation } = require('../validation');
const User = require('../models/User');
const nodemailer = require('nodemailer');

router.post('/register', async (req, res) => {
    const { error } = registerValidation(req.body);

    if (error) return res.sendStatus(400);

    const emailExists = await User.findOne({ email : req.body.email });

    if (emailExists) return res.sendStatus(400);

    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(req.body.password, salt);

    const user = new User({
        email : req.body.email,
        password : hashedPassword
    });

    try {
        const savedUser = await user.save();
    } catch(err) {
        res.sendStatus(400);
    }
});

router.post('/send', async (req, res) => {

    const transporter = nodemailer.createTransport({
        service: 'gmail',
        auth: {
            user: process.env.EMAIL_ADDRESS,
            pass: process.env.EMAIL_PASSWORD
        },
        tls: {
            rejectUnauthorized: false
        }
    });

    const mailOptions = {
        from: '"coderave" <ccoderave@gmail.com>',
        to: req.body.email,
        subject: 'Bestätigungscode', 
        text: req.body.code
      };

    transporter.sendMail(mailOptions, function (err, info) {
        if (err) res.sendStatus(400);
        else res.sendStatus(200);
    });
});

module.exports = router;
1 Answers

Kindly use the NPM Package (two-step-auth)

Use this package in your server index.js and create a route for it, and directly pass in the variables and you can do the rest,

In your app get the data from the form and pass it to your server and then to the package and it will return you an OTP to work with,

Kindly check the full procedures with the example here

Usage

const {Auth} = require('two-step-auth');

async function login(emailId){
    const res = await Auth(emailId);
    // You can follw the above approach, But we recommend you to follow the one below, as the mails will be treated as important
    const res = await Auth(emailId, "Company Name");
    console.log(res);
    console.log(res.mail);
    console.log(res.OTP);
    console.log(res.success);
}

const app = express();
app.get('./auth/mailId/CompanyName', async(req, res)=>{
    const {emailId} = req.params;
    const data  = login(emailId);
})

Output

enter image description here This will help you a lot taking care of the process of verification under the HOOD.

Related