Authentication
JWT tokens, API keys, and MFA flows for secure API access
Authentication
The PRIME API supports two authentication methods: JWT Bearer Tokens for user sessions and API Keys for programmatic access.
Authentication Methods
| Method | Use Case | Header Format |
|---|---|---|
| Bearer Token | User sessions (web/mobile) | Authorization: Bearer <jwt> |
| API Key Signature | Programmatic access (bots, integrations) | Authorization: TDXV1-HMAC-SHA256 ... |
Bearer Token Authentication
After login, use the JWT access token in the Authorization header.
Header Format
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
JWT Structure
The access token is a JWT signed with HMAC-SHA256 containing:
Standard Claims:
| Claim | Description |
|---|---|
aud | Audience |
exp | Expiration timestamp |
iat | Issued at timestamp |
iss | Issuer |
sub | Subject |
jti | JWT ID |
Custom Claims:
| Claim | Description |
|---|---|
uid | User ID |
ut | User Type (FRONT_OFFICE, BACK_OFFICE, SYSTEM) |
cid | Client Account ID |
un | Username |
mfa | MFA enabled flag |
r | Roles array |
ms | Modules array (e.g., "tdx", "issuers", "investors") |
Token Lifecycle
| Token | Typical Expiration | Renewal |
|---|---|---|
| Access Token | 1 hour | Use refresh token |
| Session Token | 7 days | Re-authenticate |
API Key Authentication
For programmatic access, use HMAC-SHA256 signed requests.
Header Format
Authorization: TDXV1-HMAC-SHA256 ApiKey=<api_key> Nonce=<uuid> Timestamp=<ms> Signature=<base64>
Components
| Component | Format | Description |
|---|---|---|
ApiKey | UUID | Your API key ID |
Nonce | UUID | Unique per-request (prevents replay) |
Timestamp | Integer | UTC timestamp in milliseconds |
Signature | Base64 | HMAC-SHA256 signature |
Signature Calculation
-
Build the hash input (space-separated):
TDXV1 <ApiKey> <Nonce> <Timestamp> <Method> <Host> <Path> <QueryString> <ContentType> <Body> -
Hash the input:
hash = SHA256(input) -
Sign the hash:
signature = Base64(HMAC-SHA256(apiSecret, hash))
Example (Python)
import hashlib
import hmac
import base64
import uuid
import time
api_key = "your-api-key-uuid"
api_secret = "your-api-secret"
nonce = str(uuid.uuid4())
timestamp = str(int(time.time() * 1000))
method = "GET"
host = "api.t-dx.com"
path = "/api/rest/v1/balances"
query = ""
content_type = ""
body = ""
# Build hash input
hash_input = f"TDXV1 {api_key} {nonce} {timestamp} {method} {host} {path} {query} {content_type} {body}"
# Calculate hash
hash_bytes = hashlib.sha256(hash_input.encode()).digest()
hash_b64 = base64.b64encode(hash_bytes).decode()
# Calculate signature
signature = base64.b64encode(
hmac.new(api_secret.encode(), hash_b64.encode(), hashlib.sha256).digest()
).decode()
# Build header
auth_header = f"TDXV1-HMAC-SHA256 ApiKey={api_key} Nonce={nonce} Timestamp={timestamp} Signature={signature}"Time Window
Requests must be within 150 seconds of the server time. Requests outside this window are rejected.
MFA (Multi-Factor Authentication)
When MFA is enabled for a user, login requires a TOTP code provided in the challenge field of the login request.
Login Flow with MFA
-
Attempt Login Without TOTP - If MFA is enabled, returns error:
POST /api/rest/v1/users/authentication/login { "username": "[email protected]", "password": "password123" }Response (401 Unauthorized):
{ "code": 16, "message": "MFA challenge required", "details": [{ "@type": "type.googleapis.com/tgtraded.Error", "reason": "MFA_REQUIRED" }] } -
Login With TOTP Code - Include the
challengefield:POST /api/rest/v1/users/authentication/login { "username": "[email protected]", "password": "password123", "challenge": "123456" } -
Receive Tokens - On success:
{ "result": { "accessToken": "eyJ...", "refreshToken": "session-uuid", "accessExpiresAt": "2024-01-15T11:30:00Z", "sessionExpiresAt": "2024-01-22T10:30:00Z" } }
Recovery Codes
Recovery codes are generated during MFA setup and can be used as alternatives to TOTP codes when the authenticator device is unavailable. Use a recovery code in place of the TOTP code in the challenge field.
Related Endpoints
| Endpoint | Description |
|---|---|
POST /users/authentication/challenge/reset/init | Start MFA reset flow |
POST /users/authentication/challenge/reset | Complete MFA reset |
POST /users/authentication/challenge/validate | Validate a TOTP code |
Token Refresh
Before the access token expires, use the refresh token to obtain a new access token.
Request
POST /api/rest/v1/users/authentication/refresh
Content-Type: application/json
{
"refreshToken": "session-uuid"
}Response
{
"result": {
"accessToken": "eyJ...",
"refreshToken": "session-uuid",
"accessExpiresAt": "2024-01-15T11:30:00Z",
"sessionExpiresAt": "2024-01-22T10:30:00Z"
}
}Authentication Errors
| Status | Reason | Description |
|---|---|---|
| 401 | UNAUTHENTICATED | Missing or invalid token |
| 401 | MFA_REQUIRED | MFA verification needed |
| 403 | PERMISSION_DENIED | Valid token but insufficient permissions |
| 403 | ACCOUNT_IS_SUSPENDED | Account suspended |
Best Practices
- Store tokens securely - Never expose tokens in URLs or logs
- Refresh proactively - Refresh tokens before expiration
- Use API keys for automation - Don't embed user credentials in scripts
- Rotate API keys regularly - Create new keys and revoke old ones
- IP whitelist API keys - Restrict to known IP addresses
- Minimum permissions - Request only necessary permissions
Updated 5 days ago
