Skip to content

Release v0.2.0

Choose a tag to compare

@github-actions github-actions released this 06 Sep 20:15
78a217d

Summary

The /_invite/exchange endpoint (InviteExchangeProcessor) parsed an invite token's jti/invite_type claims but never checked exp, and a retried exchange for the same login always blindly overwrote the recorded session. This closes both gaps for WP-01 of the onboarding-adoption epic (#10).

Added

  • The invitation exchange now rejects a token whose exp claim is missing or already in the past, instead of recording a session that would authorize forever.
  • The recorded session carries its own expiry (ExpiresAtUtc), copied verbatim from the token's exp claim, so retrying the exchange with the same token - at-least-once delivery from the authentication proxy, a user double-submitting, a lost response - converges on the same session instead of creating a duplicate or extending its lifetime.
  • InvitationIdentityProvider's session fallback now stops honoring a session the instant it expires, independent of whenever a storage cleanup sweep gets around to removing it.
  • A unique index backstops the single-session-per-login invariant, and a TTL index cleans up expired sessions in storage; both are installed at startup before the exchange endpoint is exposed to traffic.

Fixed

  • A repeated exchange for the same invitation no longer risks the earlier bug where each call was written independently, with no guard against extending the session's effective lifetime on retry.

Out of scope (tracked by later work packages)

  • Full signature verification of the invite token - this endpoint still only parses the token locally; the authentication proxy remains the actual trust boundary. Verifying that boundary depends on the proxy protocol still to be agreed in #11.
  • Expiry enforcement on the jti/invite_type claims path the authentication proxy forwards per request (the primary identity-resolution path, ahead of the AcceptedInvitation session fallback this PR hardens) - also depends on the not-yet-finalized proxy contract from #11.

(#12)

Test plan

  • dotnet build (Debug) - 0 warnings, 0 errors, proxies unaffected (no [Command]/[ReadModel] surface changed)
  • dotnet build (Release, -p:CratisProxiesOutputPath=) - 0 warnings, 0 errors
  • dotnet test - 65/65 passed, including three new specs: successful exchange (now asserting the recorded expiry), an expired token being rejected, and a retried exchange establishing the same session both times
  • No frontend/TypeScript files touched - frontend gates not applicable to this change

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_014jfWkrU6BPcC8T3gFNAc8z