I-skip tungo sa nilalaman

Pagpapatunay

Gumagamit ang SMSBAT ChatHub API ng two-level na JWT token-based authentication system na may mga token ng kumpanya at operator token.

Daloy ng Pagpapatotoo

graph TD
    A[Login Credentials] --> B[Get Company Token]
    B --> C[Use Company Token]
    C --> D[Get Operator Token]
    D --> E[Use Operator Token]
    E --> F[Validate Token]

Token ng Kumpanya

Ang mga token ng kumpanya ay nagbibigay ng access sa antas ng organisasyon sa ChatHub API.

Kumuha ng Token ng Kumpanya

Endpoint: POST /api/company/get-token

Kahilingan:

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"
  }'

Katawan ng Kahilingan:

{
  "login": "string",
  "password": "string"
}

Tugon:

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

Ang tugon ay isang JWT token string.

Gumamit ng Company Token

Isama ang token ng kumpanya sa mga kahilingan sa API gamit ang isa sa dalawang pamamaraan:

Paraan 1: Authorization Header (Inirerekomenda)

curl -X GET https://chatapi.smsbat.com/api/company/organization \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Paraan 2: X-Authorization-Key Header

curl -X GET https://chatapi.smsbat.com/api/company/organization \
  -H "X-Authorization-Key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token ng Operator

Ang mga operator token ay nagbibigay ng access na partikular sa user para sa mga indibidwal na operator sa loob ng isang organisasyon.

Kumuha ng Token ng Operator

Endpoint: POST /api/operator/get-token

Kahilingan:

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"
  }'

Katawan ng Kahilingan:

{
  "id": 0,
  "expiresAt": "2025-01-20T14:33:34.147Z"
}

Mga Parameter:

Parameter Uri Kinakailangan Paglalarawan
id integer Oo Operator ID
expiresSa string (ISO 8601) Oo Petsa at oras ng pag-expire ng token (max 24 na oras mula ngayon)

Mahalaga: Ang maximum na buhay ng token ay 24 na oras. Ang parameter na expiresAt ay hindi maaaring higit sa 24 na oras sa hinaharap.

Tugon:

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcGVyYXRvcl9pZCI6MTIzLCJleHAiOjE3Mzc0MTI3OTl9.example_signature"

Gumamit ng Operator Token

Isama ang operator token sa mga kahilingan sa API:

curl -X GET https://chatapi.smsbat.com/api/operator \
  -H "Authorization: Bearer {operator-token}"

Pagpapatunay ng Token

I-verify na valid pa rin ang isang token bago ito gamitin.

Endpoint: POST /api/operator/validate-token

Kahilingan:

curl -X POST https://chatapi.smsbat.com/api/operator/validate-token \
  -H "Authorization: Bearer {company-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'

Katawan ng Kahilingan:

{
  "token": "string"
}

Tugon (Valid na Token):

{
  "isValid": true,
  "operatorId": 123,
  "clientId": 0,
  "expiresAt": "2025-12-31T23:59:59Z",
  "error": null
}

Tugon (Di-wastong Token):

{
  "isValid": false,
  "error": "Invalid token"
}

Pag-expire ng Token

Mga Token ng Kumpanya

  • Walang tahasang expiration sa API
  • Makipag-ugnayan sa iyong account manager para sa mga patakaran sa token lifecycle
  • Paikutin ang mga token sa pana-panahon para sa seguridad

Mga Token ng Operator

  • Itinakda ang expiration kapag humihiling ng token (parameter na expiresAt)
  • I-validate ang mga token bago gamitin
  • Humiling ng mga bagong token bago mag-expire

Mga Halimbawa ng Pagpapatupad

Sawa

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):
        """Get company authentication token"""
        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):
        """Get operator token with expiration"""
        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):
        """Validate if token is still valid"""
        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()

# Usage
auth = ChatHubAuth("https://chatapi.smsbat.com")

# Get company token
company_token = auth.get_company_token("login", "password")

# Get operator token
operator_token = auth.get_operator_token(operator_id=123, expires_days=30)

