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

Представьте, что у вас уже есть пул пользователей Cognito, а затем давайте создадим клиент приложения. При создании клиента приложения не забудьте снять флажок «автоматически создавать секретный токен».

И сохраните информацию «Идентификатор клиента приложения» и «Идентификатор пула пользователей», мы будем использовать ее позже.

Создайте простой веб-сервис с помощью Express и Typescript

Сначала инициализируйте проект Node.js с помощью TypeScript:

$ npm init -y
$ npm install typescript -D
$ npx tsc -init

Я использую Node.js v12. Мой tsconfig.json выглядит так:

{
  "compilerOptions": {
    "target": "ES2018",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist",
    "sourceMap": true,
    "removeComments": true,
    "rootDir": "./src",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Затем создайте простой HTTP-сервер с express:

$ npm install express
$ npm install @types/express ts-node-dev

И создайте первый файл кода src/server.ts

import express from 'express';
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
  res.json({ message: 'Hello World!' });
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server is listening on :${port}`);
});

Затем обновите часть сценария package.json файла, чтобы создать несколько общих сценариев:

...
  "scripts": {
    "build": "tsc",
    "dev": "ts-node-dev src/server.ts"
  },
...

Сценарий dev запустит наш сервер и автоматически перезапустит процесс при изменении файла.

$ npm run dev

Теперь вы можете получить доступ к серверу через любое клиентское приложение HTTP:

$ curl -X GET --location "http://localhost:3000"
{"message":"Hello World!"}

Внедрить API аутентификации

У нас слишком много функций для работы с Cognito, например sign up, sign in, logout, update user attribute.... В этой истории мы просто реализуем некоторые «обязательные» функции, такие как «регистрация, подтверждение регистрации, вход в систему и токен доступа пользователя для получения профиля пользователя».

Создайте файл контроллера для хранения всех функций обработчика:

// src/user.controller.ts
import express from "express";
interface IUserController {
  signUp: express.Handler,
  signIn: express.Handler,
  confirmSignUp: express.Handler,
  getProfile: express.Handler,
}
const userController: IUserController = {
  signUp: (req, res) => {
    res.json(req.body);
  },
  signIn: (req, res) => {
    res.json(req.body);
  },
  confirmSignUp: (req, res) => {
    res.json(req.body);
  },
  getProfile: (req, res) => {
    res.json({});
  },
};
export default userController;

Примените функции обработчика к каждому маршруту:

// src/router.ts
import { Router} from "express";
import userController from "./user.controller";
const router = Router();
router.post('/signup', userController.signUp);
router.post('/signup/confirm', userController.confirmSignUp);
router.post('/signin', userController.signIn);
router.get('/profile', userController.getProfile);
export default router;

Наконец, обновите server.tsfile, чтобы зарегистрировать маршрутизатор в экспресс-приложении:

// src/server.ts
...
import router from "./router";
...
app.use('/api/v1/users', router);
const port = process.env.PORT || 3000;
...

Мы создаем новый файл, чтобы обернуть все функции Cognito. Для работы с сервисом Cognito мы используем amazon-cognito-identity-js.

$ npm install amazon-cognito-identity-js cross-fetch

src/cognito.user.pool.helper.ts

В этом классе мы используем две переменные среды USER_POOL_ID, CLIENT_ID для создания объекта CognitoUserPool.

Например, я настроил свой пул пользователей Cognito с использованием email в качестве имени пользователя, тогда функция signUp требует только email, password полей.

Я также конвертирую все функции в стиль Promise. Вы можете использовать promisify помощник для преобразования функции из стиля обратного вызова в стиль Promise и не забудьте bind этот контекст, если это необходимо. В нашем классе мы должны позаботиться о authenticateUser функции, она использует объект обратного вызова вместо функции.

Функция signUp возвращает строку - имя пользователя (также известную как адрес электронной почты), когда процесс успешен. Cognito отправит электронное письмо на адрес электронной почты новой учетной записи с кодом подтверждения, который используется для проверки новой учетной записи.

confirmSignUp в случае успеха возвращает строку «УСПЕХ».

signIn функция вернет токен доступа и обновит токен:

  • Объект токена: когда учетная запись уже подтверждена.
{
  "accessToken": "eyJraWQiOiJPZW1sNGFFZzk3Z...",
  "refreshToken": "eyJjdHkiOiJKV1QiLCJlbm..."
}

Теперь мы можем использовать эти функции в нашем контроллере. Давайте обновим src/user.contrller.ts:

Вы должны установить две переменные среды - USER_POOL_ID, CLIENT_ID для вашего терминала, на котором выполняется серверный процесс. Это означает, что вам нужно остановить текущий dev процесс и запустить его снова:

# I'm using mac os
$ export USER_POOL_ID=your_user_pool_id
$ export CLIENT_ID=your_client_app_id
$ npm run dev

Попробуйте создать новую учетную запись (вы можете использовать свое любимое клиентское приложение HTTP, например Postman):

# Sign Up
$ curl -X POST --location "http://localhost:3000/api/v1/users/signup" \
    -H "Content-Type: application/json" \
    -d "{
          \"password\": \"str0ngP@ssw0rd123\",
          \"email\": \"[email protected]\"
        }"
# Response, and you also get an email what includes verification code
{
  "message": "[email protected] is created."
}
# Confirm Sign Up
$ curl -X POST --location "http://localhost:3000/api/v1/users/signup/confirm" \
    -H "Content-Type: application/json" \
    -d "{
          \"email\": \"[email protected]\",
          \"code\": \"000000\"
        }"
# Response
{
  "message": "SUCCESS"
}
# Sign In
$ curl -X POST --location "http://localhost:3000/api/v1/users/signin" \
    -H "Content-Type: application/json" \
    -d "{
          \"password\": \"str0ngP@ssw0rd123\",
          \"email\": \"[email protected]\"
        }"
# Response
{
  "accessToken": "eyJraWQiOiJPZW1sNG...",
  "refreshToken": "eyJjdHkiOiJKV1QiLCJ..."
}

На стороне клиента вам необходимо хранить оба токена. Токен доступа используется для вызова защищенного API, токен обновления используется для получения нового токена доступа по истечении срока его действия (в моих настройках срок его действия истекает через 1 час).

Давайте посмотрим, как мы проверяем и используем токен доступа.

Мы будем использовать класс CognitoIdentityServiceProvider для проверки и получения информации по токену доступа. Сначала нам нужно установить пакет aws-sdk:

$ npm install aws-sdk

У нас будет слишком много API, к которым будут иметь доступ только аутентифицированные пользователи. Для проверки запроса нам понадобится функция промежуточного программного обеспечения. Функция промежуточного программного обеспечения проверит токен доступа, а также прикрепит данные пользователя к объекту запроса:

src/auth.middleware.ts

REGION переменная должна совпадать с регионом вашего пула пользователей когнитивных данных. Мы получаем токен доступа из заголовков запроса с помощью ключа authorization и используем этот токен для получения информации о пользователе. В этом примере я просто получаю id, email пользователя и прикрепляю эту информацию к объекту запроса.

Не забудьте зарегистрировать промежуточное ПО для аутентификации на маршрутизаторе:

// src/router.ts
...
import authMiddleware from './auth.middleware';
...
router.get('/profile', authMiddleware, userController.getProfile);
...

GET /profile - это частный маршрут, для доступа к нему требуется аутентификация.

Наконец, просто верните информацию о пользователе клиентской стороне:

// src/user.controller.ts
...
import { IAuthenticatedRequest } from './auth.middleware';
...
  getProfile: (req: IAuthenticatedRequest, res) => {
    res.json(req.user);
  },
...

Теперь вы можете попробовать использовать токен доступа:

$ curl -X GET --location "http://localhost:3000/api/v1/users/profile" \
    -H "Authorization: your_access_token"
# Response
{
  "id": "user-uuid-string",
  "email": "[email protected]"
}

Это все!

Заключение

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

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

Исходный код, использованный в этой статье, опубликован на Github.

Спасибо за чтение!

Больше контента на plainenglish.io