KamoCRM

An OAuth state token is a server-side record, not a blob the browser hands back

FixEmailService
Shipped
September 23, 2026 at 2:58 AM UTC
Author
Kamo
Commit
78c4a87

/api/email/oauth/callback and **************** are public and sessionless (the provider redirects the browser here with no Kamo session), so `state` was the only place the organization could come from — and it was self-describing base64 JSON the CALLER built: {"orgId": 1, "nonce": "...", "provider": "GOOGLE_WORKSPACE"}. Anyone could hand-craft one naming a different org and replay it against an authorization code they obtained for their own; the "nonce" inside it was generated but never stored or checked against anything, so it stopped nothing. The callback trusted orgId/provider/memberId straight out of that decode and activated the connected provider for whichever org the state named. OAuthStateStore now mints an opaque, unguessable token on `issue`, keeping the org/member/provider/ flow it resolves to only in a Redis record with a 10-minute TTL — never in the token itself. `peek` reads that record without spending it (used to pick the redirect URI and the flow before a callback commits to a handler); `consume` reads and permanently deletes it, so a callback is single-use: a replayed or reused token resolves to nothing, indistinguishable from one that never existed. OAuthFlowService and GroupwareOAuthService — which used to build and decode that blob independently, each with its own copy of the same mechanism — now both mint and redeem tokens through this one store, so there is exactly one place that decides what a state token means. OAuthStateStoreTest fails without the fix (a second consume of the same token still returns the record instead of null); OAuthFlowServiceTest, OAuthFlowKindStateTest and GroupwareOAuthServiceTest are updated for the new mechanism — they no longer build or decode a state blob directly, since there is nothing left to decode.

All changes

Like what you see shipping?

All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.

Start Free ForeverView Pricing