Authentication
The ZineCore2 API uses token-based authentication for write operations. Read operations are public and do not require authentication.
Authentication Model
Public Read Access
All GET requests are public — no authentication required:
# These work without authentication
curl http://localhost:8000/api/zines/
curl http://localhost:8000/api/agents/
curl http://localhost:8000/api/holdings/
curl http://localhost:8000/api/repositories/
Anyone can:
- List resources
- Retrieve individual records
- Search and filter
- Export data in all formats
Authenticated Write Access
POST, PUT, PATCH, DELETE require authentication:
# These require Authorization header
curl -X POST http://localhost:8000/api/zines/ \
-H "Authorization: Token YOUR_TOKEN" \
-d '{...}'
Authenticated users can:
- Create new records
- Update existing records
- Delete records
Token Authentication
ZineCore2 uses Django REST Framework Token Authentication.
How It Works
- User creates account (via Django admin or registration)
- User obtains token (via /api/auth/token/ endpoint)
- User includes token in Authorization header for all write requests
- Server validates token and authorizes the request
Token Characteristics
- One token per user — tokens are unique to each user account
- Persistent — tokens don't expire (unless manually regenerated)
- Random — 40-character hexadecimal string
- Secure — transmitted via HTTPS in production
Obtaining a Token
Create User Account
First, create a user account via the Django admin interface:
- Visit http://localhost:8000/admin/
- Log in with superuser credentials
- Navigate to Authentication and Authorization > Users
- Click Add User
- Set username and password
- Save
Get Token
Request a token using username and password:
POST /api/auth/token/
Content-Type: application/json
Request body:
{
"username": "your_username",
"password": "your_password"
}
Example:
curl -X POST http://localhost:8000/api/auth/token/ \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "yourpassword"
}'
Response (200 OK):
{
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}
Save this token — you'll use it for all authenticated requests.
Invalid Credentials
Response (400 Bad Request):
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
Using Tokens
Authorization Header
Include the token in the Authorization header:
Authorization: Token YOUR_TOKEN_HERE
Format: Token {token} (note the space after "Token")
Examples
Create a zine:
curl -X POST http://localhost:8000/api/zines/ \
-H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
-H "Content-Type: application/json" \
-d '{
"zine_id": "zine_my_zine",
"title": "My Zine",
"creator": ["agent_me"],
"subject": ["feminism"],
"genre": ["personal-zine"],
"date": ["2024"],
"language": ["en"],
"rights": ["cc-by-4.0"]
}'
Update a zine:
curl -X PATCH http://localhost:8000/api/zines/zine_my_zine/ \
-H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
-H "Content-Type: application/json" \
-d '{
"abstract": "Updated description"
}'
Delete a zine:
curl -X DELETE http://localhost:8000/api/zines/zine_my_zine/ \
-H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
Code Examples
Python
import requests
API_BASE = "http://localhost:8000/api"
# Obtain token
auth_response = requests.post(
f"{API_BASE}/auth/token/",
json={
"username": "admin",
"password": "yourpassword"
}
)
token = auth_response.json()["token"]
print(f"Token: {token}")
# Use token for authenticated requests
headers = {
"Authorization": f"Token {token}",
"Content-Type": "application/json"
}
# Create zine
zine_data = {
"zine_id": "zine_my_zine",
"title": "My Zine",
"creator": ["agent_me"],
"subject": ["feminism"],
"genre": ["personal-zine"],
"date": ["2024"],
"language": ["en"],
"rights": ["cc-by-4.0"]
}
response = requests.post(
f"{API_BASE}/zines/",
headers=headers,
json=zine_data
)
if response.status_code == 201:
print(f"Created: {response.json()['title']}")
else:
print(f"Error: {response.status_code} - {response.json()}")
JavaScript
const API_BASE = "http://localhost:8000/api";
// Obtain token
const authResponse = await fetch(`${API_BASE}/auth/token/`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: "admin",
password: "yourpassword"
})
});
const { token } = await authResponse.json();
console.log(`Token: ${token}`);
// Use token for authenticated requests
const zineData = {
zine_id: "zine_my_zine",
title: "My Zine",
creator: ["agent_me"],
subject: ["feminism"],
genre: ["personal-zine"],
date: ["2024"],
language: ["en"],
rights: ["cc-by-4.0"]
};
const response = await fetch(`${API_BASE}/zines/`, {
method: "POST",
headers: {
"Authorization": `Token ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(zineData)
});
if (response.status === 201) {
const zine = await response.json();
console.log(`Created: ${zine.title}`);
}
Environment Variables
Best practice: Store token in environment variable:
# .env
ZINECORE_TOKEN=9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
Python:
import os
import requests
TOKEN = os.getenv("ZINECORE_TOKEN")
headers = {
"Authorization": f"Token {TOKEN}",
"Content-Type": "application/json"
}
response = requests.post(
"http://localhost:8000/api/zines/",
headers=headers,
json=zine_data
)
JavaScript:
const TOKEN = process.env.ZINECORE_TOKEN;
const headers = {
"Authorization": `Token ${TOKEN}`,
"Content-Type": "application/json"
};
const response = await fetch("http://localhost:8000/api/zines/", {
method: "POST",
headers,
body: JSON.stringify(zineData)
});
Session Authentication (Browsable API)
The Django REST Framework browsable API uses session authentication instead of token authentication.
How to Use
- Visit http://localhost:8000/api/ in your web browser
- Click Log in in the top right corner
- Enter your username and password
- You're now authenticated for browsable API use
Features
- Interactive HTML forms for POST/PUT/PATCH
- Syntax-highlighted responses
- No need to include Authorization header (uses session cookie)
Use for:
- Manual testing
- API exploration
- Quick debugging
Not for:
- API clients (use token authentication)
- Automated scripts (use token authentication)
Error Responses
Missing Authentication
Request:
curl -X POST http://localhost:8000/api/zines/ \
-H "Content-Type: application/json" \
-d '{...}'
Response (401 Unauthorized):
{
"detail": "Authentication credentials were not provided."
}
Fix: Include Authorization: Token YOUR_TOKEN header.
Invalid Token
Request:
curl -X POST http://localhost:8000/api/zines/ \
-H "Authorization: Token invalid_token_here" \
-d '{...}'
Response (401 Unauthorized):
{
"detail": "Invalid token."
}
Fix:
- Check token is correct
- Regenerate token if needed
Insufficient Permissions
Response (403 Forbidden):
{
"detail": "You do not have permission to perform this action."
}
Fix:
- Ensure user account has appropriate permissions
- Contact administrator to grant permissions
Token Management
Regenerate Token
If your token is compromised or you need a new one:
Via Django shell:
python manage.py shell
from django.contrib.auth import get_user_model
from rest_framework.authtoken.models import Token
User = get_user_model()
user = User.objects.get(username='your_username')
# Delete old token
Token.objects.filter(user=user).delete()
# Create new token
token = Token.objects.create(user=user)
print(f"New token: {token.key}")
Via Django admin:
- Visit http://localhost:8000/admin/
- Navigate to Auth Token > Tokens
- Find your user's token
- Delete it
- A new token will be created automatically on next login
View Token
Via Django shell:
from rest_framework.authtoken.models import Token
token = Token.objects.get(user__username='your_username')
print(token.key)
Via Django admin:
- Visit http://localhost:8000/admin/
- Navigate to Auth Token > Tokens
- Find your user's token
- View the key
Security Best Practices
Use HTTPS in Production
Always use HTTPS when transmitting tokens:
# Production (HTTPS)
curl https://api.zinecore.example.com/api/zines/ \
-H "Authorization: Token YOUR_TOKEN"
# NOT http:// (insecure!)
Don't Commit Tokens
Never commit tokens to version control:
# .gitignore
.env
*.token
secrets.json
Rotate Tokens Regularly
Regenerate tokens periodically, especially:
- After suspected compromise
- When team members leave
- Every 6-12 months
Limit Token Scope
Consider creating separate user accounts for different purposes:
- One for development/testing
- One for production scripts
- One for each external integration
Monitor Token Usage
Check server logs for unusual patterns:
- Requests from unexpected IPs
- High request volumes
- Failed authentication attempts
Alternative Authentication Methods
ZineCore2 also supports session authentication (for browsable API) but token authentication is recommended for API clients.
Why Token Authentication?
Advantages:
- Stateless — no server-side session storage
- Simple — just include token in header
- Portable — works across different clients
- Secure — no cookies, no CSRF concerns
Disadvantages:
- No expiration — tokens don't expire automatically
- Manual management — must regenerate manually if compromised
Testing Authentication
Test Token Endpoint
# Should succeed
curl -X POST http://localhost:8000/api/auth/token/ \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "yourpassword"}'
# Should return token
# {"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"}
Test Authenticated Request
# Get your token first
TOKEN=$(curl -X POST http://localhost:8000/api/auth/token/ \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "yourpassword"}' \
| jq -r '.token')
# Use token for authenticated request
curl -X POST http://localhost:8000/api/zines/ \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{...}'
Test Public Access
# Should work without authentication
curl http://localhost:8000/api/zines/
# Should return 401 without authentication
curl -X POST http://localhost:8000/api/zines/ \
-H "Content-Type: application/json" \
-d '{...}'
Troubleshooting
"Unable to log in with provided credentials"
Cause: Incorrect username or password
Fix:
- Check username and password are correct
- Ensure user account exists
- Reset password via Django admin if needed
"Invalid token"
Cause: Token is incorrect or has been regenerated
Fix:
- Request a new token via /api/auth/token/
- Check token for typos
- Ensure token format is correct: Authorization: Token YOUR_TOKEN
"Authentication credentials were not provided"
Cause: Missing Authorization header
Fix:
- Include header: -H "Authorization: Token YOUR_TOKEN"
- Check header name is Authorization (capital A)
Token not working after regeneration
Cause: Using old token
Fix:
- Delete old token from database
- Request new token
- Update your environment variables/config
Next Steps
- Zines API — Create your first authenticated request
- Filtering — Advanced filtering and search
- First Steps — Complete tutorial with authentication