1. Xác thực & Token Cache
Đăng nhập và lấy Access Token, cache token để tái sử dụng giữa các lần chạy
Mục tiêu
Đăng nhập vào hệ thống FastConnect và lấy token cho toàn bộ API call sau đó. Sử dụng token cache để tránh authenticate lại mỗi lần chạy script.
Luồng xử lý
Client App → Auth API → token_cache.json- Tạo
Config→Auth→ gọiensure_auth(auth, otp=...) ensure_authload token từ cache, authenticate nếu chưa có, refresh nếu hết hạn- Auth service trả về
accessToken,refreshToken,expiresInvà lưu vàotoken_cache.json - Mọi request sau đó gắn
Authorization: Bearer <accessToken> - Nếu token hết hạn ở lần sau thì tự động refresh, không cần OTP lại
auth_helper — Token Cache Module
Module auth_helper.py giúp tái sử dụng token giữa các lần chạy script:
"""
auth_helper — Token cache để tái sử dụng token giữa các lần chạy
==================================================================
Thay vì gọi authenticate() mỗi lần chạy script, module này:
1. Load token đã lưu từ file token_cache.json (nếu có)
2. Nếu chưa có → authenticate lần đầu và lưu xuống file
3. Nếu token hết hạn → refresh và lưu lại
4. Nếu token còn hạn → dùng trực tiếp, không gọi API
Cách dùng (sync):
from auth_helper import ensure_auth
with Auth(config) as auth:
ensure_auth(auth)
Cách dùng (async):
from auth_helper import ensure_auth_async
async with AsyncAuth(config) as auth:
await ensure_auth_async(auth)
"""
import json
import os
from ssi_sdk.models import Token
TOKEN_FILE = os.path.join(os.path.dirname(__file__), "token_cache.json")
def load_token() -> Token | None:
"""Load token từ file, trả về None nếu file không tồn tại."""
if not os.path.exists(TOKEN_FILE):
return None
with open(TOKEN_FILE, "r", encoding="utf-8") as f:
_token = json.load(f)
return Token.from_dict(_token)
def save_token(token: Token) -> None:
"""Lưu token xuống file."""
with open(TOKEN_FILE, "w", encoding="utf-8") as f:
json.dump(token.to_dict(), f, indent=2)
print(f"Token đã lưu vào {TOKEN_FILE}")
def ensure_auth(auth, otp: str | None = None) -> None:
"""Đảm bảo auth có token hợp lệ (sync)."""
cached_token = load_token()
if cached_token is None:
print("Không tìm thấy file token, đang authenticate...")
cached_token = auth.token_manager.authenticate(otp=otp) if otp else auth.token_manager.authenticate()
save_token(cached_token)
print("Authenticate thành công, token đã lưu.")
auth.token_manager.set_token(cached_token)
if auth.token_manager.is_token_expired:
print("Token đã hết hạn, đang refresh...")
cached_token = auth.token_manager.refresh()
save_token(cached_token)
print("Refresh token thành công.")
else:
print("Token còn hạn, dùng token từ file.")
async def ensure_auth_async(auth, otp: str | None = None) -> None:
"""Đảm bảo auth có token hợp lệ (async)."""
cached_token = load_token()
if cached_token is None:
print("Không tìm thấy file token, đang authenticate...")
cached_token = await auth.token_manager.authenticate(otp=otp) if otp else await auth.token_manager.authenticate()
save_token(cached_token)
print("Authenticate thành công, token đã lưu.")
await auth.token_manager.set_token(cached_token)
if auth.token_manager.is_token_expired:
print("Token đã hết hạn, đang refresh...")
cached_token = await auth.token_manager.refresh()
save_token(cached_token)
print("Refresh token thành công.")
else:
print("Token còn hạn, dùng token từ file.")Sample Code — Sync
from ssi_sdk import Auth, Trading, Config
from auth_helper import ensure_auth
config = Config(
client_id="<your_client_id>",
api_key="<your_api_key>",
api_secret="<your_api_secret>",
private_key="<your_private_key>",
)
with Auth(config) as auth:
# Bước 1-2: Xác thực, nhận accessToken + refreshToken
ensure_auth(auth)
token = auth.token
print("Access Token :", token.access_token[:40], "...")
print("Token Type :", token.token_type)
print("Expires At :", token.expires_at)
# Bước 3: Token đã được SDK lưu tự động
# Mọi request kế tiếp sẽ gắn header Authorization: Bearer <accessToken>
# Bước 4: Kiểm tra token hết hạn & refresh
if auth.is_token_expired:
new_token = auth.refresh()
print("Token mới:", new_token.access_token[:40], "...")
# Xác nhận token hoạt động bằng cách gọi API
with Trading(auth) as trading:
accounts = trading.account.get_account_info()
print(f"\nXác thực thành công! Tìm thấy {len(accounts)} tài khoản:")
for acc in accounts:
print(f" - {acc.account_no} ({acc.account_type.value})")Sample Code — Async
import asyncio
from ssi_sdk import AsyncAuth, AsyncTrading, Config
from auth_helper import ensure_auth_async
config = Config(
client_id="<your_client_id>",
api_key="<your_api_key>",
api_secret="<your_api_secret>",
private_key="<your_private_key>",
)
async def main():
async with AsyncAuth(config) as auth:
await ensure_auth_async(auth)
token = auth.token
print("Access Token :", token.access_token[:40], "...")
if auth.is_token_expired:
new_token = await auth.refresh()
print("Token mới:", new_token.access_token[:40], "...")
async with AsyncTrading(auth) as trading:
accounts = await trading.account.get_account_info()
print(f"\nXác thực thành công! Tìm thấy {len(accounts)} tài khoản:")
for acc in accounts:
print(f" - {acc.account_no} ({acc.account_type.value})")
asyncio.run(main())Lưu ý quan trọng
- Không hard-code credentials trong mã nguồn. Sử dụng biến môi trường hoặc file cấu hình.
- OTP chỉ cần ở lần authenticate đầu tiên. Các lần sau dùng refresh token.
- Token cache (
token_cache.json) nên được thêm vào.gitignore. - SDK tự động gắn
Authorization: Bearer <accessToken>cho mọi request.