6 Commits

Author SHA1 Message Date
kawa c4a1775d7d Harden auth, headers, and container per OWASP review
- Add per-account lockout + IP rate limiter on local sign-in (A07)
- Emit CSP and security headers on every response (A05)
- Run container as non-root `app`, /data 0700 (A05/A02)
- Stop reflecting raw token-endpoint body into redirect URL (A09)
- Handle missing refresh_token in connect callback without a 500

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 14:30:19 +02:00
kawa 17f6010a93 Fix open-redirect token leak and related auth hardening
Security review fixes:
- Constrain OAuth connect returnUrl to a site-relative path so the
  redeemable token_key can't be redirected off-domain (was a refresh-
  token leak / connection hijack)
- Route all login redirects (entra/dev/local) through ToLocalReturnUrl,
  also closing a protocol-relative // open redirect in local-login
- Neutralize CSV formula prefixes in both audit-log exporters via
  CsvSanitizer
- Force Secure flag on the prod auth cookie (Always, not SameAsRequest)
- Gate admin pages with an app_role-claim "Admin" policy instead of a
  render-time check

Findings and rationale recorded in SECURITY-TODO.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:39:20 +02:00
kawa e6ae06e29d Merge branch 'main' of https://git.azuze.fr/kawa/SharepointToolbox-Web 2026-06-02 17:39:52 +02:00
kawa bcced08caf Register Entra app via secretless device-code bootstrap
AADSTS700016 came from the register flow sending the configured
Oidc:ClientId (still a placeholder) as the auth client. The desktop
reference app never needs config: it bootstraps with the first-party
"Microsoft Graph Command Line Tools" public client (14d82eec-...) via
MSAL interactive, which exists in every tenant.

Replicate that for the web app. A server can't do MSAL loopback and the
bootstrap client's redirect URIs don't include /connect/callback, so use
the OAuth 2.0 device authorization grant instead — the web-equivalent of
the desktop interactive flow:

- Add EntraDeviceCodeFlow: POST /devicecode then poll /token with the
  bootstrap client. No backing app, no client id/secret, no redirect URI.
- Profiles "Register in Entra" now shows the verification URL + user code
  and polls until the admin signs in, then calls AppRegistrationService
  to create the per-client app and adopts its appId.
- Remove the dead /connect/register-initiate endpoint and the
  IsRegistration branch from the callback (connect flow only now).

The client-tenant register/connect flows are now fully secretless. The
Oidc:* config is used only by the toolbox's own sign-in (unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:47:23 +02:00
kawa 0a0c59319f Make Entra app-registration flow secretless (public PKCE)
The register flow exchanged the auth code as a confidential client
(Oidc:ClientId + Oidc:ClientSecret), requiring a pre-provisioned
backing app with a secret. Drop client_secret from the exchange so it
uses PKCE only — the backing app is now a public client and no secret
touches the client-tenant register/connect flows.

The toolbox's own OIDC sign-in still uses Oidc:ClientSecret (unchanged).

Also enable user-secrets (UserSecretsId) so Oidc config stays out of
the committed appsettings.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:29:19 +02:00
kawa d19092c84e Initial commit 2026-06-02 10:56:03 +02:00