- The Python backend lives in
backends/python/apiand is built on Django. FastAPI is not used in this project. - A single
mainapp exposes REST endpoints under/api*, handles authentication, models, and helper decorators. - Auth combines Bitrix24 OAuth (via
b24pysdk) with internal JWTs issued from rows in thebitrix24accounttable. - This document covers configuration, request lifecycle, and dev practices for maintaining and extending the current implementation.
- Django — framework responsible for middleware, ORM, admin, and WSGI/ASGI entrypoints.
- b24pysdk==0.2.3a1 — SDK that wraps Bitrix24 OAuth, REST, and events.
- PostgreSQL + psycopg2-binary — default database accessed through Django ORM.
- PyJWT — generates and validates internal JWT tokens.
- django-cors-headers — sets CORS/X-Frame headers so the app can render inside Bitrix24.
- environs — loads configuration from
.env/ environment variables. - gunicorn — production WSGI server (see
Dockerfile).
Django
psycopg2-binary
django-cors-headers
PyJWT
gunicorn
environs
b24pysdk==0.2.3a1backends/python/api/
├── asgi.py / wsgi.py # standard Django entrypoints
├── config.py # Config dataclass + .env loader
├── Dockerfile # multi-stage (dev/prod)
├── manage.py # Django CLI
├── requirements.txt
├── settings.py / urls.py # global settings and routing
└── main/
├── admin.py # model registration
├── models.py # Bitrix24Account, ApplicationInstallation
├── urls.py # /api*, /api/health etc.
├── utils/
│ ├── authorized_request.py
│ └── decorators/
│ ├── auth_required.py
│ ├── collect_request_data.py
│ └── log_errors.py
└── views.py # HTTP handlers
Tables
bitrix24accountandapplication_installationare markedmanaged = False, so their schema is owned by another service (PHP backend). Django only works with pre-existing tables.
Config aggregates environment parameters via environs.Env and is exported as the singleton config. Every other module (including settings.py and models) reads values from here.
| Variable | Purpose | Default |
|---|---|---|
BUILD_TARGET |
dev / production; controls DEBUG |
dev |
DB_NAME |
DB name | appdb |
DB_USER |
DB user | appuser |
DB_PASSWORD |
DB password | apppass |
DB_HOST / PORT |
PostgreSQL address (database / 5432 in Docker) |
database / 5432 |
NGROK_AUTHTOKEN |
Ngrok authtoken | empty |
JWT_SECRET |
Used as Django SECRET_KEY and JWT secret |
default_jwt_secret |
JWT_ALGORITHM |
JWT signing algorithm | HS256 |
CLIENT_ID |
Bitrix24 OAuth client ID | client_id |
CLIENT_SECRET |
Bitrix24 OAuth client secret | client_secret |
VIRTUAL_HOST |
External URL; populates CSRF_TRUSTED_ORIGINS |
app_base_url |
Extra variables (e.g. ENABLE_RABBITMQ) are read by the Makefile during docker compose runs.
SECRET_KEY = config.jwt_secret,DEBUGcomes fromBUILD_TARGET.ALLOWED_HOSTSandCSRF_TRUSTED_ORIGINSare derived fromVIRTUAL_HOST; fallback domains arelocalhost,api-python.INSTALLED_APPScontains Django defaults +corsheaders+main.MIDDLEWAREstarts withCorsMiddlewareto ensure headers are added first.DATABASES['default']usesdjango.db.backends.postgresql_psycopg2with config values.CORS_ALLOW_ALL_ORIGINS = Truefor convenience in dev; tighten it for prod.
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"corsheaders",
"main",
]make dev-python— main workflow, launches profilesfrontend,python,ngrok(+queueif.envsetsENABLE_RABBITMQ=1).make prod-python— build + run the Python backend in production mode only.
cd backends/python/api
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python manage.py migrate --noinput
python manage.py runserver 0.0.0.0:8000The Dockerfile pipeline automatically runs makemigrations, migrate, and createsuperuser --noinput, but you can execute them manually locally.
- base:
python:3.11-slim, installspostgresql-clientand Python deps. - dev: mounts the project as a volume and runs
runserverafter migrations. - prod: copies source into the image and starts Gunicorn (
gunicorn wsgi:application --bind 0.0.0.0:8000).
| Method | Path | View | Description |
|---|---|---|---|
| GET | /api |
root |
Quick "Python Backend is running" response |
| GET | /api/health |
health |
Health-check with status + timestamp |
| GET | /api/enum |
get_enum |
Returns a static list of options |
| GET | /api/list |
get_list |
Returns a static list of items |
| POST | /api/install |
install |
Creates/updates ApplicationInstallation |
| POST | /api/getToken |
get_token |
Issues a new JWT |
All handlers are decorated with @xframe_options_exempt so Bitrix24 can embed them in an iframe.
- The simple GET endpoints serve as templates — extend them as needed.
installstoresApplicationInstallationentries for the Bitrix24 portal, using fields fromrequest.bitrix24_account.get_tokencallsBitrix24Account.create_jwt_token()(default TTL 60 minutes).- Every view is wrapped in
@auth_requiredand@log_errors, so exceptions become JSON 500 responses and are logged automatically.
AuthorizedRequestextendsHttpRequestwithbitrix24_accountto keep type hints clean.collect_request_datamerges JSON body + query + form params intorequest.data, handling multi-value keys carefully.auth_required:- Looks for header
Authorization: Bearer <jwt>. - If present, calls
Bitrix24Account.get_from_jwt_token()and assigns it torequest.bitrix24_account. - If missing, parses
OAuthPlacementDatafromrequest.dataand callsBitrix24Account.update_or_create_from_oauth_placement_data()(via the SDK). The resulting account is stored on the request as well. - Errors (
DoesNotExist,ExpiredSignature,BitrixValidationError) are converted into JSON responses with status 400/401.
- Looks for header
log_errors("name")captures exceptions and logs them through standardlogging.
@log_errors("get_token")
@auth_required
def get_token(request: AuthorizedRequest):
return JsonResponse({"token": request.bitrix24_account.create_jwt_token()})Bitrix24AccountextendsAbstractBitrixTokenand maps to tablebitrix24account(UUID PK). Key methods:bitrix_app— class property that buildsBitrixAppfromCLIENT_ID/CLIENT_SECRET.client— wrapper aroundb24pysdk.Clientfor REST calls.create_jwt_token(minutes=60)/get_from_jwt_token— issue and verify internal tokens via PyJWT.update_or_create_from_oauth_placement_data— main entrypoint used byauth_required, creates or updates an account based on OAuth payloads.- Signals (
portal_domain_changed_signal,oauth_token_renewed_signal) keep the record in sync with Bitrix24 events.
ApplicationInstallationstores installation status for a portal and has aOneToOnelink toBitrix24Account.
- Both models are registered with dynamic
list_display;idis read-only. - Dev superuser is created automatically (
createsuperuser --noinputinside Docker). Admin URL:/api/admin/.
- Bitrix24 calls the backend and sends OAuth placement payload.
collect_request_datacombines JSON + query params intorequest.data.auth_requiredconverts the payload intoOAuthPlacementDataand callsBitrix24Account.update_or_create_from_oauth_placement_data()(SDK also fetchesapp_info).- After authorization:
installcreates/updatesApplicationInstallation.get_tokenissues a JWT and returns it to the client.
- The frontend stores the JWT and sends it in the
Authorizationheader on future requests;auth_requiredthen just validates the token without calling Bitrix24 APIs.
- Keep
JWT_SECRET, OAuth keys, and DB params inside.env/ CI secrets. Do not ship defaults to production. - Rotate JWTs regularly (TTL is the
minutesargument ofcreate_jwt_token). When it expires, the frontend should call/api/getTokenor repeat the OAuth flow. CSRF_TRUSTED_ORIGINSis derived automatically, but if several Bitrix24 domains are involved, list them explicitly viaVIRTUAL_HOSTor extend the logic.- For production set
CORS_ALLOWED_ORIGINS/CORS_ALLOW_CREDENTIALSto restrict origins. - Attach centralized logging (Sentry/ELK). Currently
log_errorswrites to standard logging only.
- The Docker image is based on
python:3.11-slim. Keeprequirements.txtminimal to avoid bloating the image. - Before shipping, refresh
.env: DB params, OAuth creds, JWT secret,VIRTUAL_HOST. docker compose --env-file .env up --builduses the selected profiles (COMPOSE_PROFILES=pythonfor production).- In Kubernetes or similar platforms, run
python manage.py migrateas a separate job to prevent migration races.
- Use
pytest+pytest-djangoor Django's built-inmanage.py test. - Cover:
auth_required(JWT vs OAuth branches, PyJWT errors,BitrixValidationError).Bitrix24Account.create_jwt_token/get_from_jwt_token(invalid secret, expiry handling).- Views
install/get_tokenwith mocked models + SDK.
- For integration tests, rely on
django.test.Clientand monkeypatchb24pysdk.
Invalid JWT token—.envsecret differs from the one used to issue the token. Re-issue via/api/getToken.JWT token has expired— increase TTL or implement auto-renewal on the frontend.- CSRF / iframe issues — verify
VIRTUAL_HOSTand the Bitrix24 portal domain. BitrixValidationErrorduring install — ensure payload has required fields (domain,member_id,auth[access_token], etc.).- Database errors — confirm the
databasecontainer is running and reachable viaDB_HOST.
instructions/python/bitrix24-python-sdk.md— SDK usage details and REST examples.instructions/python/code-review.md— Python code review checklist.instructions/queues/python.md— background processing (Celery/RabbitMQ) guidelines.- Root
README.mdandmakefiledescribe docker profiles and run scenarios for the entire stack.
Updated: 5 December 2025.