π Authentication Setup (Not Permissions) β
β οΈ Important:
This document covers authentication β verifying who a user is.
It does not cover permissions, which define what the user can access or do.
See the Permissions Guide for data permissions.
π§ Authentication vs Permissions β
Authentication answers: βWho are you?β
e.g., validating a token or session.Permissions answer: βAre you allowed to do this?β
e.g., can you view this object? edit this field?
StateZero handles permissions internally, but authentication is delegated to Django REST Framework (DRF).
π Outer Defense: STATEZERO_VIEW_ACCESS_CLASS
β
StateZero uses Django-style permission classes to guard access to its own endpoints.
This is defined via:
# settings.py
STATEZERO_VIEW_ACCESS_CLASS = "rest_framework.permissions.IsAuthenticated"
This setting is like the castle gate β it defines who can even make requests to a StateZero endpoint. It is the outer defense layer, enforced before any actual request processing occurs.
- If a request fails this check, StateZero will not process it at all.
- This gate applies to all StateZero routes: schema discovery, model reads/writes, file uploads, event subscriptions, etc.
- β
You can use classes like
IsAuthenticated
,AllowAny
,IsAdminUser
, orIsStaffUser
. - β Do not use DRF permission classes like
IsAuthenticatedOrReadOnly
,DjangoModelPermissionsOrAnonReadOnly
, or others that assume REST-style verb-based logic. StateZero works with abstract operations, not HTTP verbs β these mixed-mode permission classes will not behave as intended.
π How Authentication Works β
Backend Authentication Use DRF authentication: Token, Session, JWT, etc.
Frontend Configuration Provide a
getAuthHeaders()
function in your StateZero config.Automatic Header Injection StateZero includes those headers in every request.
π§± Django Backend Setup β
In settings.py
:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
STATEZERO_VIEW_ACCESS_CLASS = "rest_framework.permissions.IsAuthenticated"
Install DRF token support:
INSTALLED_APPS = [
# ... other apps
'rest_framework.authtoken',
]
Run migrations:
python manage.py migrate
Add a token login endpoint:
# urls.py
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
path('api/token/', obtain_auth_token, name='api_token_auth'),
# ... other URLs
]
π» Frontend Configuration β
Basic example using localStorage:
// statezero.config.js
function getAuthToken() {
return localStorage.getItem('auth_token');
}
export default {
backendConfigs: {
default: {
API_URL: "http://127.0.0.1:8000/statezero",
GENERATED_TYPES_DIR: "./src/models/",
GENERATED_ACTIONS_DIR: "./src/actions/",
fileUploadMode: "server",
BACKEND_TZ: "UTC",
// Inject the auth header for all requests
getAuthHeaders: () => {
const token = getAuthToken();
return token ? { Authorization: `Token ${token}` } : {};
},
events: {
type: "pusher",
pusher: {
clientOptions: {
appKey: "your_pusher_app_key",
cluster: "your_pusher_cluster",
forceTLS: true,
authEndpoint: "http://127.0.0.1:8000/statezero/events/auth/",
},
},
},
},
},
};
π§ͺ Full Login + Token Management Example β
// auth.js
export class AuthService {
constructor() {
this.baseURL = 'http://127.0.0.1:8000/api';
}
async login(username, password) {
const res = await fetch(`${this.baseURL}/token/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (!res.ok) throw new Error('Login failed');
const data = await res.json();
localStorage.setItem('auth_token', data.token);
return data;
}
logout() {
localStorage.removeItem('auth_token');
}
getToken() {
return localStorage.getItem('auth_token');
}
isAuthenticated() {
return !!this.getToken();
}
}
export const authService = new AuthService();
// statezero.config.js β updated to use auth service
import { authService } from './auth.js';
export default {
backendConfigs: {
default: {
API_URL: "http://127.0.0.1:8000/statezero",
GENERATED_TYPES_DIR: "./src/models/",
GENERATED_ACTIONS_DIR: "./src/actions/",
fileUploadMode: "server",
BACKEND_TZ: "UTC",
getAuthHeaders: () => {
const token = authService.getToken();
return token ? { Authorization: `Token ${token}` } : {};
},
events: {
type: "pusher",
pusher: {
clientOptions: {
appKey: "your_pusher_app_key",
cluster: "your_pusher_cluster",
forceTLS: true,
authEndpoint: "http://127.0.0.1:8000/statezero/events/auth/",
},
},
},
},
},
};
β Summary β
- Authentication = verify who the user is
- Use DRF Token Auth on the backend
- Inject auth headers via
getAuthHeaders()
in the frontend STATEZERO_VIEW_ACCESS_CLASS
defines the outermost access gate- β
Use simple DRF permission classes like
IsAuthenticated
,AllowAny
,IsAdminUser
,IsStaffUser
- β Do not use
IsAuthenticatedOrReadOnly
,DjangoModelPermissionsOrAnonReadOnly
, or similar - StateZero automatically sends headers in all requests
β Authenticated users can now connect to StateZero π What they can do or see is governed by permissions
π Continue Reading: Permissions Guide β
Once authenticated, users must still pass fine-grained permissions to:
- Read or edit models
- View or write fields
- Perform object-level or bulk actions
π Learn how in the StateZero Permissions Guide