Skip to content

🔑 Token Management

Django Admin MCP uses token-based authentication for all API requests. This guide covers creating, managing, and securing tokens.

🆕 Creating Tokens

Via Django Admin

  1. Navigate to Django admin: http://localhost:8000/admin/
  2. Go to Django Admin MCP > MCP Tokens
  3. Click Add MCP Token
  4. Configure the token:

    • Name — Descriptive identifier (e.g., "MCP - Development")
    • User — Associated user for audit logging (required)
    • Is Active — Enable/disable the token
    • Expires At — Expiration date (default: 90 days from now)
    • Groups — Assign groups for permission inheritance
    • Permissions — Assign individual permissions
  5. Click Save

  6. Copy the generated token — it is only displayed once after creation

Via Django Shell

from django_admin_mcp.models import MCPToken
from django.contrib.auth.models import User, Permission

# Create a token
user = User.objects.get(username='admin')
token = MCPToken.objects.create(
    name='API Token',
    user=user,
)

# Add permissions
view_article = Permission.objects.get(codename='view_article')
token.permissions.add(view_article)

# Get the plaintext token (only available immediately after creation)
print(f"Token: {token.get_plaintext_token()}")

📋 Token Properties

🔐 Token Format

Tokens use a structured mcp_<key>.<secret> format:

  • The key (token_key) is stored in plaintext for O(1) lookup
  • The secret is hashed with a per-token salt (token_hash + salt) using SHA-256
  • Constant-time comparison prevents timing attacks
mcp_abc12345.secretparthere...

Token Security

The full token is only displayed once after creation. The secret portion is hashed and cannot be recovered. Store tokens securely.

⏰ Expiration

Tokens have an optional expiration date:

Configuration Behavior
expires_at set Token expires at that datetime
expires_at blank Token never expires
Default 90 days from creation

Check token validity:

token = MCPToken.objects.get(name='My Token')
if token.is_valid():
    print("Token is active and not expired")

🔄 Active Status

The is_active field allows quick enable/disable without deletion:

# Disable a token
token.is_active = False
token.save()

# Re-enable later
token.is_active = True
token.save()

📊 Usage Tracking

Each token tracks its last usage:

token = MCPToken.objects.get(name='My Token')
print(f"Last used: {token.last_used_at}")

This is automatically updated on each authenticated request.

🔒 Permission Assignment

🎯 Direct Permissions

Assign permissions directly to the token:

from django.contrib.auth.models import Permission

token = MCPToken.objects.get(name='My Token')

# Add view permission for Article
view_article = Permission.objects.get(
    codename='view_article',
    content_type__app_label='blog'
)
token.permissions.add(view_article)

# Add multiple permissions
add_article = Permission.objects.get(codename='add_article')
change_article = Permission.objects.get(codename='change_article')
token.permissions.add(add_article, change_article)

👥 Group Permissions

Assign groups to inherit their permissions:

from django.contrib.auth.models import Group

token = MCPToken.objects.get(name='My Token')

# Create a group with permissions
editors = Group.objects.get(name='Editors')
token.groups.add(editors)

✅ Check Permissions

token = MCPToken.objects.get(name='My Token')

# Check single permission
if token.has_perm('blog.view_article'):
    print("Can view articles")

# Get all permissions
perms = token.get_all_permissions()
print(f"Permissions: {perms}")

User Permissions Not Inherited

Token permissions are independent of the associated user's permissions. A superuser can have a token with limited access.

🛡️ Security Best Practices

🔐 Principle of Least Privilege

Create tokens with only the permissions needed:

# Read-only token
readonly_token = MCPToken.objects.create(name='Read Only')
readonly_token.permissions.add(
    Permission.objects.get(codename='view_article'),
    Permission.objects.get(codename='view_author'),
)

# Full access token
full_token = MCPToken.objects.create(name='Full Access')
full_token.permissions.add(
    *Permission.objects.filter(
        content_type__app_label='blog'
    )
)

⏰ Use Expiration Dates

Always set expiration for production tokens:

from datetime import timedelta
from django.utils import timezone

token = MCPToken.objects.create(
    name='Production Token',
    expires_at=timezone.now() + timedelta(days=30),
)

🔄 Rotate Tokens Regularly

Create new tokens and deactivate old ones:

# Create new token
new_token = MCPToken.objects.create(
    name='Production Token v2',
    user=old_token.user,
)
new_token.permissions.set(old_token.permissions.all())

# Deactivate old token
old_token.is_active = False
old_token.save()

📊 Audit Token Usage

Monitor token usage via last_used_at:

from django.utils import timezone
from datetime import timedelta

# Find unused tokens
unused = MCPToken.objects.filter(
    last_used_at__lt=timezone.now() - timedelta(days=30)
)

# Consider deactivating or deleting
for token in unused:
    print(f"Unused token: {token.name}")

🌐 Using Tokens

Include the token in the Authorization header:

curl -X POST http://localhost:8000/mcp/ \
  -H "Authorization: Bearer mcp_yourkey.yoursecret" \
  -H "Content-Type: application/json" \
  -d '{"method": "tools/list"}'

🔗 Next Steps