npm package: @hackey/alfa-server
Description: ```bash npm i npm start ```
Latest version: 0.0.3

# Запуск сервера

```bash
npm i
npm start
```

```bash
# настройки по-умолчанию из settings.json
npx alfa-auth-server
```

```bash
# путь до файла настроек будет получени из переменной окружения AUTH_SETTING_PATH
export AUTH_SETTING_PATH=ПУТЬ_ДО_ФАЙЛА_НАСТРОЕК
npx alfa-auth-server
```

```bash
# путь до файла настроек будет получени из аргумента
npx alfa-auth-server -p ПУТЬ_ДО_ФАЙЛА_НАСТРОЕК
npx alfa-auth-server --settingPath ПУТЬ_ДО_ФАЙЛА_НАСТРОЕК
```

# Создание пары ключей RSA

> npm run generate-keys

Если в БД postgres уже есть пользователи, то нужно получить пару ключей с тествого стенда или от других участников проекта.
Иначе, вновь созданные пользователи будут проходить проверку пароля, а старые пользователи не будут.

# Сервис авторизации

Авторизацию предлагаю реализовать в виде отдельного сервиса.

Основная идея состоит в том, что это по сути маленькое отдельное Web приложение, которое само общается со своей базой данных, имеет отдельный Front-end и отдельный back-end.

Задача этого сервиса на выходе отдать JWT токен, подписанный RSA ключем.

### Зачем тут нужен RSA? Почему не использовать просто хеш, например SHA256

Он нужен для того чтобы сервис авторизации был действительно изолирован. Именно используя RSA наш alfadg не будет содержать в себе вообще каких-либо ключей, ему нужен только лишь адрес сервиса авторизации.

Он обращается по нему и получает

## Элементы авторизации

1. Нужно сделать отдельный **back-end** у которого будут следующие API

   1. GET publicKey

      1. Вход: нет
      2. Выход: публичный ключ RSA ключа используемый для верификации.
      3. Этот Api вызывается другими серверами. Они получают этот ключ и при помощи него верифицируют токены, которые им шлет браузер

   2. POST auth

      1. Вход: логин и пароль, useSspi, useLdap, useInternal - указывают на то, как именно авторизовывать пользователя.

      2. Выход: JWT токен подписанный закрытым RSA ключем. В данных JWT токена нужно сохранить

         1. login
         2. группы пользователя
         3. expire date - время действия токена - по умолчанию 7 дней

         Для авторизации используем LDAP.

         Если по LDAP не нашли или LDAP недоступен, то ищем в базе.

         Вместо паролей в локальную базу нужно сохранять SHA256 (хеш+соль+соль2), а также саму случайно сгенерированную соль. Соль2 - хранится в settings.json сервера авторизации.

         Если данный пользователь уже существует, он перезаписывается.

      3. Логирование авторизации

         1. Дата, время
         2. Логин
         3. Пароль
         4. IP адрес
         5. Клиент который запросил авторизацию
         6. TBD Артур, допиши сюда пожалуйста, что еще нужно логировать в соответствии с требованиями УИБ

   3. POST SaveUser

      1. Вход: логин, пароль, данные пользователя (членство в группах)
      2. Выход: "ок"
      3. Сохраняет в локальную SHA256 (хеш+соль+соль2), саму соль и данные пользователя. Соль2 - хранится в settings.json сервера авторизации
      4. Для вызова этого API пользователь должен быть членом группы AUTH_SERVER_ADMIN - то есть вместе с вызовом этого API должен быть передан JWT ключ содержащий данную группу
      5. Для использования SaveUser требуется скопировать front-end форму авторизации, но переименовать ее в "Регистрация".

2. Нужно сделать **front-end** с одной формой авторизации, - классическая, есть всего два поля логин и пароль.

   1. Первым делом пробуем sspi
   2. Если на sspi пришел отказ, пробуем обычную авторизацию
   3. После авторизации редиректим пользователя обратно. Для того чтобы знать что такое обратно, при редиректе на авторизацию используем параметр "back".

3. Набор функций для alfadg

   1. Функция initAuth для бэкэнда, которая получает имя сервера авторизации
   2. Функция verify для бэкэнда, которая верифицирует сам токен, истечение его срока действия.
   3. Код который редиректит клиента на сервис авторизации и поддержка обратного редиректа

4. В случае если сервис авторизации запускается без файла settings.json, он должен создать данный файл, также одновременно с этим он

5. В случае если settings.json отсутствует или если он есть, но в нем указан ключ "createUser":{"login":"LOGIN", "pass":"PASSWORD",data:{groups:[AUTH_SERVER_ADMIN ]}}, то создается данный пользователь, а файл settings.json пересохраняется но уже без данного ключа. Создание пользователя осуществляется функцией SaveUser.

### Принцип работы

1. alfadg ищет в заголовке или в cookie jwt токен. Если токена нет, он истек или не валиден, то редиректим пользователя на сервис авторизации, передавая параметр "back", чтобы он потом вернулся ну ту же страницу
2. Сервер авторизации, авторизует пользователя и отдает ему в браузер JWT токен, который браузер сохраняет в куки
   1. В этот момент он логирует, как успешные так и не успешные попытки авторизации
3. Сервер авторизации обратно редиректит пользователя на alfadg/back
4. Далее уже авторизованный пользователь работает в alfadg. Каждый запрос на сервер должен верифицироваться.
5. Естественно все соединения - https, поэтому никак шифровать передаваемые логины и пароли не нужно.
6. ВАЖНО ни в jwt токене, ни где либо еще нельзя и не нужно сохранять пароль

Основные принципы разработки

Все константы должны быть вынесены в отдельный файл - settings.json. Например речь идет про константу "Имя админской группы".

## Краткий ликбез по RSA, JWT, если вдруг не ясно что это

**JWT** - это json с любыми данными, подписанный ключем. В нашем случае нужно использовать RSA ключ.

JWT генерируется сервером, а подписывается клиентом.

Если кратко:

- Это json данные
- Которые подписаные ключем, в нашем случае асимметричным RSA ключем
- Эти данные передает клиент в Cookie или в заголовке

**RSA** - это набор ассимметричных крипто алгоритмов. В основе алгоритмов лежать пары ключей.

Если упростить, то один ключ позволяет только шифровать, другой - только расшифровать.

По одному ключу невозможно получить второй ключ, это гарантируется математикой лежащей под RSA алгоритмами.

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

В нашем случае мы публикуем ключ, которым можно проверить что данные хранящиеся внутри JWT действительно были подписаны вторым ключем.

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

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

**Важный момент:**

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

**Статьи по теме:**

https://stackoverflow.com/questions/38588319/understanding-rsa-signing-for-jwt

https://habr.com/ru/post/450054/

https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/

https://connect2id.com/products/nimbus-jose-jwt/vulnerabilities

https://github.com/auth0/node-jsonwebtoken