@@ -6,53 +6,33 @@ pure chore/docs commits). Direct pushes to main must also be logged here.
66
77---
88
9- ## 2026-08-27
10-
11- - Closed the audit gaps on the #118 PR (#124 ): the legacy raw-nonce migration
12- window is now enforced at runtime, not just documented.
13- - Added ` AUTH_LEGACY_SIGNATURES_SUNSET ` (default ` 2026-10-31 ` ): after the
14- cutoff, ` verifyLegacyRawSignature() ` rejects legacy raw-nonce signatures
15- with ` AUTH_LEGACY_SIGNATURE_DISABLED ` even while
16- ` AUTH_ALLOW_LEGACY_RAW_SIGNATURES ` is still true, so the replayable
17- scheme closes automatically on the sunset date — no manual ops action
18- required. Malformed sunset values fall back to the default rather than
19- silently disabling the cutoff.
20- - Strengthened the unit suite: the legacy-disabled regression test now
21- proves rejection happens before any signature verification (mock verify
22- returns true, assert it is never called), and new tests cover the
23- sunset cutoff (past sunset + flag true → rejected; future sunset +
24- flag true → accepted). Expired-envelope rejection unit test retained.
25- - Resolved the unresolved conflict markers (leftover wrong-repo
26- StepFi-Contracts content) that had been committed into
27- ` context/progress-tracker.md ` , which the PR audit flagged as merge
28- conflicts with the base branch.
29-
30- ## 2026-08-25
31-
32- - Fixed cross-service signature replay (#118 ): ` verifySignature() ` now accepts
33- exactly one scheme per request and every accepted signature provably signs a
34- StepFi-bound challenge.
35- - ` generateNonce() ` issues a canonical challenge envelope (domain, address,
36- statement, uri, version, nonce, issuedAt, expirationTime,
37- networkPassphrase) and stores a SHA-256 digest of the exact message on the
38- nonce row (` issued_at ` , ` message_hash ` columns via migration
39- ` 20260825000000_add_nonce_message_binding.sql ` ).
40- - Verification runs only against a message whose digest matches the stored
41- challenge hash (` AUTH_CHALLENGE_MISMATCH ` otherwise), with strict
42- domain/URI/network/expiry checks (` AUTH_CHALLENGE_DOMAIN_MISMATCH ` ,
43- ` AUTH_CHALLENGE_URI_MISMATCH ` , ` AUTH_CHALLENGE_NETWORK_MISMATCH ` ,
44- ` AUTH_NONCE_EXPIRED ` ). The old "try raw, then 'Stellar Signing Key: '"
45- fallback is gone — the weakest format no longer defines the security floor.
46- - Browser wallets verify per SEP-53 (SHA-256 of
47- "Stellar Signed Message:\n" + envelope, ` signatureType: 'sep0043' ` );
48- native clients sign the envelope with raw Ed25519
49- (` signatureType: 'envelope' ` ).
50- - The legacy raw-nonce scheme is deprecated behind
51- ` AUTH_ALLOW_LEGACY_RAW_SIGNATURES ` (default true for mobile-client
52- compatibility) with a documented sunset date of ** 2026-10-31** ; when
53- disabled, legacy requests fail with ` AUTH_LEGACY_SIGNATURE_DISABLED ` .
54- - Added ` AUTH_CHALLENGE_DOMAIN ` env (defaults to ` API_URL ` host); envelope
55- ` uri ` is derived from ` API_URL ` + ` API_PREFIX ` .
9+ ## 2026-08-24
10+
11+ - ** Session families + refresh-token replay detection** (` sessions.family_id `
12+ migration, ` fam ` claim in refresh JWTs). Replaying an already-rotated
13+ refresh token now revokes every session in the family and writes a
14+ ` auth.refresh_token_reuse ` audit log entry — previously the first
15+ presenter of a stolen token won silently. Legacy tokens without a ` fam `
16+ claim keep the old ` AUTH_SESSION_NOT_FOUND ` response.
17+ - ** Blocked-user enforcement on every request** : new
18+ ` UserStatusService ` (in-memory TTL cache) consulted by ` JwtStrategy ` .
19+ Documented staleness bound: ** 30 seconds** — a blocked wallet loses API
20+ access within ~ 30s of being blocked instead of retaining access until its
21+ access token expires (up to 15 minutes). Cache is per-instance and fails
22+ open on DB errors to avoid locking out all users during a DB blip.
23+ - ** Session cleanup cron** (` src/jobs/session-cleanup/ ` , hourly,
24+ mirrors nonce-cleanup): deletes only rows with ` expires_at ` older than
25+ 1 hour; sessions no longer accumulate forever.
26+ - Tests: refresh-family rotation, replay → family-wide revocation + audit
27+ event, blocked-user denial within TTL bound, cache expiry re-query,
28+ cleanup job deletes-only-expired.
29+
30+ ## 2026-08-26
31+
32+ - Fixed registration race conditions in ` AuthService.register() ` by eliminating application-side pre-checks (` findByWallet ` , ` checkUsernameExists ` ) and relying directly on DB-level UNIQUE constraints (` users.wallet_address ` , ` users.username ` ).
33+ - Added idempotent migration ` 20260826130000_ensure_users_unique_constraints.sql ` to ensure unique indexes exist on ` users.wallet_address ` and ` users.username ` .
34+ - Updated ` UsersRepository.createProfile() ` to catch PostgreSQL unique constraint violation error ` 23505 ` and map to structured 409 ` ConflictException ` (` AUTH_WALLET_EXISTS ` , ` AUTH_USERNAME_TAKEN ` ).
35+ - Added cleanup handlers (` deleteAvatar ` , ` deleteUserById ` ) in ` AuthService.register() ` and ` UsersRepository ` to ensure failed registrations do not leave orphaned avatar files or partial user records.
5636
5737## 2026-07-23
5838
0 commit comments