Skip to content

feat: implement endpoint to ensure user is initialized in Cosmo#2497

Merged
wilsonrivera merged 6 commits intomainfrom
wilson/eng-8900-sso-expose-method-to-initialize-user-in-cosmo-using-a-bearer
Feb 12, 2026
Merged

feat: implement endpoint to ensure user is initialized in Cosmo#2497
wilsonrivera merged 6 commits intomainfrom
wilson/eng-8900-sso-expose-method-to-initialize-user-in-cosmo-using-a-bearer

Conversation

@wilsonrivera
Copy link
Copy Markdown
Contributor

@wilsonrivera wilsonrivera commented Feb 10, 2026

Summary by CodeRabbit

  • New Features

    • Added a new user initialization endpoint to streamline Cosmo account creation and onboarding.
    • Enhanced authentication flow to automatically handle user registration, organization membership, and Keycloak group synchronization; token-based initialization is idempotent.
  • Tests

    • Added integration tests validating user initialization across token scenarios and idempotent behavior.

Checklist

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 10, 2026

Walkthrough

Adds a new PlatformService RPC initializeCosmoUser and message types. Centralizes auth callback handling into AuthUtils.handleAuthCallback. Introduces getUserInfo auth helpers and makes platform webhooks async (returning Promise<void>). Adds server handler and integration tests for the new flow.

Changes

Cohort / File(s) Summary
Proto & Generated RPC
proto/wg/cosmo/platform/v1/platform.proto, connect/src/wg/cosmo/platform/v1/platform_connect.ts, connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts, connect/src/wg/cosmo/platform/v1/platform_pb.ts
Adds InitializeCosmoUserRequest/InitializeCosmoUserResponse messages and InitializeCosmoUser RPC; regenerates connect bindings and message classes for unary RPC and serialization.
Server RPC Handler & Service Surface
controlplane/src/core/bufservices/user/initializeCosmoUser.ts, controlplane/src/core/bufservices/PlatformService.ts
Adds server handler initializeCosmoUser and exposes it on PlatformService (queries/mutations), delegating to the new handler.
Auth Utilities & Controller Refactor
controlplane/src/core/auth-utils.ts, controlplane/src/core/controllers/auth.ts
Introduces AuthUtils.handleAuthCallback and helper methods; refactors /callback to delegate to AuthUtils, moving session/user/org orchestration into centralized helper.
Authentication Interfaces & Impl
controlplane/src/core/services/Authentication.ts, controlplane/src/core/services/AccessTokenAuthenticator.ts
Adds getUserInfo to Authenticator interface and implementations; AccessTokenAuthenticator exposes getUserInfo; API-key prefix check made case-insensitive.
Platform Webhooks
controlplane/src/core/webhooks/PlatformWebhookService.ts, controlplane/src/core/webhooks/OrganizationWebhookService.ts
Changes IPlatformWebhookService.send to return Promise<void> and updates implementations/mocks; minor comment text edit in OrganizationWebhookService.
Tests & Test Utilities
controlplane/test/initialize-cosmo-user.test.ts, controlplane/test/test-util.ts, controlplane/src/core/test-util.ts
Adds integration tests for initializeCosmoUser; updates test setup/authenticator to accept Keycloak URL/realm, adds getUserInfo helper, and includes first/last name in Keycloak user creation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: implement endpoint to ensure user is initialized in Cosmo' accurately and specifically describes the main change: adding a new endpoint (initializeCosmoUser) to initialize users in the Cosmo platform.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 10, 2026

Codecov Report

❌ Patch coverage is 64.28571% with 115 lines in your changes missing coverage. Please review.
✅ Project coverage is 36.68%. Comparing base (2fa2dfd) to head (88d6a0c).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
controlplane/src/core/auth-utils.ts 63.36% 85 Missing ⚠️
controlplane/src/core/controllers/auth.ts 11.11% 16 Missing ⚠️
controlplane/src/core/services/Authentication.ts 9.09% 10 Missing ⚠️
...lane/src/core/services/AccessTokenAuthenticator.ts 33.33% 2 Missing ⚠️
controlplane/src/core/test-util.ts 90.47% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2497      +/-   ##
==========================================
- Coverage   41.99%   36.68%   -5.31%     
==========================================
  Files        1000      947      -53     
  Lines      138762   124031   -14731     
  Branches     8008     5102    -2906     
