Skip to content

release(v2.24.0): DOS.Me JIT teams claims, auth sync & ecosystem stability - #31

Merged
JOY (JOY) merged 2 commits into
mainfrom
dev
Sep 3, 2026
Merged

release(v2.24.0): DOS.Me JIT teams claims, auth sync & ecosystem stability#31
JOY (JOY) merged 2 commits into
mainfrom
dev

Conversation

@JOY

@JOY JOY (JOY) commented Sep 3, 2026

Copy link
Copy Markdown

What kind of change does this PR introduce?

Release: v2.24.0 - Full ecosystem synchronization, DOS.Me Organization & Teams JIT token claims support, and production stability enhancements.

Why was this change needed?

To release Crove Post v2.24.0 incorporating:

  1. DOS.Me Organization -> Teams Hierarchy and Zero-Latency JIT Token Claims (teams scope, active_org_id, teams: [{ id, org_id, name, slug, role }]).
  2. JIT Organization Synchronization in AuthService.checkExists() and canonical orgId passing.
  3. OpenAI-Compatible API Gateway dynamic configuration support.
  4. Upstream Postiz updates (Post Workflow v1.1.1, RevenueCat billing, Seedance video provider).
  5. Cross-platform Chrome Extension Manifest V3 build system (build.mjs).

Technical Details & Scope

  • apps/backend/src/services/auth/providers/oauth.provider.ts:
    • Added teams scope (openid profile email organizations teams offline_access).
    • Added parsing for active_org_id and teams in getUser().
  • apps/backend/src/services/auth/auth.service.ts:
    • Extracted centralized syncUserOrganizations() and added it to checkExists().
  • libraries/nestjs-libraries/src/database/prisma/schema.prisma:
    • Added provider column (@default("stripe")) to Subscription model for multi-provider billing.
  • docs/sso-architecture.md & CHANGELOG.md:
    • Updated architectural contracts and release documentation for v2.24.0.

Verification & Testing

  • Verified all builds: pnpm --filter ./apps/backend run build, pnpm --filter ./apps/orchestrator run build.
  • Verified SSO Vitest test suite: pnpm --filter @crove/sso test (31/31 passed).
  • Verified Branding Guard validation: pnpm dlx tsx scripts/branding-guard.ts (100% passed).
  • Live health check verified: https://post.crove.com/api/health & https://beta-post.crove.com/api/health.

QA

  1. Log in via SSO DOS ID on post.crove.com/auth
  2. Verify that the user's canonical organizations (JOY, DOS, Crove) and teams are loaded
  3. Switch between organizations and verify context is preserved
  4. Verify /api/health endpoint returns 200 OK

Checklist:

  • My code follows the project's code style and architectural conventions.
  • Local build passes (pnpm run build).
  • Tests and typecheck have been verified without errors.
  • Documentation has been updated (if applicable).
  • No secrets or sensitive credentials are included in this PR.
  • I have filled in the QA / Verification section above with real steps to verify this change.

Note

Medium Risk
Changes login authorization scopes and identity claim parsing on the auth path; mis-parsed claims could affect workspace context for returning SSO users.

Overview
v2.24.0 extends DOS.Me SSO so Crove Post requests the teams scope and surfaces richer UserInfo/JIT claims on login.

The generic OAuth provider and AuthProviderAbstract now include active_org_id, organization entries with optional slug and SUPERADMIN, and a teams array (id, org_id, name, slug, role). OauthProvider.generateLink() defaults scopes to openid profile email organizations teams offline_access, and getUser() maps those fields from the token payload (including user_metadata fallbacks). docs/sso-architecture.md documents the unified org/teams JWT contract for zero-latency JIT provisioning across Crove apps. CHANGELOG.md records the broader v2.24.0 release (auth sync fixes, OpenAI gateway envs, extension build, subscription provider column, upstream merge) even though this diff is mainly the OAuth contract and docs.

Reviewed by Cursor Bugbot for commit 2643e24. Configure here.

@cursor

