◐ Shell
clean mode source ↗

feat(sso): SAML/OIDC single sign-on by 0ski · Pull Request #3911 · triggerdotdev/trigger.dev

@0ski marked this pull request as ready for review

June 11, 2026 17:24

devin-ai-integration[bot]

coderabbitai[bot]

coderabbitai[bot]

coderabbitai[bot]

devin-ai-integration[bot]

devin-ai-integration[bot]

coderabbitai[bot]

devin-ai-integration[bot]

devin-ai-integration[bot]

devin-ai-integration[bot]

devin-ai-integration[bot]

0ski added a commit that referenced this pull request

Jun 18, 2026
- vercel.onboarding: already-authenticated users whose domain requires SSO
  were redirected to /login/sso, which bounces authed users home and
  dropped the single-use Vercel code. Destroy the session first via
  authenticator.logout so /login/sso accepts them and the resume URL
  survives the round-trip (addresses PR #3911 review).
- auth.sso.callback: sanitize redirectTo at the exit point (IdP-initiated
  values come from relay-state and never passed the host sanitizer) and
  attribute the referral source, matching the other auth callbacks.
- ssoAuth: extract private early-return helpers (resolveSsoUserId,
  attachSsoIdentityBestEffort, provisionJitMembershipBestEffort,
  runPostAuthentication) and fail closed if the user can't be confirmed
  post-resolution instead of minting a session.

@0ski 0ski enabled auto-merge (squash)

June 18, 2026 15:25
Vendor-neutral plugin contract plus the host
wiring that consumes it. With no SSO plugin installed, everything degrades
to a no-op fallback, so OSS deployments are unaffected.

- Plugin contract (@trigger.dev/plugins) + lazy loader/fallback in
  internal-packages/sso: status, portal-link, enforce/JIT config,
  route-decision, begin/complete authorization, identity resolution, JIT
  evaluation, and periodic session validation. All methods return
  neverthrow Results; the fallback is fail-open.
- Login: 'Sign in with SSO' entry + dedicated /login/sso flow and
  /auth/sso(.callback) routes, plus auto-discovery from magic-link/OAuth.
- Org settings -> SSO page: plan-tier upsell, connection status,
  verified-domain list, enforcement + JIT provisioning + default-role
  configuration, and an admin-portal link dialog.
- AuthUser carries an optional signed 'sso' marker; SSO-established
  sessions are periodically re-validated against the identity provider on
  a single-flight, throttled, fail-open basis and logged out only on an
  explicit invalid result.
- SSO_ENABLED gate (default off) so the feature ships dark until its
  backing plugin is available; SSO_SESSION_REVALIDATION_INTERVAL_SECONDS
  controls the cadence.

@0ski 0ski enabled auto-merge (squash)

June 18, 2026 15:34

devin-ai-integration[bot]

ensureOrgMember's create + rbac.setUserRole + compensating delete are not
transactional (the RBAC plugin writes on its own connection). The common
single-failure case already recovers — a failed setUserRole deletes the row,
so the next login retries cleanly. But if the compensating delete ALSO fails,
the placeholder MEMBER row is orphaned and the findFirst no-op short-circuits
every future login, stranding the user on the wrong role.

When an existing membership is found and a JIT role is requested, complete the
assignment if (and only if) the RBAC layer shows no role assigned. Gated on
'no role assigned' so it can never demote a deliberately-set role; best-effort
so it never throws or rolls back a valid pre-existing membership.

Addresses PR #3911 review.

devin-ai-integration[bot]