Skip to content

Работа с Access Tokens

Руководство по управлению токенами доступа. Access Tokens используются для программного доступа к API и привязаны к пользователю.

Обзор

Access Token - это специальный токен для аутентификации API запросов через заголовок x-access-token. В отличие от JWT токенов (для веб-интерфейса), Access Tokens используются для машина-к-машине (M2M) коммуникации.

mermaid
sequenceDiagram
    participant Client as Внешний клиент
    participant API as Integ API
    participant DB as БД

    Client->>API: POST /api/access-tokens<br/>(создать новый токен)
    API->>DB: Сохранить токен
    DB-->>API: Token saved

    API-->>Client: {token, name, createdAt}

    Client->>API: GET /api/integrations<br/>x-access-token: token
    API->>API: Проверить токен
    API->>DB: Получить данные
    DB-->>API: Data
    API-->>Client: Response

Эндпоинты

POST /api/access-tokens

Создать новый токен доступа.

Request:

http
POST /api/access-tokens
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "name": "My Integration Token"
}

Parameters:

  • name (optional) - Название токена для удобства (например, "Telegram Integration", "Analytics Service")

Response (201 Created):

json
{
	"id": "550e8400-e29b-41d4-a716-446655440000",
	"token": "at_1234567890abcdefghijklmnopqrstuvwxyz",
	"name": "My Integration Token",
	"createdAt": "2025-01-15T10:30:00.000Z"
}

ВАЖНО

Сохраните токен в безопасном месте! Он больше не будет отображаться.

Errors:

json
// 401 Unauthorized - JWT токен отсутствует или невалидный
{
  "statusCode": 401,
  "message": "Unauthorized",
  "error": "Unauthorized"
}

// 400 Bad Request - Невалидные данные
{
  "statusCode": 400,
  "message": "Name is required",
  "error": "Bad Request"
}

GET /api/access-tokens

Получить список всех токенов пользователя.

Request:

http
GET /api/access-tokens
Authorization: Bearer <jwt-token>

Response (200 OK):

json
[
	{
		"id": "550e8400-e29b-41d4-a716-446655440000",
		"name": "My Integration Token",
		"createdAt": "2025-01-15T10:30:00.000Z",
		"lastUsedAt": "2025-01-15T12:00:00.000Z"
	},
	{
		"id": "660e8400-e29b-41d4-a716-446655440001",
		"name": "Backup Token",
		"createdAt": "2025-01-10T08:15:00.000Z",
		"lastUsedAt": null
	}
]
  • lastUsedAt - Время последнего использования токена (null если не использовался)

DELETE /api/access-tokens/:tokenId

Отозвать (удалить) токен доступа.

Request:

http
DELETE /api/access-tokens/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer <jwt-token>

Response (200 OK):

json
{
	"message": "Token revoked successfully"
}

Errors:

json
// 404 Not Found - Токен не найден
{
  "statusCode": 404,
  "message": "Token not found",
  "error": "Not Found"
}

// 403 Forbidden - Пользователь пытается удалить чужой токен
{
  "statusCode": 403,
  "message": "Access denied",
  "error": "Forbidden"
}

Использование Access Token

В заголовке x-access-token

http
x-access-token: at_1234567890abcdefghijklmnopqrstuvwxyz

Примеры

cURL

bash
TOKEN="at_1234567890abcdefghijklmnopqrstuvwxyz"

curl -X GET http://localhost:3000/api/integrations \
  -H "x-access-token: $TOKEN"

JavaScript / Fetch

javascript
const token = "at_1234567890abcdefghijklmnopqrstuvwxyz";

const response = await fetch("http://localhost:3000/api/integrations", {
	headers: {
		"x-access-token": token
	}
});

const data = await response.json();

Axios

typescript
import axios from "axios";

const api = axios.create({
	baseURL: "http://localhost:3000/api"
});

// Для всех запросов
api.defaults.headers.common["x-access-token"] = "at_1234567890...";

// Или для конкретного запроса
const response = await api.get("/integrations", {
	headers: {
		"x-access-token": "at_1234567890..."
	}
});

Node.js HTTP Client

typescript
import axios from "axios";

class IntegApiClient {
	private token: string;
	private api = axios.create({
		baseURL: process.env.INTEG_API_URL
	});