# Validate token
is_valid = auth.validate_token(operator_token)
print(f"Token valid: {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;
  }
}

// Usage
const auth = new ChatHubAuth('https://chatapi.smsbat.com');

async function authenticate() {
  // Get company token
  const companyToken = await auth.getCompanyToken('login', 'password');

  // Get operator token
  const operatorToken = await auth.getOperatorToken(123, 30);

  // Validate token
  const validation = await auth.validateToken(operatorToken);
  console.log('Token valid:', 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);
    }
}

// Usage
$auth = new ChatHubAuth('https://chatapi.smsbat.com');

// Get company token
$companyToken = $auth->getCompanyToken('login', 'password');

// Get operator token
$operatorToken = $auth->getOperatorToken(123, 30);

// Validate token
$validation = $auth->validateToken($operatorToken);
echo "Token valid: " . ($validation['isValid'] ? 'Yes' : 'No');

Pinakamahuhusay na Kasanayan

Imbakan ng Token

  • ✅ Ligtas na mag-imbak ng mga token (naka-encrypt na database, secrets manager)
  • ✅ Huwag kailanman mag-commit ng mga token sa version control
  • ✅ Gumamit ng mga variable ng kapaligiran para sa mga kredensyal
  • ❌ Huwag mag-imbak ng mga token sa plain text
  • ❌ Huwag ilantad ang mga token sa client-side code

Pag-ikot ng Token

  • Paikutin ang mga token ng kumpanya sa pana-panahon (bawat 3-6 na buwan)
  • Magtakda ng makatwirang expiration para sa mga token ng operator (7-30 araw)
  • Ipatupad ang awtomatikong pag-refresh ng token bago mag-expire
  • Bawiin ang mga token kapag umalis ang mga operator

Error sa Paghawak

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('Invalid credentials');
      }

      if (i === retries - 1) throw error;

      // Wait before retry
      await new Promise(resolve =>
        setTimeout(resolve, Math.pow(2, i) * 1000)
      );
    }
  }
}

Pagpapatunay ng Token

Palaging i-validate ang mga token bago ang mga kritikal na operasyon:

async function performSecureOperation(token, operation) {
  // Validate token first
  const validation = await validateToken(token);

  if (!validation.isValid) {
    throw new Error('Token expired or invalid');
  }

  // Proceed with operation
  return await operation();
}

Mga Pagsasaalang-alang sa Seguridad

HTTPS Lang

Palaging gumamit ng HTTPS kapag nagpapadala ng mga kahilingan sa pagpapatunay:

// ✅ Correct
const baseUrl = 'https://chatapi.smsbat.com';

// ❌ Wrong - never use HTTP for authentication
const baseUrl = 'http://api.chathub.smsbat.com';

Saklaw ng Token

Gamitin ang naaangkop na token para sa bawat operasyon:

  • Token ng Kumpanya: Pamamahala ng organisasyon, paggawa ng operator
  • Token ng Operator: Mga pagpapatakbo ng chat, paghawak ng mensahe

Paglilimita sa Rate

Ipatupad ang paglilimita sa rate para sa mga kahilingan sa pagpapatunay:

class RateLimitedAuth {
  constructor() {
    this.lastRequest = 0;
    this.minInterval = 1000; // 1 second between requests
  }

  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);
  }
}

Pag-troubleshoot

401 Hindi awtorisado

  • I-verify na tama ang mga kredensyal
  • Hindi pa nag-expire ang check token
  • Tiyaking kasama ang token sa mga header ng kahilingan
  • Patunayan ang format ng token

403 Bawal

  • I-verify na ang token ay may mga kinakailangang pahintulot
  • Suriin kung gumagamit ng tamang uri ng token (kumpanya vs operator)
  • Tiyaking hindi binawi ang token

Nag-expire na ang Token

  • Humiling ng bagong token
  • Ipatupad ang awtomatikong pag-refresh ng token
  • Itakda ang naaangkop na mga oras ng pag-expire

Mga Susunod na Hakbang