Authentication
Authenticate your applications using OAuth2 Client Credentials flow with JWT tokens.
Overview
The Huntrecht Platform API uses OAuth 2.0 Client Credentials Grant for server-to-server authentication. This flow is designed for trusted backend applications and provides:
- Secure token-based authentication
- Automatic token refresh
- Scope-based authorization
- JWT token validation
Authentication Flow
sequenceDiagram
participant App as Your Application
participant Auth as Huntrecht Auth
participant API as Platform API
App->>Auth: POST /auth/token (client_id + secret)
Auth-->>App: Access Token + Refresh Token
App->>API: GET /users/me (Bearer token)
API-->>App: User Profile Data
Getting Access Tokens
Request Access Token
import requests
response = requests.post(
"https://api.huntrecht.com/api/v1/auth/token",
json={
"grant_type": "client_credentials",
"client_id": "hnt_your_client_id",
"client_secret": "your_secret"
}
)
tokens = response.json()
print(tokens)
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "refresh_abc123..."
}
Token Expiration
| Token | Expiration | Action Required |
|---|---|---|
| Access Token | 30 minutes | Use refresh token |
| Refresh Token | 30 days | Re-authenticate with credentials |
Refresh Access Token
response = requests.post(
"https://api.huntrecht.com/api/v1/auth/token",
json={
"grant_type": "refresh_token",
"refresh_token": tokens["refresh_token"]
}
)
new_tokens = response.json()
Using Access Tokens
Include the token in the Authorization header:
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(
"https://api.huntrecht.com/api/v1/users/me",
headers=headers
)
User Registration (B2B Onboarding)
Register a New Customer Account
response = requests.post(
"https://api.huntrecht.com/api/v1/auth/register",
headers={"Content-Type": "application/json"},
json={
"email": "buyer@company.com",
"password": "secure_password123",
"company_name": "Acme Corporation",
"contact_name": "John Buyer",
"phone": "+1-555-123-4567",
"tax_id": "12-3456789",
"address": {
"street": "123 Business Ave",
"city": "Commerce City",
"state": "CA",
"postal_code": "90210",
"country": "US"
}
}
)
user = response.json()
print(f"User ID: {user['id']}")
print(f"Status: {user['status']}")
Registration Response
{
"id": "user_abc123",
"email": "buyer@company.com",
"company_name": "Acme Corporation",
"status": "pending_kyc",
"shopify_company_id": null,
"created_at": "2026-01-11T10:00:00Z"
}
Email/Password Login
Login with Credentials
response = requests.post(
"https://api.huntrecht.com/api/v1/auth/login",
json={
"email": "buyer@company.com",
"password": "secure_password123"
}
)
tokens = response.json()
Login Response
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "refresh_xyz789...",
"user": {
"id": "user_abc123",
"email": "buyer@company.com",
"company_name": "Acme Corporation",
"role": "buyer"
}
}
Google OAuth Login
Step 1: Redirect to Google
GET https://huntrecht.com/auth/google
Step 2: Handle Callback
After user authorizes, Google redirects to your callback URL with a code:
GET https://yourapp.com/callback?code=4/ABCD...
Step 3: Exchange Code for Token
response = requests.post(
"https://api.huntrecht.com/api/v1/auth/google/callback",
json={"code": "4/ABCD..."}
)
tokens = response.json()
Shopify OAuth Login
# Step 1: Redirect to Shopify authorization
# GET https://{shop}.myshopify.com/admin/oauth/authorize?
# client_id={client_id}&scope=read_orders,read_customers&redirect_uri={callback}
# Step 2: Exchange code for token
response = requests.post(
"https://api.huntrecht.com/api/v1/auth/shopify/callback",
json={
"shop": "your-store.myshopify.com",
"code": "hacc_abc123..."
}
)
tokens = response.json()
JWT Token Structure
Decoded JWT payload:
{
"sub": "user_abc123",
"client_id": "hnt_client_xyz",
"scopes": ["orders:read", "orders:write", "subscriptions:read"],
"role": "buyer",
"company_id": "comp_123",
"exp": 1705555200,
"iat": 1705551600
}
| Claim | Description |
|---|---|
sub | User ID |
client_id | API client identifier |
scopes | Granted permission scopes |
role | User role (admin, buyer, etc.) |
company_id | Associated company/tenant |
Security Best Practices
Security Requirements
- Never store credentials in code - Use environment variables
- Never expose tokens in URLs - Use headers only
- Validate tokens server-side - Never trust client validation
- Use HTTPS only - All requests must use TLS
- Rotate secrets regularly - Rotate client_secret quarterly
- Implement token refresh - Don't require re-authentication
Secure Token Storage
import os
from cryptography.fernet import Fernet
# Encrypt tokens at rest
cipher = Fernet(os.environ["ENCRYPTION_KEY"])
def store_token(token):
encrypted = cipher.encrypt(token.encode())
# Store encrypted token in database
return encrypted
def retrieve_token(encrypted):
return cipher.decrypt(encrypted).decode()
Token Scopes
| Scope | Description |
|---|---|
orders:read | View orders |
orders:write | Create/modify orders |
payments:read | View payments |
payments:write | Process payments |
subscriptions:read | View subscriptions |
subscriptions:write | Manage subscriptions |
credit:read | View credit scores |
credit:write | Request credit assessments |
kyc:read | View KYC status |
kyc:write | Submit KYC documents |
users:read | View user profiles |
users:write | Modify user profiles |
Error Handling
| Error Code | Description | Solution |
|---|---|---|
invalid_client | Invalid client_id or secret | Verify credentials |
invalid_grant | Expired/invalid refresh token | Re-authenticate |
unauthorized_client | Client not authorized for scope | Request correct scopes |
invalid_scope | Requested scope not allowed | Check allowed scopes |
access_denied | User denied authorization | User action required |
Session Management
Get Current User
response = requests.get(
"https://api.huntrecht.com/api/v1/users/me",
headers={"Authorization": f"Bearer {access_token}"}
)
user = response.json()
print(user)
Update User Profile
response = requests.put(
"https://api.huntrecht.com/api/v1/users/me",
headers={"Authorization": f"Bearer {access_token}"},
json={
"phone": "+1-555-987-6543",
"address": {
"street": "456 New Street",
"city": "Business City",
"state": "CA",
"postal_code": "90211",
"country": "US"
}
}
)
Logout
# Revoke refresh token
response = requests.post(
"https://api.huntrecht.com/api/v1/auth/revoke",
json={"refresh_token": refresh_token}
)