	constructor(accessToken: string) {
		this.token = accessToken;
		this.api.defaults.headers.common["x-access-token"] = accessToken;
	}

	async getIntegrations() {
		try {
			const { data } = await this.api.get("/integrations");
			return data;
		} catch (error) {
			if (error.response?.status === 401) {
				throw new Error("Access token is invalid or expired");
			}
			throw error;
		}
	}

	async invokeHandler(handlerId: string, args?: Record<string, any>) {
		const { data } = await this.api.post(`/handlers/${handlerId}/invoke`, { args });
		return data;
	}
}

// Использование
const client = new IntegApiClient(process.env.INTEG_API_TOKEN);
const integrations = await client.getIntegrations();

Best Practices

Безопасность

DO:

  • Храните токен в защищённом хранилище (environment переменные, vault, secrets manager)
  • Используйте разные токены для разных приложений/сервисов
  • Регулярно ротируйте токены
  • Отзывайте токены, которые больше не нужны
  • Использование HTTPS в production

DON'T:

  • Не храните токен в исходном коде (git repository)
  • Не отправляйте токен через незащищённое соединение (HTTP)
  • Не логируйте токены в сообщения
  • Не делитесь токеном между приложениями

Управление

typescript
// ✅ Good - разные токены для разных целей
const telegramToken = process.env.INTEG_TELEGRAM_TOKEN;
const slackToken = process.env.INTEG_SLACK_TOKEN;

// ✅ Good - использование конфигурации
const integApi = axios.create({
	baseURL: process.env.INTEG_API_URL,
	headers: {
		"x-access-token": process.env.INTEG_API_TOKEN
	}
});

// ❌ Bad - токен в коде
const token = "at_1234567890abcdefghijklmnopqrstuvwxyz";

// ❌ Bad - один токен для всего
const globalToken = "shared-token-for-all-integrations";

Полный пример - M2M интеграция

typescript
import axios from "axios";

// Инициализация клиента
const integApi = axios.create({
	baseURL: "http://localhost:3000/api",
	headers: {
		"x-access-token": process.env.INTEG_API_TOKEN
	}
});

// Получение интеграций
async function listIntegrations() {
	try {
		const { data } = await integApi.get("/integrations");
		return data;
	} catch (error) {
		console.error("Failed to list integrations:", error.response?.data);
		throw error;
	}
}

// Получение secrets
async function getSecrets(integrationName: string) {
	try {
		const { data } = await integApi.get(`/core/secrets?integration=${integrationName}`);
		return data;
	} catch (error) {
		console.error(`Failed to get secrets for ${integrationName}:`, error.response?.data);
		throw error;
	}
}

// Вызов handler
async function invokeHandler(handlerId: string, args: Record<string, any>) {
	try {
		const { data } = await integApi.post(`/handlers/${handlerId}/invoke`, { args });
		return data;
	} catch (error) {
		console.error(`Failed to invoke handler ${handlerId}:`, error.response?.data);
		throw error;
	}
}

// Использование
async function main() {
	// 1. Получить интеграции
	const integrations = await listIntegrations();
	console.log("Available integrations:", integrations);

	// 2. Получить secrets Telegram
	const secrets = await getSecrets("telegram");
	console.log("Telegram secrets:", secrets);

	// 3. Вызвать handler (используя ID handler'а)
	const result = await invokeHandler("handler-uuid-1", {
		message: "Hello from M2M integration!",
		chatId: "123456"
	});
	console.log("Handler result:", result);
}

main().catch(console.error);

Troubleshooting

401 Unauthorized

Причины:

  • Token отсутствует или неправильный
  • Token истёк
  • Неправильный формат заголовка

Решение:

bash
# Проверьте токен в переменной окружения
echo $INTEG_API_TOKEN

# Проверьте формат заголовка
curl -H "x-access-token: your-token" http://localhost:3000/api/integrations

# Создайте новый токен если старый потерян

403 Forbidden

Причины:

  • Пытаетесь удалить чужой токен
  • Недостаточно прав

Решение:

  • Убедитесь, что используете правильный токен
  • Проверьте, что пытаетесь делать именно с вашим токеном

Дополнительно