Аутентификация
Руководство по работе с аутентификацией в Integ API.
Обзор
Integ API использует JWT (JSON Web Tokens) для аутентификации. После успешного входа вы получаете токен, который нужно отправлять в заголовке Authorization для доступа к защищенным эндпоинтам.
Flow аутентификации
sequenceDiagram
participant Client
participant API
participant DB
Client->>API: POST /auth/sign-in<br/>{email, password}
API->>DB: Проверка credentials
DB-->>API: User data
API->>API: Генерация JWT
API-->>Client: {accessToken, user}
Client->>API: GET /integrations<br/>Authorization: Bearer token
API->>API: Валидация токена
API->>DB: Получение данных
DB-->>API: Data
API-->>Client: ResponseРегистрация
POST /api/auth/sign-up
Создание нового пользователя.
Request:
POST /api/auth/sign-up
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123"
}Response (201 Created):
{
"id": "uuid",
"email": "user@example.com",
"verificationStatus": "pending",
"createdAt": "2025-01-01T00:00:00.000Z"
}Validation Rules:
email: валидный email адрес, уникальныйpassword: минимум 8 символов
Errors:
// 400 Bad Request - Email уже существует
{
"statusCode": 400,
"message": "User with this email already exists",
"error": "Bad Request"
}
// 400 Bad Request - Невалидные данные
{
"statusCode": 400,
"message": [
"email must be an email",
"password must be longer than or equal to 8 characters"
],
"error": "Bad Request"
}Вход
POST /api/auth/sign-in
Аутентификация пользователя и получение JWT токена.
Request:
POST /api/auth/sign-in
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123"
}Response (200 OK):
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "uuid",
"email": "user@example.com",
"verificationStatus": "verified"
}
}Errors:
// 401 Unauthorized - Неверные credentials
{
"statusCode": 401,
"message": "Invalid credentials",
"error": "Unauthorized"
}
// 400 Bad Request - Невалидные данные
{
"statusCode": 400,
"message": [
"email must be an email",
"password should not be empty"
],
"error": "Bad Request"
}Проверка токена
GET /api/auth/me
Получение информации о текущем пользователе.
Request:
GET /api/auth/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Response (200 OK):
{
"id": "uuid",
"email": "user@example.com",
"verificationStatus": "verified",
"createdAt": "2025-01-01T00:00:00.000Z"
}Errors:
// 401 Unauthorized - Токен отсутствует или невалидный
{
"statusCode": 401,
"message": "Unauthorized",
"error": "Unauthorized"
}Обновление профиля
PATCH /api/auth/me
Обновление профиля текущего пользователя.
Request:
PATCH /api/auth/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"email": "new-email@example.com"
}Response (200 OK):
{
"id": "uuid",
"email": "new-email@example.com",
"verificationStatus": "verified",
"createdAt": "2025-01-01T00:00:00.000Z"
}Использование JWT токена
В заголовке Authorization
Authorization: Bearer <your-jwt-token>Примеры
cURL
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -X GET http://localhost:3000/api/integrations \
-H "Authorization: Bearer $TOKEN"JavaScript
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
const response = await fetch("http://localhost:3000/api/integrations", {
headers: {
Authorization: `Bearer ${token}`
}
});Axios
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:3000/api"
});
// Установка токена для всех запросов
api.defaults.headers.common["Authorization"] = `Bearer ${token}`;
// Или для конкретного запроса
const response = await api.get("/integrations", {
headers: {
Authorization: `Bearer ${token}`
}
});JWT Token структура
Payload
Токен содержит следующую информацию:
{
"sub": "user-uuid",
"email": "user@example.com",
"iat": 1704067200,
"exp": 1704153600
}sub(subject) - ID пользователяemail- Email пользователяiat(issued at) - Время создания токенаexp(expiration) - Время истечения токена
Срок действия
По умолчанию токен действителен 24 часа.
Refresh Tokens (Будущее)
В будущих версиях будет добавлена поддержка refresh tokens для обновления access token без повторного входа.
Безопасность
Best Practices
✅ DO:
- Храните токен безопасно (localStorage, sessionStorage, cookies с httpOnly)
- Отправляйте токен только по HTTPS в production
- Обрабатывайте ошибки 401 (перенаправление на login)
- Очищайте токен при logout
❌ DON'T:
- Не храните токен в URL или query параметрах
- Не храните пароль на клиенте
- Не логируйте токен в консоль
- Не отправляйте токен через незащищенное соединение
Валидация токена
API автоматически проверяет:
- Подпись токена (signature)
- Срок действия (expiration)
- Формат payload
При невалидном токене возвращается 401 Unauthorized.
Защищенные эндпоинты
Все эндпоинты, кроме публичных, требуют JWT токен:
Публичные (без токена)
POST /api/auth/sign-upPOST /api/auth/sign-inGET /health
Защищенные (требуют токен)
GET /api/auth/mePATCH /api/auth/meGET /api/integrationsPOST /api/integrationsGET /api/core/secrets- И все остальные...
Примеры полного flow
React + TypeScript
import { useState } from 'react';
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:3000/api'
});
function AuthExample() {
const [token, setToken] = useState<string | null>(null);
const login = async (email: string, password: string) => {
try {
const { data } = await api.post('/auth/sign-in', { email, password });
setToken(data.accessToken);
// Установка токена для будущих запросов
api.defaults.headers.common['Authorization'] = `Bearer ${data.accessToken}`;
// Сохранение в localStorage
localStorage.setItem('token', data.accessToken);
return data;
} catch (error) {
console.error('Login failed:', error);
throw error;
}
};
const fetchIntegrations = async () => {
try {
const { data } = await api.get('/integrations');
return data;
} catch (error) {
if (error.response?.status === 401) {
// Токен истек или невалидный - перенаправить на login
setToken(null);
localStorage.removeItem('token');
}
throw error;
}
};
const logout = () => {
setToken(null);
localStorage.removeItem('token');
delete api.defaults.headers.common['Authorization'];
};
// Восстановление токена при загрузке
useEffect(() => {
const savedToken = localStorage.getItem('token');
if (savedToken) {
setToken(savedToken);
api.defaults.headers.common['Authorization'] = `Bearer ${savedToken}`;
}
}, []);
return (
// ... UI components
);
}Node.js Backend-to-Backend
import axios from "axios";
class IntegApiClient {
private token: string | null = null;
private api = axios.create({
baseURL: process.env.INTEG_API_URL
});
async authenticate() {
const { data } = await this.api.post("/auth/sign-in", {
email: process.env.INTEG_API_EMAIL,
password: process.env.INTEG_API_PASSWORD
});
this.token = data.accessToken;
this.api.defaults.headers.common["Authorization"] = `Bearer ${this.token}`;
}
async getIntegrations() {
if (!this.token) {
await this.authenticate();
}
try {
const { data } = await this.api.get("/integrations");
return data;
} catch (error) {
if (error.response?.status === 401) {
// Повторная аутентификация
await this.authenticate();
const { data } = await this.api.get("/integrations");
return data;
}
throw error;
}
}
}Troubleshooting
401 Unauthorized
Причины:
- Токен отсутствует
- Токен невалидный (поврежден, неправильная подпись)
- Токен истек
- Неправильный формат заголовка
Решение:
- Проверьте формат:
Authorization: Bearer <token> - Проверьте срок действия токена
- Повторите login для получения нового токена
CORS ошибки
Причины:
- Фронтенд на другом домене/порту
- Отсутствует заголовок Origin
Решение:
- Убедитесь, что
CLIENT_BASE_URLв environment настроен правильно - В development CORS открыт для всех (
*)
Следующие шаги
- Работа с интеграциями - CRUD операции
- Архитектура - Как устроена аутентификация внутри