Authentication
Version: 1.0 Last Updated: 2026-02-03 Security Level: Production-Ready
Table of Contents
- Overview
- Authentication Methods
- JWT Token Authentication
- API Key Authentication
- OAuth2 Integration
- Security Best Practices
- Token Management
- Troubleshooting
Overview
AINative Studio uses industry-standard authentication mechanisms to secure API access. We support multiple authentication methods to accommodate different use cases:
- JWT Bearer Tokens: For user-facing applications and session management
- API Keys: For server-to-server communication and long-lived integrations
- OAuth2: For third-party integrations (GitHub, LinkedIn)
Authentication Flow Diagram
User Registration/Login
│
▼
Generate JWT Token
│
├──────> Store in localStorage (web)
├──────> Store in keychain (mobile)
└──────> Store in memory (server)
│
▼
API Request with Token
│
▼
Backend Validates Token
│
├──────> Valid ──────> Process Request
│
└──────> Invalid ────> 401 Unauthorized
Authentication Methods
Comparison Table
| Method | Use Case | Expiration | Revocable | Best For |
|---|---|---|---|---|
| JWT Token | User sessions | 8 hours | Yes (blacklist) | Web/mobile apps |
| API Key | Service integration | 90 days (configurable) | Yes | Server-to-server |
| OAuth2 | Third-party auth | N/A | Yes | Social login |
JWT Token Authentication
How JWT Tokens Work
JSON Web Tokens (JWT) are self-contained tokens that include:
- Header: Algorithm and token type
- Payload: User claims (id, email, role, expiration)
- Signature: Cryptographic signature to verify authenticity
Token Structure
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 <- Header
.eyJzdWIiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDAiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTcwNDExMDQwMH0 <- Payload
.xyz123abc <- Signature
Decoded Payload
{
"sub": "550e8400-e29b-41d4-a716-446655440000", // User ID
"email": "user@example.com",
"role": "USER",
"exp": 1704110400, // Expiration timestamp
"iat": 1704082400 // Issued at timestamp
}
Obtaining a JWT Token
Method 1: Email/Password Registration
POST /v1/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!",
"full_name": "John Doe"
}
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 28800
}
Method 2: Email/Password Login
POST /v1/auth/login-json
Content-Type: application/json
{
"username": "user@example.com",
"password": "SecurePass123!"
}
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 28800
}
Using JWT Tokens
Include the token in the Authorization header:
GET /v1/auth/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Python Example
import requests
# Login and get token
login_response = requests.post(
"https://api.ainative.studio/v1/auth/login-json",
json={
"username": "user@example.com",
"password": "SecurePass123!"
}
)
token = login_response.json()["access_token"]
# Use token for authenticated requests
headers = {"Authorization": f"Bearer {token}"}
user_response = requests.get(
"https://api.ainative.studio/v1/auth/me",
headers=headers
)
print(user_response.json())
JavaScript Example
// Login and get token
const loginResponse = await fetch(
'https://api.ainative.studio/v1/auth/login-json',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'user@example.com',
password: 'SecurePass123!'
})
}
);
const { access_token } = await loginResponse.json();
// Use token for authenticated requests
const userResponse = await fetch(
'https://api.ainative.studio/v1/auth/me',
{
headers: { 'Authorization': `Bearer ${access_token}` }
}
);
const userData = await userResponse.json();
console.log(userData);
Token Refresh
Tokens expire after 8 hours. Refresh before expiration:
POST /v1/auth/refresh
Authorization: Bearer <current_token>
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 28800
}
Automatic Token Refresh (Python)
import requests
from datetime import datetime, timedelta
class AuthClient:
def __init__(self, email, password):
self.email = email
self.password = password
self.token = None
self.token_expires_at = None
self.login()
def login(self):
response = requests.post(
"https://api.ainative.studio/v1/auth/login-json",
json={"username": self.email, "password": self.password}
)
data = response.json()
self.token = data["access_token"]
self.token_expires_at = datetime.now() + timedelta(seconds=data["expires_in"])
def get_token(self):
# Refresh 5 minutes before expiration
if datetime.now() >= self.token_expires_at - timedelta(minutes=5):
self.refresh_token()
return self.token
def refresh_token(self):
response = requests.post(
"https://api.ainative.studio/v1/auth/refresh",
headers={"Authorization": f"Bearer {self.token}"}
)
data = response.json()
self.token = data["access_token"]
self.token_expires_at = datetime.now() + timedelta(seconds=data["expires_in"])
def make_request(self, url, **kwargs):
headers = kwargs.get("headers", {})
headers["Authorization"] = f"Bearer {self.get_token()}"
kwargs["headers"] = headers
return requests.get(url, **kwargs)
# Usage
client = AuthClient("user@example.com", "SecurePass123!")
response = client.make_request("https://api.ainative.studio/v1/database/projects")
print(response.json())
API Key Authentication
When to Use API Keys
Use API keys for:
- Server-to-server communication
- CI/CD pipelines
- Long-running services
- Background jobs
- Webhook endpoints
Do NOT use API keys for:
- Client-side JavaScript (keys will be exposed)
- Mobile apps (use JWT tokens instead)
- User-facing applications
Creating an API Key
POST /v1/api-keys
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json
{
"name": "Production Server Key",
"expires_in_days": 90
}
Response:
{
"id": "key_550e8400-e29b-41d4-a716-446655440000",
"api_key": "sk_live_abcdef1234567890wxyz",
"name": "Production Server Key",
"prefix": "sk_live",
"expires_at": "2026-05-03T00:00:00Z",
"created_at": "2026-02-03T12:00:00Z"
}
Critical: Save the api_key value immediately - it's only shown once!
API Key Formats
| Prefix | Environment | Usage |
|---|---|---|
sk_live_ | Production | Real data, billed usage |
sk_test_ | Testing | Sandbox environment |
Using API Keys
Option 1: X-API-Key Header (Recommended)
GET /v1/database/projects
X-API-Key: sk_live_abcdef1234567890wxyz
Option 2: Authorization Header
GET /v1/database/projects
Authorization: Bearer sk_live_abcdef1234567890wxyz
Python Example with API Key
import requests
import os
API_KEY = os.getenv("AINATIVE_API_KEY")
headers = {"X-API-Key": API_KEY}
response = requests.get(
"https://api.ainative.studio/v1/database/projects",
headers=headers
)
print(response.json())
Managing API Keys
List All Your API Keys
GET /v1/api-keys
Authorization: Bearer YOUR_JWT_TOKEN
Response:
[
{
"id": "key_123",
"name": "Production Server Key",
"prefix": "sk_live",
"is_active": true,
"last_used_at": "2026-02-03T11:45:00Z",
"usage_count": 15432,
"expires_at": "2026-05-03T00:00:00Z",
"created_at": "2026-02-03T12:00:00Z"
}
]
Revoke an API Key
DELETE /v1/api-keys/{key_id}
Authorization: Bearer YOUR_JWT_TOKEN
Response:
{
"message": "API key revoked successfully"
}
API Key Rotation Strategy
Best practice: Rotate keys every 90 days
import requests
from datetime import datetime, timedelta
def rotate_api_key(old_key_id: str, jwt_token: str):
"""
Rotate API key: create new key, update services, revoke old key
"""
# Step 1: Create new key
create_response = requests.post(
"https://api.ainative.studio/v1/api-keys",
headers={"Authorization": f"Bearer {jwt_token}"},
json={"name": f"Rotated Key {datetime.now()}", "expires_in_days": 90}
)
new_key = create_response.json()["api_key"]
print(f"New API Key: {new_key}")
print("Update your services with this new key, then press Enter to revoke the old key...")
input()
# Step 2: Revoke old key
revoke_response = requests.delete(
f"https://api.ainative.studio/v1/api-keys/{old_key_id}",
headers={"Authorization": f"Bearer {jwt_token}"}
)
print("Old key revoked successfully")
return new_key
OAuth2 Integration
Supported OAuth Providers
- GitHub
GitHub OAuth Flow
Step 1: Redirect User to GitHub
https://github.com/login/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=user:email
Step 2: Exchange Code for Token
POST /v1/auth/github/callback
Content-Type: application/json
{
"code": "github_authorization_code",
"redirect_uri": "https://yourapp.com/callback"
}
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"user": {
"id": "github_user_id",
"email": "user@example.com",
"name": "John Doe",
"avatar_url": "https://avatars.githubusercontent.com/..."
}
}
LinkedIn OAuth Flow
Step 1: Redirect User to LinkedIn
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=openid%20profile%20email
Step 2: Exchange Code for Token
POST /v1/auth/linkedin/callback
Content-Type: application/json
{
"code": "linkedin_authorization_code",
"redirect_uri": "https://yourapp.com/callback"
}
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 28800,
"user": {
"id": "linkedin_user_id",
"email": "user@example.com",
"name": "John Doe",
"picture": "https://media.licdn.com/..."
}
}
Security Best Practices
1. Token Storage
| Platform | Recommended Storage |
|---|---|
| Web (Browser) | localStorage or sessionStorage (XSS risk), prefer secure HttpOnly cookies |
| Mobile (iOS/Android) | Keychain (iOS) or KeyStore (Android) |
| Server | Environment variables or secrets manager |
| Desktop App | OS-specific secure storage |
2. Never Commit Credentials
# .gitignore
.env
.env.local
*.pem
*.key
config/secrets.json
3. Use Environment Variables
# ❌ WRONG - Hardcoded
API_KEY = "sk_live_abc123"
# ✅ CORRECT - Environment variable
import os
API_KEY = os.getenv("AINATIVE_API_KEY")
4. Implement Token Expiration Handling
import requests
def make_authenticated_request(url, token):
try:
response = requests.get(
url,
headers={"Authorization": f"Bearer {token}"}
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
# Token expired - refresh or re-login
new_token = refresh_token(token)
return make_authenticated_request(url, new_token)
raise
5. Use HTTPS Only
# ❌ WRONG
base_url = "http://api.ainative.studio"
# ✅ CORRECT
base_url = "https://api.ainative.studio"
6. Implement Rate Limiting
from time import sleep
import requests
class RateLimitedClient:
def __init__(self, api_key, requests_per_minute=60):
self.api_key = api_key
self.requests_per_minute = requests_per_minute
self.request_times = []
def make_request(self, url, **kwargs):
# Remove old requests from tracking
current_time = time.time()
self.request_times = [
t for t in self.request_times
if current_time - t < 60
]
# Wait if rate limit reached
if len(self.request_times) >= self.requests_per_minute:
sleep_time = 60 - (current_time - self.request_times[0])
sleep(sleep_time)
# Make request
headers = kwargs.get("headers", {})
headers["X-API-Key"] = self.api_key
kwargs["headers"] = headers
response = requests.get(url, **kwargs)
self.request_times.append(time.time())
return response
7. Validate SSL Certificates
import requests
# ✅ Always verify SSL (default)
response = requests.get(url, verify=True)
# ❌ NEVER do this in production
response = requests.get(url, verify=False) # Insecure!
8. Password Requirements
When creating user accounts:
- Minimum 8 characters
- At least one uppercase letter (A-Z)
- At least one lowercase letter (a-z)
- At least one number (0-9)
- At least one special character (!@#$%^&*)
Token Management
Token Blacklist
Logout invalidates tokens by adding them to a blacklist:
POST /v1/auth/logout
Authorization: Bearer <token>
Password Reset Flow
Step 1: Request Reset
POST /v1/auth/forgot-password
Content-Type: application/json
{
"email": "user@example.com"
}
Step 2: Reset with Token from Email
POST /v1/auth/reset-password
Content-Type: application/json
{
"token": "reset_token_from_email",
"new_password": "NewSecurePass456!"
}
Change Password
POST /v1/auth/change-password
Authorization: Bearer <token>
Content-Type: application/json
{
"current_password": "OldPass123!",
"new_password": "NewPass456!"
}
Troubleshooting
Common Issues
1. 401 Unauthorized
Cause: Invalid or expired token
Solution:
# Refresh token or re-login
response = requests.post(
"https://api.ainative.studio/v1/auth/refresh",
headers={"Authorization": f"Bearer {token}"}
)
new_token = response.json()["access_token"]
2. 403 Forbidden
Cause: Insufficient permissions
Solution: Verify your account has the required role/permissions
3. Token Not Working After Logout
Cause: Token is blacklisted
Solution: Generate a new token by logging in again
4. API Key Invalid
Cause: Key was revoked or expired
Solution: Create a new API key
Testing Authentication
Test Endpoints
# Test authentication status
GET /v1/test-auth/auth-status
# Create test user
POST /v1/test-auth/create-test-user
Content-Type: application/json
{
"email": "test@example.com",
"password": "TestPass123!"
}
# Test login
POST /v1/test-auth/test-login
Content-Type: application/json
{
"email": "test@example.com",
"password": "TestPass123!"
}
Additional Resources
Last Updated: 2026-02-03 Version: 1.0 Security Review: Completed
Refs #1015