Skip to main content

Authentication

All Noxys API endpoints (except /healthz, /readyz, /metrics, and /auth/login) require authentication via a JWT Bearer token.

Getting a Token

Login with Email & Password

Endpoint: POST /api/v1/auth/login

curl -X POST https://api.noxys.cloud/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "alice@acme.fr",
"password": "your-password"
}'

Response (200 OK):

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMTAiLCJlbWFpbCI6ImFsaWNlQGFjbWUuZnIiLCJ0ZW5hbnRfaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDEiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3NDUxNjU5MzEsImV4cCI6MTc0NTI1MjMzMX0...",
"user": {
"id": "00000000-0000-0000-0000-000000000010",
"email": "alice@acme.fr",
"display_name": "Alice Martin",
"role": "admin",
"tenant_id": "00000000-0000-0000-0000-000000000001"
}
}

Token Validity: 24 hours from issue time

Security:

  • Store the token securely (environment variables, secure vaults)
  • Never commit tokens to version control
  • Rotate tokens regularly
  • Use HTTPS for all requests

Rate Limiting on Login

Login endpoint is rate-limited to 5 requests per minute per IP address. This prevents brute-force attacks.

If exceeded:

HTTP 429 Too Many Requests

{
"error": "Too many login attempts. Please try again in a few minutes."
}

Using a Token

Include the token in every subsequent request via the Authorization header:

curl https://api.noxys.cloud/api/v1/interactions \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token Format

JWT tokens have three parts separated by dots: header.payload.signature

Example Decoded Payload:

{
"sub": "00000000-0000-0000-0000-000000000010",
"email": "alice@acme.fr",
"tenant_id": "00000000-0000-0000-0000-000000000001",
"role": "admin",
"iat": 1745165931,
"exp": 1745252331
}

Claims:

ClaimTypeDescription
subUUIDUser ID
emailStringUser email address
tenant_idUUIDOrganization/tenant ID
roleStringUser role (admin or viewer)
iatUnix timestampToken issued-at time
expUnix timestampToken expiration time (24 hours after iat)

Token Refresh

Tokens expire after 24 hours. When a token expires, you'll receive a 401 Unauthorized response:

HTTP 401 Unauthorized

{
"error": "Token expired"
}

To refresh: Call /auth/login again with your email and password to get a new token.

For automation, consider:

  1. Storing credentials securely in environment variables
  2. Calling login once per day before using the API
  3. Implementing automatic retry-on-401 logic in your client

Example: Python Auto-Refresh

import requests
import os
from datetime import datetime, timedelta

class NoxysClient:
def __init__(self, email: str, password: str):
self.email = email
self.password = password
self.token = None
self.token_expiry = None
self.base_url = "https://api.noxys.cloud/api/v1"

def _login(self):
"""Get a new token."""
response = requests.post(
f"{self.base_url}/auth/login",
json={"email": self.email, "password": self.password}
)
response.raise_for_status()
data = response.json()
self.token = data["token"]
# Token is valid for 24 hours
self.token_expiry = datetime.utcnow() + timedelta(hours=24)

def _ensure_valid_token(self):
"""Refresh token if expired."""
if not self.token or datetime.utcnow() >= self.token_expiry:
self._login()

def get_interactions(self, limit=50):
"""List interactions."""
self._ensure_valid_token()
response = requests.get(
f"{self.base_url}/interactions?limit={limit}",
headers={"Authorization": f"Bearer {self.token}"}
)
response.raise_for_status()
return response.json()

# Usage
client = NoxysClient("alice@acme.fr", "password")
interactions = client.get_interactions()

Role-Based Access Control

Tokens include a role claim that determines permissions:

Admin Role

Full access to all endpoints:

  • Create, read, update, delete policies
  • Manage users (invite, update role, delete)
  • View audit log
  • Access admin-only endpoints (e.g., /classification/classify)
  • View all interactions and alerts
  • Export data

Viewer Role

Read-only access:

  • View dashboard and statistics
  • View interactions and alerts
  • View policies (read-only)
  • Cannot modify policies, users, or settings

Attempting a privileged action as a viewer:

HTTP 403 Forbidden

{
"error": "Insufficient permissions. This action requires admin role."
}

OAuth 2.0 (Coming Soon)

We're implementing OAuth 2.0 for delegated authentication and service-to-service flows in Q2 2026.

Expected flow:

# 1. Redirect user to authorization endpoint
GET https://api.noxys.cloud/oauth/authorize \
?client_id=abc123 \
&response_type=code \
&redirect_uri=https://your-app.com/callback \
&scope=read:interactions write:policies

# 2. User authenticates and grants permission
# Receives authorization code at redirect_uri

# 3. Exchange code for token
POST https://api.noxys.cloud/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "auth-code-from-step-2",
"client_id": "abc123",
"client_secret": "secret123"
}'

# 4. Use access token for API calls
Authorization: Bearer access_token_value

Scopes (in development):

  • read:interactions — List and view interactions
  • write:policies — Create and modify policies
  • read:users — View user list
  • admin:all — Full admin access

Rate Limiting

Beyond the login endpoint, all API calls are rate-limited:

Limits:

  • 100 requests/minute per IP address (backstop)
  • 1,000 requests/minute per tenant (primary limit)

When exceeded (HTTP 429):

HTTP 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1745252400

{
"error": "Rate limit exceeded. Please retry after 60 seconds."
}

Response Headers:

HeaderDescription
Retry-AfterSeconds to wait before retrying
X-RateLimit-LimitRequest limit for this endpoint
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when limit resets

Rate Limiting Strategy

To avoid hitting limits:

  1. Batch operations: Use /interactions/batch instead of creating one at a time
  2. Pagination: Use limit parameter to fetch many items in one request
  3. Exponential backoff: Wait longer before each retry
  4. Contact us: For higher limits, email support@noxys.eu

Example: Exponential Backoff in Python

import requests
import time

def api_call_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)

if response.status_code == 429:
wait_time = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Waiting {wait_time} seconds...")
time.sleep(wait_time)
continue

return response

raise Exception("Max retries exceeded")

Security Best Practices

  1. Always use HTTPS — Never send tokens over plain HTTP
  2. Store tokens securely:
    • Use environment variables for credentials
    • Use secret management tools (HashiCorp Vault, AWS Secrets Manager)
    • Never hardcode in code
  3. Rotate credentials regularly — Change passwords every 90 days
  4. Use strong passwords — At least 12 characters, mixed case and numbers
  5. Monitor token usage — Check audit logs for unauthorized access
  6. Set up IP whitelisting — Restrict API calls to known IPs (contact support)
  7. Implement API key rotation — If using API keys, rotate every 90 days

Troubleshooting

"Token expired"

HTTP 401 Unauthorized
{
"error": "Token expired"
}

Solution: Call /auth/login again to get a new token.

"Invalid token"

HTTP 401 Unauthorized
{
"error": "Invalid token"
}

Solutions:

  • Verify token is included in Authorization: Bearer <token> header
  • Check token hasn't been tampered with
  • Ensure token has correct format (three dot-separated parts)

"Insufficient permissions"

HTTP 403 Forbidden
{
"error": "Insufficient permissions"
}

Solution: Contact your admin to upgrade your role from viewer to admin.

"Too many login attempts"

HTTP 429 Too Many Requests
{
"error": "Too many login attempts. Please try again in a few minutes."
}

Solution: Wait at least 60 seconds before retrying. Check for scripts making accidental login loops.

What's Next?