Categorygithub.com/Timasha/AuthService
module
0.0.0-20240623220637-d67abc5ce098
Repository: https://github.com/timasha/authservice.git
Documentation: pkg.go.dev

# README

AuthService

Терминология:

1)В фигурных скобках будут писаться переменные. Пример: {path}, {ip}. 2)Для ответа в виде ошибки используется связка переменных errorCode (int) и error (string). errorCode необходим для идентефикации ошибки без необходимости сравнивать строки, а error - это текст ошибки для последующего вывода/обработки.

Функциональные возможности

  1. Регистрация
    1. Handler endpoint "{ip}:{port}/register"
    2. Модель api: json. Формат: { "login":"", "password":"" }
    3. Модель ответа: json. Формат: { "error":"", "errorCode":0 }
    4. Возвращаемые ошибки: ErrUserAlreadyExists,ErrTooShortLoginOrPassword, ErrServiceInternal, ErrInvalidInput
    5. Вариант использования - регистрация пользователя по логину и паролю.
  2. Аутентификация
    1. Handler endpoint "{ip}:{port}/authenticate"
    2. Модель api: json. Формат: { "login":"", "password":"" }
    3. Модель ответа: json. Формат: { "error":"", "errorCode":0, "otpEnabled":false, "intermediateToken:"", "authInfo":{ "accessToken":"", "refreshToken":"", } }
    4. Возвращаемые ошибки: ErrUserNotExists,ErrTooShortLoginOrPassword, ErrServiceInternal, ErrInvalidInput
    5. Вариант использования - отправка логина и пароля, при корректности которых происходит либо получение токена для доступа к сервисам и токена для обновления токенов, в случае если для пользователя отключена двухфакторная аутентификация, либо отправляется otpEnabled = true и промежуточный токен, который необходим для продолжения аутентификации с помощью {ip}:{port}/authenticate/continue
  3. Продолжение аутентификации при использовании otp
    1. Handler endpoint "{ip}:{port}/authenticate/continue"
    2. Модель api: json. Формат: { "intermediateToken":"", "otpCode":"" }
    3. Модель ответа: json. Формат: { "error":"", "errorCode":0, "authInfo":{ "accessToken":"", "refreshToken":"", } }
    4. Возвращаемые ошибки: ErrInvalidOtp, ErrInvalidIntermediateToken, ErrExpiredIntermediateToken, ErrServiceInternal, ErrInvalidInput
    5. Вариант использования - после попытки аутентификации, пользователь отправляет промежуточный токен и код из приложения аутентификации. Если они валидны, то в ответ получает токен доступа к сервисам и токен для обновления токенов.
  4. Авторизация
    1. Handler endpoint "{ip}:{port}/authorize"
    2. Модель api: json. Формат: { "accessToken":"", "requiredRoleId":"" }
    3. Модель ответа: json. Формат: { "userId":"", "error":"", "errorCode":0 }
    4. Возвращаемые ошибки: ErrUserNotExists, ErrExpiredAccessToken, ErrInvalidAccessToken, ErrRoleHasNoAccess, ErrServiceInternal, ErrInvalidInput
    5. Вариант использования - отправка токена доступа полученного ранее, и необходимой для действия роли и проверка валидности токена, с отправкой uuid, если всё успешно и пустого uuid и ошибки, если токен не валиден.
  5. Обновление токенов
    1. Handler endpoint "{ip}:{port}/refresh"
    2. Модель api: json. Формат: { "accessToken":"", "refreshToken":"", }
    3. Модель ответа: json. Формат: { "accessToken":"", "refreshToken":"", "error":"", "errorCode":0 }
    4. Возвращаемые ошибки: ErrExpiredRefreshToken, ErrInvalidRefreshToken, ErrInvalidAccessToken, ErrUserNotExists, ErrServiceInternal, ErrInvalidInput
    5. Вариант использования - если токен доступа истек, но в остальных аспектах валиден, то клиент может отправить запрос и получить новые токены. Токены для обновления ходят парой, т.к. refresh токен привязан к access токену. В ответ отправляются новые токены.
  6. Включение otp для пользователя
    1. Handler endpoint "{ip}:{port}/otp/enable"
    2. Модель api: json. Формат: тело запроса не читается. Однако необходимо наличие заголовка запроса Authorization в формате: "Bearer {accessToken}"
    3. Модель ответа: json. Формат: { "error":"", "errorCode":0, "otpKey":"", "otpUrl":"", }
    4. Возвращаемые ошибки: ErrOtpAlreadyEnabled, ErrUserNotExists, ErrServiceInternal, ErrWrongAuthorizeMethod
    5. Вариант использования - если аутентифицированный пользователь захотел включить двухфакторную аутентификацию для своего аккаунта, то он отправляет пустой запрос со своим токеном доступа в заголовке auhtorize. В ответ он получает ключ otp, для ручного ввода в аутентификатор и ссылку, которую можно преобразовать в qr код для быстрого добавления кода в аутентификатор
  7. Выключение otp для пользователя
    1. Handler endpoint "{ip}:{port}/otp/disable"
    2. Модель api: json. Формат: тело запроса не читается. Однако необходимо наличие заголовка запроса Authorization в формате: "Bearer {accessToken}"
    3. Модель ответа: json. Формат: { "error":"", "errorCode":0, }
    4. Возвращаемые ошибки: ErrUserNotExists, ErrOtpAlreadyDisabled, ErrServiceIntenal, ErrWrongAuthorizeMethod
    5. Вариант использования - если аутентифицированный пользователь захотел выключить двухфакторную аутентификацию для своего аккаунта, то он отправляет пустой запрос со своим токеном доступа в заголовке auhtorize. В случае успеха ему отправляется error="" и errorCode=0
Для всех запросов необходим заголовок Content-Type=application/json Во всех случаях в ответе имеются поля error и errorCode, которые предназначены для обработки ошибок на стороне клиента. В случае корректной работы отправляется error="" и errorCode=0. В случае неудачи по коду можно будет определить тип ошибки, например истекший токен или не валидный токен. Также есть одна ошибка обозначающая некорректную работу сервера, которая возвращается при неожиданной ошибке в процессе обработки запроса. Все коды ошибок смотрите в пункте пользовательские ошибки и их коды.

База данных:

Если таблиц нет, то они создаются запросом (файлы находится в папке с миграциями и имеет название user_storage_up.sql и roles_storage_up.sql):

create table if not exists users (
    UserId text PRIMARY KEY,
    Login text UNIQUE,
    Password text,
    OtpEnabled boolean,
    OtpKey text,
    RoleId bigserial REFERENCES roles (RoleId)
);
create table if not exists roles (
    RoleId bigserial PRIMARY KEY,
    RoleName text UNIQUE
);

Таблица Users:

UserId - это UUID, который используется как внешний ключ, который возвращается при авторизации. С помощью него сервисы, которым необходимо авторизовать пользователя привязывают свои данные к модели пользователя. Login и Password - это поля для регистрации и аутентификации. RoleId - внешний ключ для связи с таблицой ролей. OtpEnabled - логическая переменная, говорящая включена ли двухфакторная аутентификация для пользователя. OtpKey - ключ для генерации кода двухфакторной аутентификации для сравнения на стороне сервера.

Таблица Roles

RoleId - число идентефикатор роли. RoleName - имя роли (для понимания людьми).

На данный момент используется база данных postgresql, для нее написан provider, реализующий интерфейс UserStorage и RolesStorage.

Архитектура проекта

Проект выполнен с использованием луковичной архитектуры. Самая высокоуровневая структура - это LogicProvider. Он является основной логикой проекта. Зависит от шести интерфейсов: UserStorage - хранилище пользователей, предоставляющее методы CRUD; RolesStorage - хранилище ролей, предоставляющее методы CRUD; PasswordHasher - хэшер паролей, предоставляющий методы хэширования и сравнения паролей; UUIDProvider - интерфейс предоставляющий доступ к генерации UUID; TokensProvider - интерфейс предоставляющий доступ к генерации и валидации access и refresh токенов; OtpGenerator - интерфейс предоставляющий доступ к генерации otp (используется для двухфакторной аутентификации).

Уровнем ниже идет CasesProvider. Он является прослойкой, которая отвечает за проверку полей и отклонение всех запросов по истечению контекста. Зависит от LogicProvider, интерфейса UseCasesConfig - интерфейсу предоставляющим методы чтения параметров для проверки данных ввода и от интерфейса логгирования предоставляющий доступ к логгированию в несколько источников.

Уровнем ниже идёт API, который использует CasesProvider. Задача слоя API десериализовать данные в аргументы для методов CasesProvider, а также сериализовать данные ответа. На данный момент реализованы методы API для фреймворка Fiber.

Функция main инициализирует все зависимости, и запускает API.

Развертывание проекта

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

Тестирование

На данный момент написаны тесты для модуля CasesProvider без использования otp. Планируется написание интеграционных тестов.

Структура конфига

{
	"organizationName":"",
	
    "apiPort":"8080",

	"minLoginLen":5,
	"minPasswordLen":5,

	"accessTokenKey":"",
	"accessTokenLifeTime":60,

	"refreshTokenKey":"",
    "refreshTokenLifeTime":24,
	"accessPartLen":5,

	"intermediateTokenKey":"",
	"intermediateTokenLifetime":5,

	"postgresConfig":{
		"login":"some_user",
		"password":"some_password",
		"ip":"db",
		"port":"5432"
	},
	"migrationsPath":"./migrations/postgres",
	"roles":[{
		"roleId":1,
		"roleName":"root"
	},{
		"roleId":2,
		"roleName":"user"
	}],
	"defaultRoleId":2
}

Элементы конфига можно разделить на изменяемые и привязанные к развертыванию Docker контейнера. Изменяемые:

  1. organizationName - переменная, которая используется при генерации ссылки для добавления otp в аутентификатор (отображается в аутентификаторе)
  2. minLoginLen - минимальная длина логина проверяемая во всех методах запрашивающих логин.
  3. minPasswordLen - минимальная длина пароля проверяемая во всех методах запрашивающих пароль.
  4. accessTokenKey - ключ, использующийся для шифрования и валидации access токена.
  5. accessTokenLifeTime - время жизни (в минутах) access токена.
  6. refreshTokenKey - ключ, использующийся для шифрования и валидации refresh токена.
  7. refreshTokenLifeTime - время жизни (в часах) refresh токена.
  8. accessPartLen - длина части токена используемая для связывания access и refresh токена
  9. intermediateTokenKey - ключ, использующийся для шифрования и валидации intermediate токена
  10. intermediateTokenLifeTime - время жизни (в минутах) intermediate токена
  11. postgresConfig - конфиг для взаимодействия с postgres. Этого поля может не быть в случае использования другой базы данных (однако пока что не написаны provider для других баз данных).
    1. login - логин пользователя postgresql.
    2. password - пароль пользователя postgresql.
    3. ip - домен/ip сервера postgresql.
    4. port - порт сервера postgresql.
  12. roles - массив ролей, которые будут действовать при работе сервера. По умолчанию добавлены 2 роли root и user.
  13. defaultRoleId - роль, которая выдается по умолчанию для регистрирующегося пользователя.
Привязанные к развертыванию Docker контейнера:
  1. apiPort - порт сервера, внутри Docker контейнера. В описании образа Dockerfile открывается через EXPOSE 8080
  2. migrationsPath - путь внутри Docker контейнера с необходимыми файлами миграций. Копируется из папки проекта.

Пользовательские ошибки и их коды

  1. ErrServiceInternal - ошибка, возвращаемая при неожиданной ошибке в работе сервера. Код - 1.
  2. ErrExpiredAccessToken - ошибка, возвращаемая при истечении срока действия токена доступа к сервисам. Код - 101
  3. ErrExpiredRefreshToken - ошибка, возвращаемая при истечении срока действия токена для обновления токенов. Код - 102
  4. ErrExpiredIntermediateToken - ошибка, возвращаемая при истечении срока действия токена для продолжения аутентификации с помощью otp. Код - 103
  5. ErrInvalidPassword - внутренняя ошибка сигнализирующая при вводе неправильного пароля. Не возвращается в api, т.к. преобразуется в ErrInvalidLoginOrPassword. Код - 104
  6. ErrInvalidAccessToken - ошибка, возвращаемая при невалидности токена доступа к сервисам. Код - 105
  7. ErrInvalidRefreshToken - ошибка, возвращаемая при невалидности токена для обновления токенов. Код - 106
  8. ErrInvalidIntermediateToken - ошибка возвращаемая при невалидности токена для продолжения аутентификации с помощью otp. Код - 107
  9. ErrUserAlreadyExists - ошибка возвращаемая, если запрошенный пользователь уже зарегистрирован в системе. Код - 108
  10. ErrUserNotExists - ошибка возвращаемая, если запрошенный пользователь не зарегистрирован. Код - 109
  11. ErrInvalidOtp - ошибка возвращаемая, если пришедший otp код не валиден в текущий промежуток времени. Код - 110
  12. ErrRoleHasNoAccess - ошибка возвращаемая, если пользователь не имеет прав для выполнения операции. Код - 111
  13. ErrRoleAlreadyExists - внутренняя ошибка сигнализирующая о том, что роль уже зарегистрирована в системе. Не возвращается в api. Код - 112
  14. ErrRoleNotExists - ошибка возвращаемая, если запрошенная роль не зарегистрирована в системе. Код - 113
  15. ErrOtpAlreadyEnabled - ошибка возвращаемая, если для запрошенного пользователя уже включена двухфакторная аутентификация. Код - 114
  16. ErrOtpAlreadyDisabled - ошибка возвращаемая, если для запрошенного пользователя уже выключена двухфакторная аутентификация
  17. ErrInvalidLoginOrPassword - ошибка возвращаемая, если логин или пароль введенные пользователем при аутентификации не правильные. Код - 201
  18. ErrTooShortLoginOrPassword - ошибка возвращаемая, если введенные логин или пароль пользователя слишком короктие. Код - 202
  19. ErrInvalidInput - ошибка возвращаемая, когда не получилось прочитать ввод пользователя. Код - 301
  20. ErrWrongAuthorizeMethod - ошибка возвращаемая, когда метод авторизации не соответствует выбранному системой (В данном случае заголовок Authorization не существует или не соответствует формату "Bearer {accessToken}"). Код - 302

# Packages

No description provided by the author
No description provided by the author
No description provided by the author