Создание системы аутентификации без пароля

Создание системы аутентификации без пароля

Существует новый способ встраивания аутентификации в ваше внутреннее приложение. Этот подход устраняет необходимость в пароле, что привело к появлению термина ”беспарольная аутентификация”. Системы беспарольной аутентификации становятся все более популярными, поскольку они обладают рядом преимуществ по сравнению с традиционными системами, основанными на паролях. Например, они обеспечивают более удобную работу с пользователями, снижают риск утечки данных и уменьшают затраты на управление паролями.

Система аутентификации без пароля может быть реализована различными способами, включая использование биометрической информации, одноразовых кодов или криптографии с открытым и закрытым ключом. Выбор оптимального варианта зависит от конкретных требований системы и пользователей. Каждый метод имеет свои преимущества и недостатки. В данной статье мы будем использовать одноразовый код, который будет отправлен на электронную почту пользователя.

Сначала давайте запустим наш проект npm

Выполним npm init —y, затем установим наши зависимости, для этого приложения мы будем использовать express.

npm i express

Давайте создадим наше приложение и запустим наш сервер.

// index.ts
import express from 'express';

const app = express();

app.listen(3000, () => console.log(`server running on port 3000`))

Далее мы настроим нашу базу данных с Mongoose,

Для этого сначала нужно установить Mongoose.

npm i mongoose

Нам необходимо настроить схему и модели mongoose, что мы и сделаем ниже;

// user.schema.js

import { Schema } from 'mongoose';

export const UserSchema = new Schema({
  email: {
    type: String,
    required: [true, "Please provide your email"],
    unique: true
  },
  fistName: String,
  lastName: String,
  otp: number;
}, {
  timestamps: true
})

Выше мы объявили схему пользователей, схема пользователей определяет некоторые основные свойства, которыми должен обладать пользователь, самое главное - электронная почта, которую мы установили обязательной для всех пользователей, мы также гарантируем, что два пользователя не могут иметь одинаковые электронные адреса, обеспечивая уникальность электронных адресов. Давайте создадим модель пользователя для обработки всей логики.

// user.model.js

import { UserSchema } from './user.schema';
import { model } from 'mongoose';

UserSchema.statics.createAccount = async function ({
  email, 
  firstName,
  lastName
}) {
  try {
    const user = await this.create({ 
      email,
      firstName,
      lastName
    })
    // Generate OTP for user
    const otp = Math.floor(Math.random() * 1000000);
    await user.updateOne({ otp });
    sendEmailSomehow(`Your otp is ${otp}`)
    return [user, null]
  } catch(error) {
    return [null, error.message]
  }
}

Приведенный выше фрагмент создает для нас нового пользователя, мы генерируем otp, который будет служить в качестве одноразового кода доступа, который мы будем использовать для верификации пользователя, мы используем службу электронной почты для отправки пользователю письма, я предполагаю, что у вас есть сторонняя служба, которая будет заниматься доставкой электронной почты.


UserSchema.statics.login = async function (email) {
  try {
    const user = await this.findOne({ email });
    const otp = Math.floor(Math.random() * 1000000);
    await user.updateOne({ otp });
    sendEmailSomehow(`Your otp is ${otp}`);
    return [user, null]
  } catch (error) {
     return [null, error.message]
  }
}

Этот фрагмент выше обрабатывает процесс входа в систему, мы проверяем, есть ли пользователь с электронной почтой, которая совпадает с указанной, если такой пользователь есть, мы создаем для него otp и отправляем его на его электронную почту. Давайте напишем функцию, которая будет проверять их otp.


UserSchema.statics.verifyOTP = async function (otp, email) {
  try {
    const user = await this.findOne({ email, otp });
    return [user, null];
  } catch (error) {
    return [null, error.message]
  }
}

export const Users = await model('user', UserSchema);

Теперь у нас есть базовая настройка, нам нужно импортировать модель Users в наши контроллеры, которые мы создадим ниже.

// user.controller.js

import { Users } from 'user.model';

export const createAccount = async (req, res) => {
  const { firstName, lastName, email } = req.body;
  const [user, err] = await Users.createAccount({
    firstName,
    lastName,
    email,
  });
  if (err) res.status(400).json({ error: err })
  res.status(201).json({ user })
}

У нас есть первый контроллер, который обрабатывает регистрацию пользователей, ниже мы настроим другие контроллеры для входа в систему и проверки нашего пользовательского токена.


export const login = async (req, res) => {
  const { email } = req.body;
  const [user, err] = await Users.login(email);
  if (err) res.status(400).json({ error: err });
  res.status(201).json({ user });
}

export const verifyOTP = async (req, res) => {
  const { otp } = req.body;
  const [user, err] = await Users.verifyOTP(otp);
  if (err) res.status(400).json({ error: err });
  res.status(201).json({ user });
}

Мы создали и экспортировали наши функции контроллера, теперь нам нужно сопоставить каждую функцию контроллера с маршрутом, это будет сделано внутри файла маршрута.

// router.js
import { createAccount, login, verifyOTP } from './user.controller';
import { Router } from 'express';

export const router = Router();

router.post('/register', createAccount);
router.post('/login', login);
router.post('/otp', verifyOTP);

Наше приложение уже готово, теперь нам нужно импортировать наш маршрутизатор и применить его к нашему приложению.

// index.js continued
import { router } from 'router';

app.use(router);

Запустите свое приложение

И у вас есть базовая настройка для системы аутентификации без пароля, хотите посмотреть, как мы можем сделать это, но с TypeORM и MySQL в качестве нашей базы данных и fastify в качестве нашего фреймворка.

Увидимся в следующей статье