Request resolution

How a raw HTTP request becomes a (tenant, event, schema, view) in Midsummer. This walk through the middleware stack is essential context for anything that touches routing, domains, or auth redirects.

The middleware stack (in order)

From midsummer/settings.py MIDDLEWARE (showing the meaningful ones):

  1. SecurityMiddleware — security headers.

  2. SessionMiddleware — sessions.

  3. AuthenticationMiddleware — attaches request.user.

  4. CommonMiddleware — Django common middleware.

  5. OAuthDomainMiddleware — on the configured global/OAuth domain only, routes /o/* to oauth2_provider. OAuth endpoints 404 on any other domain.

  6. TenantMainMiddleware — the core of multi-tenancy.

  7. EventSetupMiddleware — resolves request.current_event.

  8. WhiteNoiseMiddleware — static files.

  9. CorsMiddlewareCsrfViewMiddlewareMessageMiddlewareXFrameOptionsMiddleware.

Step by step

1. TenantMainMiddleware

  • Reads the host.

  • Looks up DomainTenant. If found, switches the DB connection to that tenant’s schema (connection.set_schema(tenant.schema_name)).

  • Auto-creates a Membership for authenticated users visiting a tenant they have none in.

  • Sets the URLconf: TENANT_URLCONF for tenant domains, PUBLIC_SCHEMA_URLCONF for the public domain.

  • Attaches request.tenant.

2. OAuthDomainMiddleware

Only active on the configured OAuth/global domain. If the path starts with /o/, it routes to oauth2_provider URLs. On any other domain, /o/* is a 404. This keeps OAuth provider endpoints scoped to a single canonical domain.

3. EventSetupMiddleware

  • Resolves request.current_event from the subdomain, or from a CustomDomain tied to a specific event (status SSL_ISSUED).

  • localhost falls back to the first active event (handy for dev).

  • Attaches request.current_custom_domain when applicable.

After these three, a view receives:

  • request.tenant — the owning org (schema already switched).

  • request.current_event — the event for this host.

  • The correct URLconf (tenant vs. public).

URL routing model

midsummer/urls.py (root)
  ├─ /admin/                    Django admin
  ├─ /o/                        OAuth2 provider (OAuthDomainMiddleware-gated)
  ├─ /login, /login/...         auth views (midsummer/login/)
  ├─ /app/<module>/...          app routers: register, staff, schedule,
  │                             vendors, shop, account, kioskos
  ├─ /api/v2026-01/...          versioned public API
  ├─ /ui/...                    submenu + trigger endpoints
  └─ SPA shell helpers          angular_page / angular_page_event_protected
  • REST lives under each app’s urls.py, mounted via /app/<module>/. Apps mix DRF ViewSets (on a DefaultRouter, typically at api/m2m/) and function-based @api_view views. So the base path is /app/<module>/, with an internal api/ prefix — not /api/<module>/.

  • The SPA shell is served by midsummer.common.views.angular_page / angular_page_event_protected: a thin template that boots the named Angular app. Per-module page routes (e.g. system/audit-log/) are permission-gated in Python, then served by the SPA shell.

The login redirect (?next=)

After login, users are returned to the page they came from. The login views (midsummer/login/views.py) build a safe redirect:

  • safe_next() accepts only same-origin / server-relative paths, and rejects backslashes, control characters, and non-http(s) schemes (javascript:, data:) to prevent open-redirect (//evil.com, /\evil.com).

  • _resolve_next() prefers ?next=, falls back to a same-origin Referer, and skips /login referrers to avoid loops.

  • The frontend builds the login URL via buildLoginUrl() (in ui/src/app/utils/login-url.ts), capturing the current pathname+search.

Note

The ui/ login page is Django-served only (no Angular /login route). tenantui/ has both a Django-served /login/ and an Angular /login route guarded by tenant-admin.guard.ts.

Where to look

  • midsummer/settings.pyMIDDLEWARE, ROOT_URLCONF, URLconf settings.

  • midsummer/middleware.py — the three custom middleware classes.

  • midsummer/urls.py — root URLconf.

  • midsummer/login/views.pysafe_next / _resolve_next.

  • midsummer/common/views.pyangular_page / angular_page_event_protected.