feat: local accounts password auth (argon2 + lockout + reset)

Scope

Implement real local-account authentication: argon2id password storage, lockout after N failed attempts, password-reset flow, session creation parity with OIDC sign-in.

Background

Stage 4 (#494 (closed)) ships a /auth/local-login GET stub that returns 501 when [local_accounts].enabled = true in idp.toml. This issue replaces the stub with a real implementation.

Acceptance

  • local_accounts table migration (forward-only per ADR-016): email, argon2id hash, salt, failed_count, locked_until, created_at
  • GET /auth/local-login renders password form (Stage 4 stub becomes a real handler)
  • POST /auth/local-login accepts credentials, argon2id verify, session creation parity with OIDC path
  • Lockout policy (N failures → locked for T minutes) — values configurable
  • Password-reset flow (email token → reset form)
  • ATO/IRS-1075 review for canopy-internal credential storage
  • CHANGELOG entry

References

  • Tracking: epic &51 #460
  • Stage 4 plan: docs/modules/ROOT/pages/plans/worker-portal-redesign-stage4-idp-loader-and-sign-in.adoc