Skip to main content

Authentication

Version: 1.0 Last Updated: 2026-02-03 Security Level: Production-Ready


Table of Contents

  1. Overview
  2. Authentication Methods
  3. JWT Token Authentication
  4. API Key Authentication
  5. OAuth2 Integration
  6. Security Best Practices
  7. Token Management
  8. 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

MethodUse CaseExpirationRevocableBest For
JWT TokenUser sessions8 hoursYes (blacklist)Web/mobile apps
API KeyService integration90 days (configurable)YesServer-to-server
OAuth2Third-party authN/AYesSocial login

JWT Token Authentication

How JWT Tokens Work

JSON Web Tokens (JWT) are self-contained tokens that include:

  1. Header: Algorithm and token type
  2. Payload: User claims (id, email, role, expiration)
  3. 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

PrefixEnvironmentUsage
sk_live_ProductionReal data, billed usage
sk_test_TestingSandbox environment

Using API Keys

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
  • LinkedIn

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

PlatformRecommended Storage
Web (Browser)localStorage or sessionStorage (XSS risk), prefer secure HttpOnly cookies
Mobile (iOS/Android)Keychain (iOS) or KeyStore (Android)
ServerEnvironment variables or secrets manager
Desktop AppOS-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