Skip to content

Applications Guide — API Client Management

Tenxyte provides a built-in Application model to manage multiple API clients (such as Web apps, Mobile apps, testing scripts, or B2B integrations). Each application gets its own access_key and access_secret which are required to authenticate API requests.


Table of Contents


Overview

In modern systems, a backend usually serves multiple clients. Instead of using a single global API key or hardcoding credentials, Tenxyte allows you to generate distinct keys for each client.

For example, you could have: - Web Frontend App - iOS Mobile App - Partner X Integration App

Tenxyte supports two authentication modes for applications:

Mode Headers Use Case
Frontend (browser) X-Access-Key + Origin header Web apps, SPAs
Backend (server-to-server) X-Access-Key + X-Access-Secret Cron jobs, webhooks, admin scripts

The X-Access-Secret must never be exposed in a browser environment.


How It Works

  1. Creation: An administrator creates an Application via the API (or Python code).
  2. Credentials Display: The system generates an access_key (public) and an access_secret (private). The raw access_secret is returned only once and then securely hashed (bcrypt + base64) in the database.
  3. Usage:
  4. Backend: Pass X-Access-Key and X-Access-Secret headers.
  5. Frontend: Pass only X-Access-Key. The middleware validates the request's Origin header against the application's allowed_origins list.
  6. Revocation: If a secret leaks, administrators can regenerate credentials for that specific application or deactivate the application entirely.

Frontend (Key-Only) Mode

For browser-based clients, configure allowed_origins on the application:

# When creating the application
app, secret = Application.create_application(
    name="Web Frontend",
    allowed_origins=["https://app.example.com", "http://localhost:3000"],
)

The frontend only sends the public key:

GET /api/v1/auth/me/
X-Access-Key: pkg_abc123...
Origin: https://app.example.com

If allowed_origins is empty, key-only mode is disabled and the secret is required.


Configuration

The default Application model can be customized. If you need to add fields (like rate limits, owner, etc.), you can extend AbstractApplication.

# Create a custom model
from tenxyte.models import AbstractApplication
from django.db import models

class CustomApplication(AbstractApplication):
    owner = models.ForeignKey('users.User', on_delete=models.CASCADE)
    api_rate_limit = models.IntegerField(default=1000)

    class Meta(AbstractApplication.Meta):
        db_table = 'custom_applications'

And update your settings.py:

TENXYTE_APPLICATION_MODEL = 'myapp.CustomApplication'


API Usage

All endpoints are located under /api/v1/auth/applications/ and require appropriate RBAC permissions (applications.view, applications.create, applications.update, applications.delete, applications.regenerate) and a valid JWT.

Create an Application

Creates a new application and returns the secret.

POST /api/v1/auth/applications/
Authorization: Bearer <token>

{
  "name": "Mobile iOS App",
  "description": "Main iOS app for end-users",
  "redirect_uris": ["myapp://callback", "https://app.example.com/auth"]
}

Response 201:

{
  "message": "Application created successfully",
  "application": {
    "id": 1,
    "name": "Mobile iOS App",
    "description": "Main iOS app for end-users",
    "access_key": "c4ca4238a0b923820dcc509a6f75849b...",
    "is_active": true,
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z"
  },
  "credentials": {
    "access_key": "c4ca4238a0b923820dcc509a6f75849b...",
    "access_secret": "8b1a9953c4611296a827abf8c47804d7..."
  },
  "warning": "Save the access_secret now! It will never be shown again."
}

List Applications

Returns a paginated list of all applications.

GET /api/v1/auth/applications/
Authorization: Bearer <token>

Accepts query parameters such as ?search=mobile, ?is_active=true, ?ordering=name.

Get Application Details

Retrieves details of a standard application. The secret is never returned.

GET /api/v1/auth/applications/1/
Authorization: Bearer <token>

Update an Application

Updates the name or description of an application.

PUT /api/v1/auth/applications/1/
Authorization: Bearer <token>

{
  "name": "Mobile iOS App v2",
  "description": "Updated iOS app",
  "is_active": true
}

Alternatively, use PATCH to quickly toggle the active status:

PATCH /api/v1/auth/applications/1/
Authorization: Bearer <token>

{
  "is_active": false
}

Regenerate Credentials

If a secret is compromised or lost, you can invalidate the old credentials and create new ones. This requires a specific explicit string confirmation ("REGENERATE").

POST /api/v1/auth/applications/1/regenerate/
Authorization: Bearer <token>

{
     "confirmation": "REGENERATE"
}

Response 200:

{
  "message": "Credentials regenerated successfully",
  "application": { /* ... */ },
  "credentials": {
    "access_key": "new_key...",
    "access_secret": "new_secret..."
  },
  "warning": "Save the access_secret now! It will never be shown again.",
  "old_credentials_invalidated": true
}

Delete an Application

Permanently deletes the application and revokes its access completely.

DELETE /api/v1/auth/applications/1/
Authorization: Bearer <token>

Security Notes

  1. Hashing: Just like passwords, application secrets are securely hashed using bcrypt and stored in base64. They cannot be recovered by reading the database.
  2. One-Time Display: The access_secret is only returned once when the POST or regenerate endpoint is called. After that, it is inaccessible.
  3. Deactivation: Before completely deleting an application, consider deactivating it (PATCH is_active: false) to temporarily halt its access without losing its history and statistics.

Python API

You can programmatically interact with the Application model inside your Python code:

from tenxyte.models import get_application_model

Application = get_application_model()

# 1. Create Application
app, raw_secret = Application.create_application(
    name="Test Script API", 
    description="For internal testing"
)
print(f"Key: {app.access_key}")
print(f"Secret: {raw_secret}")

# 2. Verify Secret
is_valid = app.verify_secret(raw_secret) # Returns True or False

# 3. Regenerate Credentials
new_credentials = app.regenerate_credentials()
print(new_credentials["access_key"])
print(new_credentials["access_secret"])

Data Model

Application (AbstractApplication)
├── id  (Primary Key)
├── name (string)
├── description (text)
├── access_key (string, unique, indexed)
├── access_secret (string, hashed)
├── is_active (boolean, default: true)
├── allowed_origins (JSON array, default: [])
├── redirect_uris (JSON array, default: [])
├── created_at (datetime)
└── updated_at (datetime)

Note: When allowed_origins is empty, key-only (frontend) auth is disabled and the X-Access-Secret is required. When populated, requests from those origins are accepted with only X-Access-Key.

Note: When redirect_uris is empty, all redirect URIs are permitted (backward-compatible). When populated, only exact matches are allowed during OAuth flows. See Security Guide for details.