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):
SecurityMiddleware— security headers.SessionMiddleware— sessions.AuthenticationMiddleware— attachesrequest.user.CommonMiddleware— Django common middleware.OAuthDomainMiddleware— on the configured global/OAuth domain only, routes/o/*tooauth2_provider. OAuth endpoints 404 on any other domain.TenantMainMiddleware— the core of multi-tenancy.EventSetupMiddleware— resolvesrequest.current_event.WhiteNoiseMiddleware— static files.CorsMiddleware→CsrfViewMiddleware→MessageMiddleware→XFrameOptionsMiddleware.
Step by step¶
1. TenantMainMiddleware¶
Reads the host.
Looks up
Domain→Tenant. If found, switches the DB connection to that tenant’s schema (connection.set_schema(tenant.schema_name)).Auto-creates a
Membershipfor authenticated users visiting a tenant they have none in.Sets the URLconf:
TENANT_URLCONFfor tenant domains,PUBLIC_SCHEMA_URLCONFfor 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_eventfrom the subdomain, or from aCustomDomaintied to a specific event (statusSSL_ISSUED).localhostfalls back to the first active event (handy for dev).Attaches
request.current_custom_domainwhen 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 DRFViewSets (on aDefaultRouter, typically atapi/m2m/) and function-based@api_viewviews. So the base path is/app/<module>/, with an internalapi/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-originReferer, and skips/loginreferrers to avoid loops.The frontend builds the login URL via
buildLoginUrl()(inui/src/app/utils/login-url.ts), capturing the currentpathname+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.py—MIDDLEWARE,ROOT_URLCONF, URLconf settings.midsummer/middleware.py— the three custom middleware classes.midsummer/urls.py— root URLconf.midsummer/login/views.py—safe_next/_resolve_next.midsummer/common/views.py—angular_page/angular_page_event_protected.