==========================================
- Hits        58270    45506   -12764     
+ Misses      78867    76956    -1911     
+ Partials     1625     1569      -56     
Files with missing lines Coverage Δ
...ntrolplane/src/core/bufservices/PlatformService.ts 81.74% <100.00%> (+0.10%) ⬆️
...e/src/core/bufservices/user/initializeCosmoUser.ts 100.00% <100.00%> (ø)
...ne/src/core/webhooks/OrganizationWebhookService.ts 41.47% <ø> (ø)
...lplane/src/core/webhooks/PlatformWebhookService.ts 54.71% <100.00%> (+0.87%) ⬆️
...lane/src/core/services/AccessTokenAuthenticator.ts 21.42% <33.33%> (+0.91%) ⬆️
controlplane/src/core/test-util.ts 88.20% <90.47%> (+0.17%) ⬆️
controlplane/src/core/services/Authentication.ts 27.05% <9.09%> (-2.28%) ⬇️
controlplane/src/core/controllers/auth.ts 16.56% <11.11%> (+6.99%) ⬆️
controlplane/src/core/auth-utils.ts 35.90% <63.36%> (+24.07%) ⬆️

... and 78 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 10, 2026

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-2052a89048afaf50b3da884f6a64ac835fb630be

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@controlplane/src/core/auth-utils.ts`:
- Around line 431-436: The advisory lock is acquired using db.execute which can
use a different connection and won't scope the lock to the transaction; inside
the db.transaction callback replace the db.execute call with the transaction
handle (tx.execute) so the pg_try_advisory_xact_lock(hashtext(${userId})) runs
on the same connection as the rest of the transaction (the code around
db.transaction, advisoryLockRows, userId and the returned [undefined, -1] should
remain unchanged aside from swapping db.execute → tx.execute).
- Around line 459-474: The webhook call platformWebhooks.send is async but not
awaited, so rejections escape the surrounding try/catch; update the call in the
block that checks numberOfOrganizations === 0 to await
platformWebhooks.send(...) (i.e., add the await keyword before
platformWebhooks.send and keep it inside the existing try/catch), and confirm
the enclosing function (in auth-utils.ts) is async so the await is valid—if the
enclosing function cannot be made async, instead return or properly chain the
Promise so failures are still caught.

In `@controlplane/src/core/bufservices/user/initializeCosmoUser.ts`:
- Around line 52-67: AuthUtils.handleAuthCallback is currently awaited but its
return value (which can be [undefined, -1] on advisory lock acquisition failure)
is ignored; update initializeCosmoUser to capture the result of
AuthUtils.handleAuthCallback, inspect the returned numberOfOrganizations (or
second tuple element) for -1, and if -1 return an error response instead of
EnumStatusCode.OK (or propagate/throw an error) so concurrent lock failures are
surfaced; reference the call site AuthUtils.handleAuthCallback and the
EnumStatusCode.OK response to implement the conditional handling.

In `@controlplane/src/core/services/Keycloak.ts`:
- Around line 108-123: The catch block inside the token verification loop in
Keycloak.ts never records errors, so AggregateError is never reached; update the
catch to capture the thrown value (e) and push a normalized Error into the
errors array (e.g., if e is already an Error push it, otherwise create new
Error(String(e))) while continuing the loop over signingKeys; keep the existing
post-loop logic that throws 'None of the signing keys...' when errors is empty
and otherwise throws new AggregateError(errors, 'Token verification failed') so
diagnostic info is preserved for jwtVerify and signingKeys failures.
🧹 Nitpick comments (3)
controlplane/src/core/services/Keycloak.ts (1)

125-149: Add a timeout to JWKS fetch.
Axios defaults to no timeout, so token verification can hang on network stalls.

⏱️ Suggested tweak
-    const response = await axios.get<JSONWebKeySet>(jwkEndpoint);
+    const response = await axios.get<JSONWebKeySet>(jwkEndpoint, { timeout: 5_000 });
controlplane/src/core/bufservices/PlatformService.ts (1)

177-177: Consider grouping initializeCosmoUser with mutations for readability.

This RPC initializes/creates users (side effects), so placing it under the Mutations section would keep the file’s organization consistent.

Also applies to: 480-482

controlplane/test/initialize-cosmo-user.test.ts (1)

24-128: Ensure server cleanup runs even on assertion failures.

server.close() only runs on the happy path; a thrown assertion can leak handles and hang the suite. Wrap each test in a try/finally (or use a shared helper).

♻️ Suggested pattern (apply to each test)
-  test.each(['', '     '])('should return `Bad Request` when token is empty or whitespace', async (token: string) => {
-    const { client, server } = await SetupTest({ dbname });
-
-    const response = await client.initializeCosmoUser({ token });
-    expect(response.response?.code).toBe(EnumStatusCode.ERR_BAD_REQUEST);
-
-    await server.close();
-  });
+  test.each(['', '     '])('should return `Bad Request` when token is empty or whitespace', async (token: string) => {
+    const { client, server } = await SetupTest({ dbname });
+    try {
+      const response = await client.initializeCosmoUser({ token });
+      expect(response.response?.code).toBe(EnumStatusCode.ERR_BAD_REQUEST);
+    } finally {
+      await server.close();
+    }
+  });

Comment thread controlplane/src/core/auth-utils.ts
Comment thread controlplane/src/core/auth-utils.ts
Comment thread controlplane/src/core/bufservices/user/initializeCosmoUser.ts
Comment thread controlplane/src/core/services/Keycloak.ts Outdated
Comment thread controlplane/src/core/services/Keycloak.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@controlplane/test/initialize-cosmo-user.test.ts`:
- Around line 24-65: Each test should guarantee server shutdown even on
assertion failures: for every test that calls SetupTest(...) and obtains {
client, server, ... }, wrap the test body in try/finally and move await
server.close() into the finally block so server.close() always runs; do this for
all tests that call SetupTest and use client.initializeCosmoUser, including the
parameterized test, the invalid token test, and the existing-user test that uses
createAuthenticator, UserRepository and OrganizationRepository.

