Closes BACKLOG B2-8 (autoprovision SSO follow-up). Horilla 1.4 ships with local Django auth as the default backend; layering OIDC on top requires a small Django app + settings overlay. Plus the fork addresses two upstream packaging quirks we hit on dev:
-
Missing
auth.User.is_new_employeemigration. Upstream ships the customized model but no migration to add the column. Today'sprovision-mobieus-horilla.shworks around it viapython manage.py makemigrationsat provision time. Baking the migration in here removes the workaround. -
No
local_settings.pyextension point. Horilla'ssettings.pydoesn't end with the standardtry: from local_settings import *block, so settings overrides require a patch. We add the loader as a one-line patch and ship our overrides as a separate file the Dockerfile drops in.
Live verification today (2026-05-10) confirmed the autoprovision
side: Company + Employee link bypass the load_demo_database
prompt; hr.dev.mobieus.io/ lands on the proper Login page.
What's left is the OIDC sign-in flow on top of that login page.
README.md ← this file
MOBIEUS-PATCHES.md ← drop-in for the fork's root
Dockerfile.mobieus ← image layer on top of horilla/horilla:1.4
mobieus_horilla_oidc/ ← Django app: OIDC backend + URL routes
__init__.py
apps.py
auth.py ← MobieusOIDCBackend
urls.py ← /oidc/login/ + /oidc/callback/
templates/ ← optional template overrides
patches/
0001-add-local-settings-loader.patch ← settings.py end-of-file hook
0002-auth-user-migration.py ← missing auth.User migration
local_settings.py ← Mobieus settings overlay
.github/workflows/mobieus-image.yml ← GHCR image build
Per ADR-0035: Patrick creates the fork, copies the package in,
tags mobieus-1.4-1, GHA builds + pushes the image, then we
repoint mobieus-horilla-docker.compose.yaml.tmpl at the new
image and add the MOBIEUS_OIDC_* env vars.
# Patrick's workstation:
gh repo create baconsammich/mobieus-horilla --public \
--description "Mobieus build of Horilla with OIDC SSO and packaging fixes"
git clone git@github.com:baconsammich/mobieus-horilla.git
cd mobieus-horilla
cp -r /home/patrick/mobieus-io/docs/patches/mobieus-horilla/. .
git add .
git commit -m "MOBIEUS PATCH: initial — OIDC SSO plugin + missing migration + settings hook"
git push -u origin main
git tag mobieus-1.4-1
git push origin mobieus-1.4-1Then in mobieus-io repo, repoint:
# scripts/templates/mobieus-horilla-docker.compose.yaml.tmpl
- image: horilla/horilla:1.4
+ image: ghcr.io/baconsammich/mobieus-horilla:mobieus-1.4-1And add to the per-tenant env block:
MOBIEUS_OIDC_ENABLED: "true"
MOBIEUS_OIDC_ISSUER: https://auth.mobieus.io
MOBIEUS_OIDC_CLIENT_ID: mobieus-hr
MOBIEUS_OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET}
MOBIEUS_OIDC_LOGIN_LABEL: "Sign in with Mobieus"-
Bakes the auth.User migration in. Future tenants don't need the
makemigrationsworkaround inprovision-mobieus-horilla.sh— the patches dir's migration file lands as Django'sauth/migrations/0013_user_is_new_employee.py. Existing tenants that already ran the workaround get a no-op since the column already exists. -
Adds a local_settings.py loader. One-line patch to
horilla/settings.pythat doestry: from .local_settings import *at the end, so subsequent settings overrides (ours and operators') don't need to re-patch upstream. -
Ships local_settings.py. Our overlay registers
mobieus_horilla_oidcinINSTALLED_APPS, prepends the OIDC backend inAUTHENTICATION_BACKENDS(so it's checked before the local password backend), and pulls theMOBIEUS_OIDC_*env vars into Django settings. -
Adds mobieus_horilla_oidc Django app. Authorization-code flow against Hydra with the
client_secret_postworkaround. On first sign-in, looks up the User by email; if present and active, signs them in. If absent, autocreates a User + an Employee record (soinitialize_database_condition()stays happy). Tenant super-admins getis_superuser=True+is_staff=Truebased on the OIDCgroupsclaim. -
Surfaces the "Sign in with Mobieus" button on Horilla's login template via a small Django template-fragment override.
- Doesn't replace local password auth — both backends remain in
AUTHENTICATION_BACKENDS. OIDC is checked first; the local backend stays as a break-glass path for the operator (hr-admin+<slug>@mobieus.io). - Doesn't auto-link existing local users to OIDC identities — only does the bind on first sign-in. If a tenant has existing local users, the email match means they get adopted on first OIDC sign-in.
- Doesn't propagate role demotions back from Mobieus. Same reasoning as the Pretix patch: a missing-group claim could lock out an operator if Hydra returns an unexpected payload. Demotion is an explicit operator action.
- Hydra
client_secret_postworkaround. Same situation as Pretix and the existing wiki/help integrations — the OIDC backend forcesclient_secret_postbecause Hydra rejects the defaultclient_secret_basic. Seeproject_hydra_client_secret_post.mdin mobieus-io memory. - Redirect URI exact-match.
provision-mobieus-horilla.shmust registerhttps://hr.<slug>.mobieus.io/oidc/callback/with themobieus-hrHydra client viaadd-tenant-redirect.sh. Convention same as the wiki/help work. - CSRF + session middleware order. Horilla's middleware
stack needs the OIDC URLs exempt from CSRF on the callback
side; the app's
urls.pydecoratesMobieusCallbackViewwith@csrf_exempt.
Every patch in this fork carries a MOBIEUS PATCH: header in
its commit message and an entry in MOBIEUS-PATCHES.md at the
fork root. See ADR-0035.