Entra registered the URIs with trailing slashes
(https://optical-{dev,prod}.oliver.solutions/hm-aiqc/), but the
JS was producing the URI without a trailing slash because Flask's
request.script_root strips it (X-Script-Name: /hm-aiqc).
Result was AADSTS50011 'Reply address did not match' on every
sign-in attempt. Now always normalise to exactly one trailing
slash, matching what's registered in Entra.
Lifted JWT-cookie auth pattern from the AI QC sibling project:
core/auth/middleware.py validates Azure AD JWTs and stores them in
an httpOnly cookie (hm_aiqc_auth_token). Tenant membership is
enforced by JWTValidator's tid check, which is sufficient for the
tenant-wide access policy chosen for this project.
templates/login.html now drives an MSAL.js popup that POSTs the
ID token to /auth/login. base.html exposes Azure config to all
pages so the logout button can also clear the MSAL session.
app.py's @before_request now checks the JWT cookie and exposes
g.user; modules read user identity via core.auth.current_user_email
so usage logs and created_by columns now record the signed-in
user's email rather than a session value.
Legacy username/password code removed: top-level auth_middleware.py,
jwt_validator.py, deploy/generate_password.py.
- Add Dockerfile, docker-compose.yml, .dockerignore for containerised deployment
- Add deploy/ scripts (deploy.sh, nginx/apache configs, password generator)
- Replace MSAL/Azure AD auth with local username/password authentication
- Add login.html template
- Simplify app.py, middleware, and auth routes for production use
- Update gunicorn_config.py and wsgi.py for Docker/production
- Update templates to work with new auth and URL prefix handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The popup login flow was broken because the Flask 302 redirect from
/ to /reporting/index caused MSAL in the popup to consume the auth
code hash before the parent window could detect it, leaving the
parent stuck on "Authenticating..." while the popup rendered the
full app.
- Switch signIn() from loginPopup() to loginRedirect()
- Add handleRedirectPromise() at start of initAuth() to process
the auth code on page load after returning from Microsoft
- Change root route from 302 redirect to direct template render
so the #code=... hash fragment is preserved for MSAL
- Switch signOut() from logoutPopup() to clearCache()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New blueprint-based module system (hm_qc, video_qc, video_master,
reporting), core framework (database, config, templates), and
unified web interface with progress tracking and tab navigation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>