Автентикация
SMSBAT ChatHub API използва система за удостоверяване на две нива, базирана на JWT токени с токени на компания (company token) и токени на оператор (operator token).
Работен процес на автентикация
graph TD
A[Идентификационни данни] --> B[Вземане на Company Token]
B --> C[Използване на Company Token]
C --> D[Вземане на Operator Token]
D --> E[Използване на Operator Token]
E --> F[Валидиране на Token]
Company Token (Токен на компания)
Токените на компания предоставят достъп на ниво организация до ChatHub API.
Получаване на Company Token
Крайна точка (Endpoint): POST /api/company/get-token
Заявка:
curl -X POST https://chatapi.smsbat.com/api/company/get-token \
-H "Content-Type: application/json" \
-d '{
"login": "your-company-login",
"password": "your-company-password"
}'
Тяло на заявката:
Отговор:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
Отговорът е стринг на JWT токена.
Използване на Company Token
Включете токена на компанията в API заявките, като използвате един от двата метода:
Метод 1: Authorization Header (Заглавка - Препоръчително)
curl -X GET https://chatapi.smsbat.com/api/company/organization \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Метод 2: X-Authorization-Key Header
curl -X GET https://chatapi.smsbat.com/api/company/organization \
-H "X-Authorization-Key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Operator Token (Токен на оператор)
Токените на оператор предоставят специфичен за потребителя достъп за отделни оператори в рамките на организацията.
Получаване на Operator Token
Крайна точка (Endpoint): POST /api/operator/get-token
Заявка:
curl -X POST https://chatapi.smsbat.com/api/operator/get-token \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 123,
"expiresAt": "2025-12-31T23:59:59Z"
}'
Тяло на заявката:
Параметри:
| Параметър | Тип | Задължителен | Описание |
|---|---|---|---|
id |
integer | Да | ID на оператора |
expiresAt |
string (ISO 8601) | Да | Дата и час на изтичане на токена (максимум 24 часа от момента) |
Важно: Максималният живот на токена е 24 часа. Параметърът expiresAt не може да бъде повече от 24 часа в бъдещето.
Отговор:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcGVyYXRvcl9pZCI6MTIzLCJleHAiOjE3Mzc0MTI3OTl9.example_signature"
Използване на Operator Token
Включете токена на оператора в API заявките:
Валидиране на токен (Token Validation)
Проверете дали даден токен все още е валиден, преди да го използвате.
Крайна точка (Endpoint): POST /api/operator/validate-token
Заявка:
curl -X POST https://chatapi.smsbat.com/api/operator/validate-token \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'
Тяло на заявката:
Отговор (Валиден токен):
{
"isValid": true,
"operatorId": 123,
"clientId": 0,
"expiresAt": "2025-12-31T23:59:59Z",
"error": null
}
Отговор (Невалиден токен):
Изтичане на токен
Company Токени
- Няма изрично изтичане в API
- Свържете се с вашия акаунт мениджър за политики относно жизнения цикъл на токена
- Ротирайте (сменяйте) токените периодично за сигурност
Operator Токени
- Изтичането се задава при заявяване на токена (параметър
expiresAt) - Валидирайте (проверете) токените преди употреба
- Поискайте нови токени преди изтичането им
Примери за внедряване
Python
import requests
from datetime import datetime, timedelta
class ChatHubAuth:
def __init__(self, base_url):
self.base_url = base_url
self.company_token = None
self.operator_tokens = {}
def get_company_token(self, login, password):
"""Вземете токен за автентикация на компания"""
response = requests.post(
f"{self.base_url}/api/company/get-token",
json={"login": login, "password": password}
)
response.raise_for_status()
self.company_token = response.json()
return self.company_token
def get_operator_token(self, operator_id, expires_days=30):
"""Вземете токен на оператор с изтичане"""
expires_at = (
datetime.utcnow() + timedelta(days=expires_days)
).isoformat() + "Z"
response = requests.post(
f"{self.base_url}/api/operator/get-token",
headers={"Authorization": f"Bearer {self.company_token}"},
json={"id": operator_id, "expiresAt": expires_at}
)
response.raise_for_status()
token = response.json()
self.operator_tokens[operator_id] = token
return token
def validate_token(self, token):
"""Проверете дали токенът е все още валиден"""
response = requests.post(
f"{self.base_url}/api/operator/validate-token",
headers={"Authorization": f"Bearer {self.company_token}"},
json={"token": token}
)
response.raise_for_status()
return response.json()
# Употреба
auth = ChatHubAuth("https://chatapi.smsbat.com")
# Вземане на company token
company_token = auth.get_company_token("login", "password")
# Вземане на operator token
operator_token = auth.get_operator_token(operator_id=123, expires_days=30)
# Валидиране на токена
is_valid = auth.validate_token(operator_token)
print(f"Токенът е валиден: {is_valid['valid']}")
JavaScript (Node.js)
const axios = require('axios');
class ChatHubAuth {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.companyToken = null;
this.operatorTokens = {};
}
async getCompanyToken(login, password) {
const response = await axios.post(
`${this.baseUrl}/api/company/get-token`,
{ login, password }
);
this.companyToken = response.data;
return this.companyToken;
}
async getOperatorToken(operatorId, expiresDays = 30) {
const expiresAt = new Date(
Date.now() + expiresDays * 24 * 60 * 60 * 1000
).toISOString();
const response = await axios.post(
`${this.baseUrl}/api/operator/get-token`,
{ id: operatorId, expiresAt },
{
headers: {
Authorization: `Bearer ${this.companyToken}`
}
}
);
const token = response.data;
this.operatorTokens[operatorId] = token;
return token;
}
async validateToken(token) {
const response = await axios.post(
`${this.baseUrl}/api/operator/validate-token`,
{ token },
{
headers: {
Authorization: `Bearer ${this.companyToken}`
}
}
);
return response.data;
}
}
// Употреба
const auth = new ChatHubAuth('https://chatapi.smsbat.com');
async function authenticate() {
// Вземане на company token
const companyToken = await auth.getCompanyToken('login', 'password');
// Вземане на operator token
const operatorToken = await auth.getOperatorToken(123, 30);
// Валидиране на токена
const validation = await auth.validateToken(operatorToken);
console.log('Токенът е валиден:', validation.isValid);
}
authenticate();
PHP
<?php
class ChatHubAuth {
private $baseUrl;
private $companyToken;
private $operatorTokens = [];
public function __construct($baseUrl) {
$this->baseUrl = $baseUrl;
}
public function getCompanyToken($login, $password) {
$ch = curl_init($this->baseUrl . '/api/company/get-token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'login' => $login,
'password' => $password
]));
$response = curl_exec($ch);
curl_close($ch);
$this->companyToken = json_decode($response);
return $this->companyToken;
}
public function getOperatorToken($operatorId, $expiresDays = 30) {
$expiresAt = date(
'Y-m-d\TH:i:s\Z',
time() + ($expiresDays * 24 * 60 * 60)
);
$ch = curl_init($this->baseUrl . '/api/operator/get-token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->companyToken
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'id' => $operatorId,
'expiresAt' => $expiresAt
]));
$response = curl_exec($ch);
curl_close($ch);
$token = json_decode($response);
$this->operatorTokens[$operatorId] = $token;
return $token;
}
public function validateToken($token) {
$ch = curl_init($this->baseUrl . '/api/operator/validate-token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->companyToken
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'token' => $token
]));
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
}
// Употреба
$auth = new ChatHubAuth('https://chatapi.smsbat.com');
// Вземане на company token
$companyToken = $auth->getCompanyToken('login', 'password');
// Вземане на operator token
$operatorToken = $auth->getOperatorToken(123, 30);
// Валидиране на токена
$validation = $auth->validateToken($operatorToken);
echo "Токенът е валиден: " . ($validation['isValid'] ? 'Да' : 'Не');
Добри практики
Съхранение на токени
- ✅ Съхранявайте токените сигурно (криптирана база данни, мениджър на тайни)
- ✅ Никога не добавяйте токени в системата за контрол на версиите (Git)
- ✅ Използвайте променливи на средата за идентификационни данни (environment variables)
- ❌ Не съхранявайте токени в обикновен текст
- ❌ Не излагайте токени в кода от страна на клиента
Ротация (смяна) на токени
- Сменяйте company токените периодично (на всеки 3-6 месеца)
- Задайте разумно изтичане на operator токените (7-30 дни)
- Внедрете автоматично опресняване на токена преди изтичането му
- Отнемайте (revoke) токени, когато операторите напуснат компанията
Обработка на грешки
async function authenticateWithRetry(login, password, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await getCompanyToken(login, password);
} catch (error) {
if (error.response?.status === 401) {
throw new Error('Невалидни идентификационни данни');
}
if (i === retries - 1) throw error;
// Изчакайте преди повторен опит
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 1000)
);
}
}
}
Валидиране на токен
Винаги валидирайте токените преди критични операции (critical operations):
async function performSecureOperation(token, operation) {
// Първо валидирайте токена
const validation = await validateToken(token);
if (!validation.isValid) {
throw new Error('Токенът е изтекъл или невалиден');
}
// Продължете с операцията
return await operation();
}
Съображения за сигурност
Само HTTPS
Винаги използвайте HTTPS, когато изпращате заявки за удостоверяване:
// ✅ Правилно
const baseUrl = 'https://chatapi.smsbat.com';
// ❌ Неправилно - никога не използвайте HTTP за удостоверяване
const baseUrl = 'http://api.chathub.smsbat.com';
Обхват на токена (Scope)
Използвайте подходящия токен за всяка операция:
- Company Token: Управление на организация, създаване на оператор
- Operator Token: Операции в чата, обработка на съобщения
Ограничаване на скоростта (Rate Limiting)
Внедрете ограничаване на скоростта (rate limiting) за заявки за удостоверяване:
class RateLimitedAuth {
constructor() {
this.lastRequest = 0;
this.minInterval = 1000; // 1 секунда между заявките
}
async getToken(login, password) {
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequest;
if (timeSinceLastRequest < this.minInterval) {
await new Promise(resolve =>
setTimeout(resolve, this.minInterval - timeSinceLastRequest)
);
}
this.lastRequest = Date.now();
return await makeAuthRequest(login, password);
}
}
Отстраняване на проблеми
401 Unauthorized (Неупълномощен достъп)
- Проверете дали идентификационните данни са правилни
- Проверете дали токенът не е изтекъл
- Уверете се, че токенът е включен в заглавките на заявката (headers)
- Валидирайте формата на токена
403 Forbidden (Забранен достъп)
- Проверете дали токенът има необходимите права (permissions)
- Проверете дали използвате правилния тип токен (company срещу operator)
- Уверете се, че токенът не е бил отменен
Изтекъл токен
- Поискайте нов токен
- Приложете автоматично опресняване на токена (automatic refresh)
- Задайте подходящо време на изтичане
Следващи стъпки
- Организации - Управление на организации
- Оператори - Работа с оператори
- Интеграция на Widget - Интегриране на чат уиджет