Release v0.2.0
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
expclaim 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'sexpclaim, 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_typeclaims path the authentication proxy forwards per request (the primary identity-resolution path, ahead of theAcceptedInvitationsession 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