Commit Graph

3 Commits

Author SHA1 Message Date
Allison
0513e67838 feat(auth): B-2.4 OAuth Microsoft/Google + magic link (Loi 25 deferred consent)
Adds Microsoft 365 + Google OAuth providers (separate from the existing
generic OIDC SSO at src/auth/sso.py) and a passwordless magic-link login
flow. New OAuth signups capture Loi 25 art. 14 consents (4 granular
checkboxes) BEFORE creating the User row via /auth/oauth/finish-signup.

Per compatibility-audit.md C2:
- No src/auth_extended/ directory — extends src/auth/ in place
- No new User columns — reuses sso_provider/sso_subject + email_verified
- Magic-link tokens via itsdangerous URLSafeTimedSerializer (15-min, no DB)
- All routes added to existing auth_bp; templates extend marketing/base.html
- Anti-enumeration on /auth/magic-link (generic flash for unknown OR
  unverified emails) and /auth/magic-link/<token> (same flash for
  invalid/expired/unverified-user)

Files added:
- src/auth/oauth_providers.py — Microsoft + Google OAuth registration,
  is_oauth_provider_enabled(), find_user_by_oauth(), create_oauth_user_with_consent()
- src/auth/magic_link.py — generate/consume magic-link tokens
- templates/auth/magic_link_request.html, templates/auth/oauth_finish_signup.html
- tests/test_oauth_magic_link.py + tests/_run_oauth_magic_link_windows.py (16 tests)
- config/env.oauth.example

Files modified:
- src/api/auth.py — 5 new routes (oauth_provider_login/callback,
  oauth_finish_signup, magic_link_request/consume); login flashes translated FR;
  oauth_*_enabled flags passed to login template
- src/app.py — wires init_oauth_providers(app) after blueprint registration
- src/services/email.py — adds send_magic_link_email() (FR + DictIA brand)
- templates/login.html — refondu IN PLACE (was 178 lines legacy Vue/TW3)
  to extend marketing/base.html with OAuth buttons, password form,
  magic-link CTA, signup link
- templates/auth/check_email.html — adds action='magic_link' branch
- static/css/tailwind.config.js — adds templates/login.html to content
- static/css/marketing.css — rebuilt

Tests: 16/16 PASS via Windows manual driver.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:29:24 -04:00
Allison
3b324ad0b9 fix(auth): B-2.2 review fixes — Tailwind path + WCAG + race + flash + tests
C-1: Add templates/register.html (and templates/auth/**) to tailwind.config.js
content array so utility classes used by the signup template don't get purged
on next build. Rebuilt static/css/marketing.css; verified text-brand-navy/90
and min-h-[calc(100vh-62px)] are now compiled.

I-1: Replace flash() calls for missing required consents with WTForms
field-level errors (form.consent_cgu.errors.append / form.consent_confidentialite
.errors.append). Errors render inline next to each consent checkbox via
{% if form.consent_cgu.errors %}<p role="alert">…</p>{% endif %}. Prevents
session-backed flash messages from leaking across unrelated navigations.

I-2: Wrap user creation + flush in IntegrityError retry loop (max 5 attempts);
import IntegrityError from sqlalchemy.exc. Absorbs the inherent race between
_generate_unique_username's lookup and the subsequent flush under concurrent
signups. Added docstring note to _generate_unique_username explaining the
wrapper.

I-3: Move db.create_all() inside the try/finally in
test_signup_route_csrf_enforced so WTF_CSRF_ENABLED is restored even if
table creation fails.

I-4: Pin test_signup_rejects_duplicate_email assertion to status_code == 200
(WTForms validate_email raises ValidationError → form fails validation →
fall-through to default 200 render_template).

I-5: Add id="password-help" to the password help paragraph and
aria-describedby="password-help" to the password input so screen readers
announce the password requirements when the field is focused.

I-6: Bump flash banner text colors from -700/-800 to -900 variants
(text-amber-900, text-blue-900, text-red-900, text-green-900) for safer
WCAG 2.2 AA contrast against the -50 backgrounds. Same bump applied to the
new consent and password inline error renders.
2026-04-27 22:43:00 -04:00
Allison
b27b3c1d44 feat(marketing): bootstrap Tailwind v4 + design tokens FlexiHub
Adds Tailwind v4 / PostCSS pipeline that compiles to static/css/marketing.css,
to be loaded only by future templates/marketing/** templates and to coexist
with the existing legacy v3 JIT runtime used by index.html / account.html /
admin.html. The legacy v3 runtime stays untouched.

- package.json: postcss-cli build:css and watch:css scripts
- postcss.config.js: @tailwindcss/postcss + autoprefixer
- static/css/tailwind.config.js: brand tokens (b1/b2/b3, navy, navy2, navy3,
  bg, border), font families, brand-grad, cta shadows, FlexiHub keyframes
  (tc-fade-in-up/right, tc-float-y, tc-pulse-glow, plus-breathe). content
  paths scoped to marketing/legal/billing/macros only - purge cannot touch
  legacy templates.
- static/css/input.css: @import "tailwindcss"; + @config directive (Tailwind
  v4 backward-compat for v3-style JS config). @font-face Inter Variable +
  JetBrains Mono Variable (woff2). base layer body font/color, h1-h3
  letter-spacing. utilities: grad-text, grad-bg, eyebrow.
- Dockerfile: new stage 3 'assets-builder' (node:20-alpine) compiles CSS;
  runtime stage copies the built file in via --from=assets-builder, after
  COPY . . so the freshly-built file always wins.
- .gitignore + .dockerignore: exclude node_modules.

Build verified locally: marketing.css = 121 KB minified (Tailwind v4 ships
all default theme tokens + reset properties even with empty content;
realistic baseline, will not grow much as marketing markup is added).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 15:23:25 -04:00