Skip to content

fix(auth): dynamically sync WJ server clock offset for submission window - #69

Closed
JackyTJie wants to merge 1 commit into
mainfrom
fix/wj-clock-sync
Closed

fix(auth): dynamically sync WJ server clock offset for submission window#69
JackyTJie wants to merge 1 commit into
mainfrom
fix/wj-clock-sync

Conversation

@JackyTJie

Copy link
Copy Markdown
Contributor

Problem

The WJ questionnaire platform's server clock runs progressively slow (~7s/day; measured ~230s slow on 2026-09-08). The OTP verification compares initiated_at (our clock) against submitted_at (WJ clock), so the growing skew breaks all signup/login/password-reset flows with Submission timestamp outside validity window.

Fix (port of 67c6d3c, already live on fix/production-deploy)

NTP-style dynamic calibration instead of a hard-coded tolerance:

  • get_latest_answer samples the WJ clock offset from each API response's Date header (RTT midpoint corrected; same clock domain as submitted_at — verified against the measured drift history)
  • submitted_at is corrected by the offset before the window check; residual jitter tolerance 60s (AUTH.TIMESTAMP_JITTER_TOLERANCE)
  • Rolling EWMA estimate in Redis (wj:clock:offset, 48h TTL) as fallback; no sample at all → fixed 220s legacy window
  • Zero extra network requests; sampler failure degrades gracefully

Porting compatibility fixes

Required for the touched modules to import/run on this base (same as production branch):

  • parenthesize multi-exception except clauses in apps/auth (invalid Python 3 syntax on main — utils.py/views.py currently cannot import)
  • int()-cast OTP_TIMEOUT (env override is a string; arithmetic would raise)

Tests

apps/auth/tests/test_clock_offset.py — 13 tests: sampler accuracy, EWMA blending, corrected acceptance at -300s drift, replay rejection, legacy fallback. All pass. Verified live against wj.sjtu.edu.cn: sampler reports -230.4s, matching independent curl measurement.

⚠️ main has 38 commits of other work accumulated on fix/production-deploy (Turnstile retry refactor, syllabus fixes, etc.) — this PR is only the clock fix; the rest still needs a separate merge-back decision.

Port of 67c6d3c (deployed on fix/production-deploy) to main.

The WJ questionnaire platform's server clock runs progressively slow
(~7s/day; measured ~-230s on 2026-09-08), causing "Submission timestamp
outside validity window" failures for all OTP verifications.

Sample the offset from the WJ API response's Date header on every
get_latest_answer call (NTP-style; same clock domain as submitted_at)
and correct submitted_at before the validity-window check, with a 60s
residual jitter tolerance. A rolling EWMA estimate is kept in Redis
(wj:clock:offset, 48h TTL) as fallback; when no sample is available the
check degrades to a fixed 220s tolerance. Adds
AUTH.TIMESTAMP_JITTER_TOLERANCE (default 60) plus tests covering
sampling, EWMA blending, correction acceptance, replay rejection, and
fallback behavior.

Porting compatibility fixes (required for the touched modules to import
and run on this base, matching the production branch):
- apps/auth: parenthesize multi-exception except clauses (invalid Python 3 syntax)
- apps/auth: int()-cast OTP_TIMEOUT read from settings
@JackyTJie JackyTJie closed this Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant