Skip to main content
← Back to list
01Issue
BugOpenSwamp ClubPublic
AssigneesNone

Relationships

#1819 IdP group memberships go stale indefinitely — user.idpGroups is only written by the SSO login hook

Opened by keeb · 8/25/2026

Problem

user.idpGroups is the source of truth for idp-group: grant subjects in swamp serve, but it is written in exactly one place — the swamp-club SSO provisioning hook (lib/auth.ts:1038-1041):

await db.collection("user").updateOne(
  authIdFilter(user.id),
  { $set: { idpGroups, emailVerified: true } },
);

provisionUserOnEveryLogin: true (lib/auth.ts:1020) means this fires on every SSO login — and only on login. There is no other writer of that field.

Impact

The refresh path documented in #1171 does not actually refresh group membership:

  1. A group changes in the IdP.
  2. user.idpGroups in Mongo stays stale until that user next completes an SSO login. This is unbounded — days or weeks for a long-lived session.
  3. swamp serve CollectiveRefreshService ticks every --group-refresh-interval (default 4h), calls userinfo, which reaches getAdditionalUserInfoClaim (lib/auth.ts:980-990), which reads that same stale user.idpGroups.

The serve refresh loop is faithfully re-fetching a value that only changes at login. It cannot fix staleness it inherits. The real bound on an IdP group change taking effect is "until the user next authenticates via SSO", not the 4h interval the serve manual describes.

This is security-relevant because idp-group: subjects gate grant enforcement (#1154). A user removed from an IdP group keeps the corresponding swamp permissions for as long as their session survives.

Inside getAdditionalUserInfoClaim, collectives are resolved live through _collectiveLookup, while groups are read from the frozen user document field. Two claims in the same userinfo response with materially different freshness guarantees, which is easy to misread when reasoning about grant behavior.

Suggested direction

Options, roughly in increasing order of effort:

  1. Re-resolve groups at userinfo time rather than reading the stored field, so the existing 4h serve refresh becomes meaningful.
  2. Push-invalidate on IdP group change (requires SCIM or equivalent inbound provisioning — filed separately).
  3. At minimum, correct the serve manual so the documented deprovisioning window matches actual behavior.

Option 1 alone would bring the effective bound from unbounded down to the documented 4h.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

8/25/2026, 2:33:47 PM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

keeb commented 8/25/2026, 2:34:12 PM

Siblings from the same investigation: #1820 (SSO profile fields frozen at first login) and #1821 (SCIM 2.0 provisioning). Option 2 in the suggested direction above depends on #1821 landing.

Sign in to post a ripple.