DEV Community

Daniel Ioni
Daniel Ioni

Posted on

Building Verifiable Bounty Payments and Revocable Account Sessions at MyZubster

Building Verifiable Bounty Payments and Revocable Account Sessions at MyZubster
Over the latest MyZubster development cycle, we completed two important pieces of infrastructure: a verifiable cryptocurrency bounty settlement flow and a substantially hardened authentication/session architecture.
The objective was not simply to add more endpoints. We wanted financial operations to leave an auditable record and authenticated sessions to become controllable server-side rather than depending exclusively on long-lived client credentials.
This article covers what is implemented, what we validated, and what works today.

  1. Cryptocurrency Bounty Settlement MyZubster has started using real cryptocurrency settlement for contributor bounties. During this development cycle, two bounty-related settlement paths were exercised using BTC and ETH. One of the treasury operations involved a BTC-to-ETH conversion through SimpleSwap: Purpose: BOUNTY_SETTLEMENT Provider: SimpleSwap

Source:
BTC: 0.000464
Transaction:
2ea0bc209a2d5bcfbf520f830f1a39dee0691e47c461afc316e5aeb2e83cdd2d

Target:
ETH: 0.014355678390291923
Network: ethereum-mainnet
Transaction:
0x62b58e69931ef6046d8f68df39c118fe0b1fc7234b88c780d26a8b53fdcddcc6
The important engineering work was not the exchange itself. It was turning an external blockchain operation into a structured internal settlement record.
The resulting treasury conversion was recorded as:
state: CONVERSION_COMPLETED
verification.status: VERIFIED
purpose: BOUNTY_SETTLEMENT
The Ethereum transaction became the verification reference for the conversion.

  1. Idempotent Treasury Recording Financial operations should not create duplicate accounting records because an operator accidentally executes the same command twice. The treasury recording flow therefore supports idempotent replay. The first committed execution created the conversion record: RECORDED id: 6ab96bcb4c201256912e837a Running the same operation again produced: IDEMPOTENT REPLAY — conversion already recorded instead of inserting another settlement. This gives the treasury workflow an important property: external transaction ↓ verification ↓ normalized conversion ↓ idempotency check ↓ persistent treasury record The conversion service also exposes a provider boundary to the settlement dashboard. If the conversion repository becomes unavailable, the dashboard can report the conversion layer as unavailable instead of crashing the complete settlement view. That separation is important because financial observability should degrade explicitly rather than silently corrupting or inventing state.
  2. Dry-Run Before Commit The treasury recorder supports a dry-run mode. Before a database mutation is made, the operator can inspect the normalized settlement object and blockchain verification result. A successful dry run terminates with: DRY-RUN OK — no database write performed Only an explicit commit writes the conversion. That gives us a safer operational sequence: verify → inspect → commit → replay-safe persistence instead of coupling verification and mutation into one irreversible action.
  3. Server-Side Revocable Sessions The second major area of work was MYZ-71: authentication, sessions, and account security. Previously, a valid JWT could largely represent authentication state on its own. The new architecture introduces persistent server-side sessions. Access tokens are now associated with a session identifier, while the server maintains the authoritative session state. Conceptually: User │ ├── Session A — current browser ├── Session B — mobile device └── Session C — previous device A cryptographically valid token is therefore no longer necessarily sufficient. The associated session can be expired, missing, or revoked server-side. This makes remote revocation possible.
  4. Rotating Refresh Tokens We also implemented persistent refresh-token rotation. Refresh tokens are opaque credentials and their raw values are not stored in the database. Instead, the server stores an HMAC-SHA256 representation. When a refresh succeeds: refresh token N ↓ validate ↓ consume ↓ issue refresh token N+1 The previous token becomes unusable. Rotation uses an atomic compare-and-swap operation so concurrent requests cannot legitimately consume the same refresh token multiple times. Consumed-token history is bounded to prevent session documents from growing indefinitely.
  5. Refresh-Token Replay Detection Rotation also allows the server to detect reuse. If an already-consumed refresh token appears again, the system treats it as a replay and revokes the complete session. The tested behavior is: AUTH_REFRESH_REPLAY ↓ session revoked ↓ revokedReason: refresh-token-replay This was explicitly covered by the backend integration tests.
  6. Concurrent Browser Refresh Protection Single-use refresh tokens introduce another problem: several frontend requests may receive 401 responses simultaneously. Without coordination, every request could attempt to rotate the same token. The frontend authentication client therefore deduplicates concurrent refresh attempts. Multiple failed requests share one refresh operation: Request A ─┐ Request B ─┼──→ one refresh operation Request C ─┘ ↓ rotated credentials ↓ requests retry The original authenticated request is retried at most once. This behavior is now integrated into the Metaverse API client.
  7. Device and Session Management A new account-security surface exposes active sessions to the authenticated user. The API now includes operations for: POST /api/auth/refresh POST /api/auth/logout GET /api/auth/me GET /api/auth/me/sessions DELETE /api/auth/me/sessions/:sessionId The account-security UI can display active devices/sessions and their relevant lifecycle information, including current-session state, creation time, last activity, and expiration. Users can revoke another session remotely. The current session can also be terminated explicitly, after which browser authentication state is cleared and the user is returned through the login flow. Raw client IP addresses are not stored as plain values; the session design uses an HMAC-SHA256 digest instead.
  8. Metaverse Authentication Recovery Authentication recovery was also integrated into the MyZubster Metaverse surfaces. Neon Plaza, individual rooms, and room creation now react to terminal authentication expiry. When access can still be renewed, the authenticated client attempts refresh and retries the request. When authentication can no longer be recovered, local credentials are cleared and an authentication-expired event is emitted. Navigation to login is deduplicated so multiple failing requests do not trigger multiple competing redirects. The original local destination can also be preserved, including room paths and relevant local query/hash state, so authentication does not unnecessarily destroy the user's navigation context. Protocol-relative return targets are rejected.
  9. CSRF Hardening Moving authentication into cookies requires explicit CSRF protection. Unsafe cookie-authenticated requests now validate browser-origin information. The implementation distinguishes bearer authentication from ambient cookie authentication. For cookie-authenticated mutations, trusted Origin or Referer information is required where appropriate, and cross-site Fetch Metadata can be rejected before session lookup. Rejected requests return a structured error: AUTH_CSRF_REJECTED Production origins can be configured through trusted-origin configuration. Bearer clients remain supported during the migration because bearer credentials are not automatically attached by the browser in the same way cookies are.
  10. Structured Authentication Errors Authentication failures now expose structured application errors rather than leaking raw JWT verification details. Errors can carry stable codes and request IDs. That improves both frontend behavior and operational debugging: authentication failure ↓ structured error code ↓ request_id ↓ UI handling / server investigation This becomes particularly useful when distinguishing expiration, revocation, refresh replay, CSRF rejection, and terminal authentication failure.
  11. Fixing a Cross-Mongoose Runtime Boundary The final validation uncovered an architectural issue that ordinary unit testing could easily have missed. The root application uses Mongoose 7, while the nested Metaverse backend uses Mongoose 8. socialIdentityService was consuming a MetaverseCharacter model originating from the backend runtime while other models and the test database connection belonged to the root runtime. Initially this appeared as: Operation metaversecharacters.findOne() buffering timed out Connecting both Mongoose instances exposed the deeper incompatibility: BSONVersionError: Unsupported BSON version An ObjectId created by one BSON/Mongoose runtime was crossing into another. Rather than hiding the issue with larger Jest timeouts or --forceExit, we changed the model boundary. MetaverseCharacter now exposes a model factory, allowing root consumers to bind the schema to the root Mongoose instance. The result is a consistent model/connection boundary: Root application │ Mongoose 7 connection │ ├── User ├── AuthSession └── MetaverseCharacter The integration test uses the same strategy with MongoMemoryServer. After the repair, the previously hanging social-identity suite completed normally.
  12. Final Validation The final MYZ-71 repair was tested at both backend and frontend boundaries. Backend final gate: Test Suites: 3 passed, 3 total Tests: 18 passed, 18 total Frontend targeted gate: Test Suites: 4 passed, 4 total Tests: 13 passed, 13 total The optimized frontend production build also completed successfully. The final source check: git diff --check completed without errors. The production build still reports non-blocking ESLint warnings in existing frontend areas. They do not prevent compilation and were intentionally kept separate from this security repair.
  13. What Works Today At this stage, the completed system provides a concrete foundation across payments and identity:
  14. cryptocurrency bounty settlement can be represented as structured treasury data;
  15. the BTC → ETH treasury conversion was blockchain-referenced and recorded as verified;
  16. treasury recording supports dry-run execution and idempotent persistence;
  17. server-side authentication sessions can be revoked;
  18. refresh credentials rotate and replay can revoke a compromised session;
  19. concurrent frontend refresh attempts are deduplicated;
  20. users have account/device session controls;
  21. Metaverse requests can automatically recover from renewable authentication expiry;
  22. terminal expiry returns users through authentication without losing the intended local destination;
  23. cookie-authenticated mutations have origin-based CSRF defenses;
  24. authentication failures expose structured codes and request IDs;
  25. social identities and persistent Metaverse characters work across the repaired Mongoose model boundary;
  26. the final backend and frontend targeted test gates pass;
  27. the frontend production bundle builds successfully. The MYZ-71 implementation is now in GitHub as commit: 1878e02a fix: harden revocable account sessions The branch has been pushed and the corresponding pull request is ready for review. What Comes Next This is not the end of MYZ-71. The remaining roadmap includes passkeys/magic-link authentication, step-up authentication for sensitive operations, extending automatic renewal beyond the current Metaverse client surface, completing the migration away from legacy localStorage/bearer credentials, and production verification of trusted origins, OAuth behavior, and database stability. For the treasury side, the next step is to continue moving from individually verified settlement records toward a complete contributor-facing settlement lifecycle where bounty state, payout transaction, conversion provenance, verification, and notification can be correlated without relying on manual reconstruction. The important milestone is that both systems now have stronger boundaries. A bounty payment can become a verifiable treasury event. An authenticated browser can become a revocable server-side session. Those are the primitives we need before building more automation on top of either system.

Top comments (0)