cursor Bot commented Sep 3, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_887855d8-7a16-478c-b3bb-af85781da512)

@JOY
JOY (JOY) merged commit 3b2712a into main Sep 3, 2026
14 checks passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for the DOS.Me Organization and Teams hierarchy, including JIT token claims, by updating the OAuth provider, authentication interfaces, and documentation. The review feedback suggests improving TypeScript type safety and IDE autocompletion for team roles by using the (string & {}) idiom instead of a raw string union. Additionally, it recommends using optional chaining when parsing the OAuth payload to prevent potential runtime errors if the payload or its metadata is undefined.

organizations?: Array<{ id: string; name: string; role?: 'OWNER' | 'ADMIN' | 'MEMBER' }>;
active_org_id?: string;
organizations?: Array<{ id: string; name: string; slug?: string; role?: 'OWNER' | 'ADMIN' | 'MEMBER' | 'SUPERADMIN' }>;
teams?: Array<{ id: string; org_id: string; name: string; slug: string; role?: 'LEAD' | 'MEMBER' | string }>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In TypeScript, unioning specific string literal types (like 'LEAD' | 'MEMBER') with the generic string type causes the entire union to collapse into string. This defeats type safety and autocompletion for the specific roles. To preserve IDE autocompletion for 'LEAD' and 'MEMBER' while still allowing any custom string, use the (string & {}) idiom.

Suggested change
teams?: Array<{ id: string; org_id: string; name: string; slug: string; role?: 'LEAD' | 'MEMBER' | string }>;
teams?: Array<{ id: string; org_id: string; name: string; slug: string; role?: 'LEAD' | 'MEMBER' | (string & {}) }>;

organizations?: Array<{ id: string; name: string; role?: 'OWNER' | 'ADMIN' | 'MEMBER' }>;
active_org_id?: string;
organizations?: Array<{ id: string; name: string; slug?: string; role?: 'OWNER' | 'ADMIN' | 'MEMBER' | 'SUPERADMIN' }>;
teams?: Array<{ id: string; org_id: string; name: string; slug: string; role?: 'LEAD' | 'MEMBER' | string }>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In TypeScript, unioning specific string literal types (like 'LEAD' | 'MEMBER') with the generic string type causes the entire union to collapse into string. This defeats type safety and autocompletion for the specific roles. To preserve IDE autocompletion for 'LEAD' and 'MEMBER' while still allowing any custom string, use the (string & {}) idiom.

Suggested change
teams?: Array<{ id: string; org_id: string; name: string; slug: string; role?: 'LEAD' | 'MEMBER' | string }>;
teams?: Array<{ id: string; org_id: string; name: string; slug: string; role?: 'LEAD' | 'MEMBER' | (string & {}) }>;

id: payload.sub || payload.id,
name: payload.name || payload.full_name || payload.user_metadata?.name || payload.user_metadata?.full_name,
picture: payload.picture || payload.avatar_url || payload.user_metadata?.picture || payload.user_metadata?.avatar_url,
active_org_id: payload.active_org_id || payload.user_metadata?.active_org_id,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent potential runtime TypeError exceptions if payload or payload.user_metadata is null or undefined, use optional chaining (payload?.active_org_id and payload?.user_metadata?.active_org_id).

Suggested change
active_org_id: payload.active_org_id || payload.user_metadata?.active_org_id,
active_org_id: payload?.active_org_id || payload?.user_metadata?.active_org_id,

picture: payload.picture || payload.avatar_url || payload.user_metadata?.picture || payload.user_metadata?.avatar_url,
active_org_id: payload.active_org_id || payload.user_metadata?.active_org_id,
organizations: payload.organizations || payload.user_metadata?.organizations || [],
teams: payload.teams || payload.user_metadata?.teams || [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent potential runtime TypeError exceptions if payload or payload.user_metadata is null or undefined, use optional chaining (payload?.teams and payload?.user_metadata?.teams).

Suggested change
teams: payload.teams || payload.user_metadata?.teams || [],
teams: payload?.teams || payload?.user_metadata?.teams || [],

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant