Planned improvements for the authentication system to increase resilience and align with the SIAAS data model.
These changes are low-risk and should be done regardless of other work.
Files: dsp_clinic/settings.py, users/urls.py
Problem: AUTHENTICATION_BACKENDS still includes CrossClientModelBackend and ModelBackend. Anyone who discovers /accounts/login/ can authenticate via password, creating a Django session with no oidc_id_token. This breaks logout (no token to send to Keycloak) and bypasses the intended Keycloak-only flow.
Fix:
CrossClientModelBackend and ModelBackend from AUTHENTICATION_BACKENDS/accounts/login/ that sends users to /oidc/authenticate//admin/login/) uses its own auth flow — verify it still works_load_dynamic_aliasesFile: tenancy/routers.py
Problem: The function catches ALL exceptions silently with except Exception: pass. If the query for client tenants fails, no error is logged — making debugging very difficult.
Fix: Replace pass with logger.exception(...).
File: users/oidc_views.py
Problem: The oidc_id_token has a 5-minute expiry. If a user stays logged in for hours and then clicks Logout, Keycloak may reject the stale token with "Invalid parameter: id_token_hint". The user sees an error page instead of being logged out.
Fix: If Keycloak returns an error response during logout, fall back to local logout (clear Django session, redirect home).
The SectorDatabaseRouter already has logic to route users app operations to client tenant databases when a request has tenant context:
# tenancy/routers.py (lines 110-116)
if app_label == 'users':
tenant = get_current_tenant()
if tenant and tenant.sector.slug == 'client':
client_db = _client_db_alias(tenant)
if client_db:
return client_db
return 'default'
And allow_migrate allows users tables on both databases:
if app_label == 'users':
return db == 'default' or db.startswith('sector_client_')
This means users should be created in client tenant databases when the request comes through a partner subdomain. But currently all users (including patients) appear in the default database.
Verify tenant context during OIDC callback:
KeycloakOIDCBackend.create_user() to log the current tenant and database aliastest-client.gp.veripath.co.ukIf tenant is missing from thread-local during callback:
TenantMiddleware runs set_current_tenant() before the viewauth.authenticate() might run on a different thread or the thread-local might be clearedIf tenant IS present but router returns 'default':
_client_db_alias(tenant) — does it return the correct alias?settings.DATABASES_load_dynamic_aliases() function might be failing silently (see fix 1.2)Once the routing is fixed for new users, existing patient/receptionist/clinician users must be migrated from the default database to their respective client tenant databases:
sector_client_{subdomain} databaseOnce Phase 2 is complete, the Django admin (which queries only the default database) will naturally show only:
matthew, mstickels, etc.)Patient and operational staff accounts will no longer appear because they live in client tenant databases.
No code change needed — this is a side effect of Phase 2.
| Phase | Priority | Risk | Effort |
|---|---|---|---|
| 1.1 Remove password backends | High | Low | Small |
| 1.2 Log silent exceptions | High | Low | Small |
| 1.3 Expired token logout fallback | Medium | Low | Small |
| 2. Fix user database routing | High | Medium | Medium |
| 3. Django admin cleanup | Low | None | None |