🔑 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¶
- Navigate to Django admin:
http://localhost:8000/admin/ - Go to Django Admin MCP > MCP Tokens
- Click Add MCP Token
-
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
-
Click Save
- 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
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:
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¶
- Permissions — Detailed permission system guide
- Client Setup — Configure MCP clients