Comment thread controlplane/test/initialize-cosmo-user.test.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@controlplane/test/initialize-cosmo-user.test.ts`:
- Around line 87-88: Update the mistaken comment text "that the was just
created" to "that the user was just created" in the test comment that begins "//
To simulate that the was just created in Keycloak..." to fix the typo and
improve clarity.
🧹 Nitpick comments (1)
controlplane/test/initialize-cosmo-user.test.ts (1)

69-70: Terminology mismatch: comments say "ID token" but code uses access_token.

The comments on lines 69 and 107 refer to "ID token," but the code extracts and uses access_token. Similarly, the error message on line 157 says "No ID token returned" while checking for access_token. Consider aligning the terminology for clarity.

✏️ Suggested terminology fixes
-    // Authenticate the user to get an ID token
+    // Authenticate the user to get an access token
     const { access_token: token } = await signIn(users.adminAliceCompanyA.email, 'wunder@123');
-    // Authenticate the new user to get an ID token
+    // Authenticate the new user to get an access token
     const { access_token: token } = await signIn(userEmail, 'wunder@123');
     if (!respObj.access_token) {
-      throw new Error('No ID token returned from Keycloak');
+      throw new Error('No access token returned from Keycloak');
     }

Also applies to: 107-108, 155-158

Comment thread controlplane/test/initialize-cosmo-user.test.ts
Copy link
Copy Markdown
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

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

LGTM

@wilsonrivera wilsonrivera merged commit 31b005f into main Feb 12, 2026
45 checks passed
@wilsonrivera wilsonrivera deleted the wilson/eng-8900-sso-expose-method-to-initialize-user-in-cosmo-using-a-bearer branch February 12, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants