Skip to content

[cli] auth client update#2624

Merged
stopachka merged 14 commits into
mainfrom
cli-codex-update
May 5, 2026
Merged

[cli] auth client update#2624
stopachka merged 14 commits into
mainfrom
cli-codex-update

Conversation

@stopachka
Copy link
Copy Markdown
Contributor

@stopachka stopachka commented Apr 30, 2026

This PR adds npx instant-cli auth client update, so users can change OAuth client credentials after a client has already been created.

The main use case is Google shared dev credentials: start fast with Instant dev credentials, then switch to your own Google OAuth credentials when you are ready. It also supports rotating credentials and updating provider-specific settings for the rest of our OAuth client types.

shared helpers

This also extracts shared OAuth client helpers that are now used by add, update, list, and delete:

  • Flag helpers for checking provided CLI args.
  • Shared provider prompts for client IDs, client secrets, redirect URIs, Apple fields, Clerk publishable keys, and Firebase project IDs.
  • Shared provider utilities for Apple private key files, Clerk discovery domains, Firebase discovery endpoints, OAuth client lookup by ID or name, and redirect setup success messages.
  • optOrPromptWhenNeeded, which lets update flows only prompt for the fields selected by flags or by the interactive action.

This keeps add and update using the same prompt copy and validation instead of duplicating provider-specific details across commands.

update

Select a client by name or ID

You can update a client by --name or --id:

npx instant-cli auth client update --name google-dev --client-id test-google-client-id --client-secret test-google-client-secret --yes

If you do not pass --name or --id in interactive mode, the CLI asks which client to update. Dev-credential clients are labeled in the picker.

Google dev credentials can move to custom credentials

A Google web client that was created with Instant dev credentials can be updated to use your own Google OAuth credentials:

npx instant-cli auth client update \
  --name google-dev \
  --client-id test-google-client-id \
  --client-secret test-google-client-secret \
  --yes

When the client moves to custom credentials, the CLI prints the Google Console redirect URI again:

┌─────────────────────────────────────────────────────┐
│ Google OAuth client updated: google-dev             │
│ Credentials: custom                                 │
│ ID: 33b53917-bc51-486f-af84-413f2b52e603            │
│                                                     │
│ This client no longer uses Instant dev credentials. │
│                                                     │
│ Add this redirect URI in Google Console:            │
│ https://api.instantdb.com/runtime/oauth/callback    │
└─────────────────────────────────────────────────────┘

Google custom credentials can move back to dev credentials

You can also switch a Google web client back to Instant dev credentials:

npx instant-cli auth client update --name google-custom --dev-credentials --yes

The update clears the redirect URI and marks the client as using shared credentials again:

┌─────────────────────────────────────────────────┐
│ Google OAuth client updated: google-dev         │
│ Credentials: Instant dev credentials            │
│ ID: 33b53917-bc51-486f-af84-413f2b52e603        │
│                                                 │
│ No Google Console setup required.               │
│ Works on localhost and Expo during development. │
└─────────────────────────────────────────────────┘

Custom credential providers can rotate values

GitHub, LinkedIn, and Google custom clients can rotate --client-id, --client-secret, and --custom-redirect-uri without recreating the client.

Apple can update --services-id, and the web redirect settings: --team-id, --key-id, --private-key-file, and --custom-redirect-uri.

Clerk and Firebase can update their source settings:

npx instant-cli auth client update \
  --name clerk \
  --publishable-key pk_test_Z3VpZGluZy1wZWdhc3VzLTkzLmNsZXJrLmFjY291bnRzLmRldiQ \
  --yes

npx instant-cli auth client update \
  --name firebase \
  --project-id my-app-123 \
  --yes

For Clerk and Firebase, the CLI also refreshes the stored discovery endpoint.

Interactive mode asks what you want to update

For custom credential clients, interactive mode asks whether to rotate credentials or update the redirect URI. For Google web clients, it can also switch back to Instant dev credentials. When a redirect URI changes, the success output mirrors the provider-specific setup guidance from auth client add.

add

The Google dev-credentials success box now points users at the new update command:

Google OAuth client created: google-dev
App type: web
Credentials: Instant dev credentials
ID: ae5ac75d-6840-42c9-a8d5-2881f630300e

No Google Console setup required.
Works on localhost and Expo during development.

Ready for production? Run:
  instant-cli auth client update --name google-dev \
    --client-id <id> \
    --client-secret <secret>

The rest of the add flow should behave the same as before, but now uses the shared helpers described above for provider prompts and validation.

Testing

Update flow:

# 0. Make sure to re-run `make dev`, so your backend is up to date.

# 1. Come on into cli-nodejs and build a throwaway app.

export INSTANT_CLI_DEV=true
cd client/sandbox/cli-nodejs
rm -f .env
pnpm exec instant-cli init --yes --title testing-app

# 2. Create a Google web client with Instant dev credentials.

pnpm exec instant-cli auth client add \
  --type google \
  --app-type web \
  --name google-dev \
  --dev-credentials

## Notice the success box says it is using Instant dev credentials and points at auth client update.

# 3. Update that Google client to custom credentials.

pnpm exec instant-cli auth client update \
  --name google-dev \
  --client-id test-google-client-id \
  --client-secret test-google-client-secret \
  --yes

## Notice the output says the client no longer uses Instant dev credentials and shows the Google Console redirect URI.

# 4. List clients and confirm google-dev now shows Credentials: custom.

pnpm exec instant-cli auth client list

# 5. Move the same Google client back to Instant dev credentials.

pnpm exec instant-cli auth client update \
  --name google-dev \
  --dev-credentials \
  --yes

## Notice the output says Credentials: Instant dev credentials.

# 6. List clients and confirm google-dev is back to dev credentials.

pnpm exec instant-cli auth client list

# 7. Create a custom Google web client and update only its redirect URI.

pnpm exec instant-cli auth client add \
  --type google \
  --app-type web \
  --name google-custom \
  --client-id old-google-client-id \
  --client-secret old-google-client-secret \
  --yes

pnpm exec instant-cli auth client update \
  --name google-custom \
  --custom-redirect-uri https://example.com/oauth/callback \
  --yes

## Notice this does not ask for a new client ID or client secret. It prints the Google Console redirect guidance and custom redirect test URL.

# 8. Test the interactive Google path.

pnpm exec instant-cli auth client update --name google-custom

## See the CLI ask what you want to update. Try both switching to dev credentials and updating the redirect URI.

# 9. Test validation: --dev-credentials cannot be combined with custom credential flags.

pnpm exec instant-cli auth client update \
  --name google-custom \
  --dev-credentials \
  --client-id test-google-client-id \
  --yes

# Notice this fails with a message saying --dev-credentials cannot be combined with --client-id.

# 10. Test validation: --dev-credentials is rejected for native Google clients.

pnpm exec instant-cli auth client add \
  --type google \
  --app-type ios \
  --name google-ios \
  --client-id test-ios-google-client-id \
  --yes

pnpm exec instant-cli auth client update \
  --name google-ios \
  --dev-credentials \
  --yes

# Notice this fails with a message saying --dev-credentials is only supported for Google web clients.

# 11. Create and update a GitHub client.

pnpm exec instant-cli auth client add \
  --type github \
  --name github-custom \
  --client-id old-github-client-id \
  --client-secret old-github-client-secret \
  --yes

pnpm exec instant-cli auth client update \
  --name github-custom \
  --client-secret new-github-client-secret \
  --yes

## Notice this updates without requiring a new client ID.

# 12. Test a GitHub interactive redirect-only update.

pnpm exec instant-cli auth client update --name github-custom

## Choose Update redirect URI and enter https://example.com/oauth/callback. Notice the output says to add this callback URL in your GitHub OAuth App settings.

# 13. Create and update a LinkedIn redirect URI.

pnpm exec instant-cli auth client add \
  --type linkedin \
  --name linkedin-custom \
  --client-id old-linkedin-client-id \
  --client-secret old-linkedin-client-secret \
  --yes

pnpm exec instant-cli auth client update \
  --name linkedin-custom \
  --custom-redirect-uri https://example.com/linkedin/callback \
  --yes

## Notice the output says to add this redirect URI in your LinkedIn app settings.

# 14. Create and update an Apple redirect URI.

pnpm exec instant-cli auth client add \
  --type apple \
  --name apple-custom \
  --services-id com.example.apple \
  --yes

pnpm exec instant-cli auth client update \
  --name apple-custom \
  --custom-redirect-uri https://example.com/apple/callback \
  --yes

## Notice the output says to add this return URL under your Apple Services ID.

# 15. Create and update Clerk.

pnpm exec instant-cli auth client add \
  --type clerk \
  --name clerk \
  --publishable-key pk_test_Z3VpZGluZy1wZWdhc3VzLTkzLmNsZXJrLmFjY291bnRzLmRldiQ \
  --yes

pnpm exec instant-cli auth client update \
  --name clerk \
  --publishable-key pk_test_Z3VpZGluZy1wZWdhc3VzLTkzLmNsZXJrLmFjY291bnRzLmRldiQ \
  --yes

## Notice the update refreshes the Clerk discovery endpoint.

# 16. Create and update Firebase.

pnpm exec instant-cli auth client add \
  --type firebase \
  --name firebase \
  --project-id old-app-123 \
  --yes

pnpm exec instant-cli auth client update \
  --name firebase \
  --project-id my-app-123 \
  --yes

## Notice the update refreshes the Firebase discovery endpoint.

# 17. Test --yes validation.

pnpm exec instant-cli auth client update --client-secret new-secret --yes

# Notice this fails because --yes requires --id or --name.

pnpm exec instant-cli auth client update --name github-custom --yes

# Notice this fails because --yes requires at least one update field.

Add and shared-helper regressions:

# 1. Reuse the same sandbox app from the first block.

export INSTANT_CLI_DEV=true
cd client/sandbox/cli-nodejs

# 2. Create a Google web client with explicit dev credentials.

pnpm exec instant-cli auth client add \
  --type google \
  --app-type web \
  --name google-add-dev \
  --dev-credentials

## Notice the success box says Credentials: Instant dev credentials and includes the auth client update command.

# 3. Create a Google web client with custom credentials.

pnpm exec instant-cli auth client add \
  --type google \
  --app-type web \
  --name google-add-custom \
  --client-id test-google-client-id \
  --client-secret test-google-client-secret \
  --yes

## Notice the success box still shows the Google Console redirect URI.

# 4. Run an interactive GitHub add to check the shared client ID, client secret, and redirect URI prompts.

pnpm exec instant-cli auth client add \
  --type github \
  --name github-add-interactive

## Enter a client ID, client secret, and optional redirect URI. Notice the prompt copy and output still match the previous add flow.

# 5. Exercise the shared Apple private key reader and Apple web fields.

printf '%s\n' \
  '-----BEGIN PRIVATE KEY-----' \
  'test-private-key' \
  '-----END PRIVATE KEY-----' \
  > /tmp/instant-apple-test-key.p8

pnpm exec instant-cli auth client add \
  --type apple \
  --name apple-add-web \
  --services-id com.example.apple.add \
  --team-id TEAM123 \
  --key-id KEY123 \
  --private-key-file /tmp/instant-apple-test-key.p8 \
  --yes

## Notice the Apple client is created and the .p8 file path is accepted.

# 6. Exercise the shared Firebase project ID validation.

pnpm exec instant-cli auth client add \
  --type firebase \
  --name firebase-invalid \
  --project-id BAD \
  --yes

# Notice this fails with the Firebase project ID validation message.

# 7. Create a valid Firebase client and list everything.

pnpm exec instant-cli auth client add \
  --type firebase \
  --name firebase-add-valid \
  --project-id valid-app-123 \
  --yes

pnpm exec instant-cli auth client list

## Confirm the Google dev client is labeled as Instant dev credentials, the Google custom client is custom, and the provider-specific clients are present.

@drew-harris @dwwoelfel @nezaj

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an interactive "auth client update" CLI command, extracts shared CLI prompt/validation helpers, refactors add/delete to use them, introduces Clerk publishable-key decoding, extends OAuth client schemas and update APIs to handle shared/dev credentials and discovery endpoints, and updates server routes/models and tests accordingly.

Changes

Auth client update + shared CLI helpers

Layer / File(s) Summary
Data Shape
client/packages/cli/src/lib/oauth.ts
Adds GoogleAppTypeSchema, structured OAuthClientMeta, nullable use_shared_credentials on OAuthClient; extends addOAuthClient and adds updateOAuthClient & findClientByIdOrName.
Shared Helpers
client/packages/cli/src/commands/auth/client/shared.ts
New flag/meta utilities, prompt factories (client id/secret, redirect, Apple, Clerk, Firebase), redirectSetupMessages, readPrivateKeyFile Effect, validateFirebaseProjectId, firebaseDiscoveryEndpoint, and domainFromClerkKey alias.
Core CLI Implementation
client/packages/cli/src/commands/auth/client/update.ts
New exported authClientUpdateCmd Effect implementing provider dispatch (google, github, linkedin, apple, clerk, firebase), interactive prompts/validation, Google credential-mode resolution (dev/custom/redirect), Apple web config, Clerk/Firebase discovery handling, and calls to updateOAuthClient.
Add Command Refactor
client/packages/cli/src/commands/auth/client/add.ts
Refactored to use shared prompt/helpers; updated Google credential-mode detection and output text (dev output now shows “Ready for production? Run:” with auth client update); replaced inline redirect instructions with redirectSetupMessages; reused Clerk/Firebase/Apple helpers.
Delete Command Simplification
client/packages/cli/src/commands/auth/client/delete.ts
Non-interactive delete now uses findClientByIdOrName({ id, name }) for lookup; removed previous separate --id/--name branching and explicit mutual-exclusion error.
CLI Wiring
client/packages/cli/src/index.ts
Adds authClientUpdateDef subcommand (auth client update) and wires authClientUpdateCmd to run with the app layer.
Client platform helper
client/packages/platform/src/clerk.ts, client/packages/platform/src/index.ts
Adds clerkDomainFromPublishableKey and re-exports it from platform index.
Client UI
client/www/components/dash/auth/Clerk.tsx
Replaces local Clerk key decoding with clerkDomainFromPublishableKey and updates UI flows to use it.
Server Endpoint & Model
server/src/instant/dash/routes.clj, server/src/instant/model/app_oauth_client.clj
update-oauth-client now authorizes via superadmin/ref-token helper, preserves presence vs omission of fields (supports :discovery_endpoint and :use_shared_credentials), and includes :redirect_to/:use_shared_credentials in responses; model adds validate-discovery-endpoint!, validates discovery endpoint on update, encrypts client-secret only when non-nil, and applies use-shared-credentials directly.
Tests
client/packages/cli/__tests__/authClientUpdate.test.ts, server/test/instant/dash/routes_test.clj
Adds comprehensive Vitest suite for authClientUpdateCmd (Google mode transitions, provider-specific updates, --yes validation) and a server test verifying clearing custom credentials when switching to shared.
Test Updates
client/packages/cli/__tests__/authClientAddGoogle.test.ts
Updates dev-credentials assertions to expect “Ready for production? Run:” with instant-cli auth client update --name google-web.

Sequence Diagram

sequenceDiagram
  actor User
  participant CLI as instant-cli (command)
  participant Shared as shared.ts (prompts & helpers)
  participant OAuthLib as CLI OAuth lib
  participant Server as Server (routes/model)
  participant DB as Database

  User->>CLI: run "auth client update" (flags / interactive)
  CLI->>OAuthLib: findClientByIdOrName(id/name) or getAppsAuth()
  OAuthLib-->>CLI: { auth, client }
  CLI->>Shared: request prompts / resolve mode
  Shared->>User: show prompts
  User-->>Shared: answers
  Shared-->>CLI: validated inputs
  CLI->>OAuthLib: updateOAuthClient({clientId, clientSecret, discoveryEndpoint, redirectTo, meta, useSharedCredentials})
  OAuthLib->>Server: POST /dash/apps/{app}/oauth_clients/{id}
  Server->>Server: validate-discovery-endpoint! (if present)
  Server->>DB: persist client changes
  DB-->>Server: saved row
  Server-->>OAuthLib: updated client payload
  OAuthLib-->>CLI: updated client
  CLI-->>User: confirmation + redirect setup messages
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • instantdb/instant#2553: Implements use_shared_credentials and shared dev-credentials support — directly related to credential-mode behavior.
  • instantdb/instant#2588: Server-side OAuth client handling for shared credentials — related to route/model changes here.
  • instantdb/instant#2571: Modifies CLI auth client add flow and callback handling — overlaps with add/update refactors.

Suggested reviewers

  • drew-harris
  • dwwoelfel
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[cli] auth client update' directly matches the primary feature: a new auth client update command for modifying OAuth client credentials.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description comprehensively explains the new auth client update command, shared helpers, usage examples, and testing procedures, directly aligning with the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cli-codex-update

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

View Vercel preview at instant-www-js-cli-codex-update-jsv.vercel.app.

Copy link
Copy Markdown
Contributor

@drew-harris drew-harris left a comment

Choose a reason for hiding this comment

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

Image I think in the dev mode case, this menu should still be displayed with grayed out options that say (can't do this in dev mode)

Comment thread client/packages/cli/src/commands/auth/client/update.ts Outdated
Comment thread client/packages/cli/src/commands/auth/client/update.ts Outdated
Comment thread client/packages/cli/src/commands/auth/client/update.ts Outdated
) {
const { yes } = yield* GlobalOpts;
const publishableKey = yield* optOrPromptIf(opts, 'publishable-key', {
promptIf: !yes && !hasFlag(opts, 'publishable-key'),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the reasoning for the optOrPromptIf, is it just an inverted case of optOrPrompt's skipIf?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not quite. The reason is that update has a third case that skipIf doesn't model.

optOrPrompt handles:

  1. Parse the flag if it is present
  2. If the flag is missing, prompt for it

skipIf means the option is not allowed at all. If skipIf is true, we should reject the flag and also avoid prompting.

For update, we need a different behavior:

  1. Parse the flag if it is present
  2. If the flag is missing, prompt only when this update path needs the value
  3. If the flag is missing and this update path does not need the value, leave it unset

For example:

instant-cli auth client update --name github-prod --custom-redirect-uri https://example.com/callback

This should update only the redirect URI. We still want to allow --client-id and --client-secret if the user provides them, but we don't want to prompt for them when they're omitted.


I do wonder if we need to re-think the abstraction though. It's a bit hard to reason about. I changed the name to optOrPromptWhenNeeded, but perhaps I can think of something a bit different. Let me mull on this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Update: I will keep optOrPromptWhenNeeded in this PR, but propose a refactor in a separate PR.

My thinking is that we can 'decompose' optOrPromptIf, into a series of pipe operators:

 const name = yield* parseStringFlag(opts.name, '--name').pipe(
   orAsk(clientNamePrompt),
   orFail('--name'),
 )

This could let us be more explicit.

I don't want to make the change here though, as it would be quite involved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good idea

Comment thread client/packages/cli/src/commands/auth/client/update.ts Outdated
Comment thread client/packages/cli/src/lib/oauth.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: 3

🧹 Nitpick comments (1)
client/packages/cli/__tests__/authClientUpdate.test.ts (1)

265-278: ⚡ Quick win

Interactive Google-web → dev-credentials: missing clientId: null / clientSecret: null assertions

The toMatchObject at line 273 doesn't assert clientId: null and clientSecret: null, even though the real updateGoogleToDevCredentials explicitly passes those as null (Context snippet 2). If a regression causes the implementation to omit those fields, this test won't catch it.

🛡️ Tighter assertions
     expect(updatedClients[0]).toMatchObject({
       oauthClientId: 'google-web',
+      clientId: null,
+      clientSecret: null,
       useSharedCredentials: true,
       redirectTo: null,
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/packages/cli/__tests__/authClientUpdate.test.ts` around lines 265 -
278, The test "interactive Google web update can select dev credentials"
currently asserts properties on updatedClients[0] but omits clientId and
clientSecret; update the assertion in that test to also require clientId: null
and clientSecret: null so the test will fail if updateGoogleToDevCredentials
stops passing those fields — modify the expect(updatedClients[0]).toMatchObject
call in the test to include clientId: null and clientSecret: null.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/packages/cli/src/commands/auth/client/shared.ts`:
- Around line 205-210: The function domainFromClerkKey currently splits the key
on '_' and takes the last chunk, which breaks when the base64-encoded domain
contains underscores; change it to take the entire suffix after the first '_'
(e.g., use key.indexOf('_') and substring/slice from that position) so the full
base64 payload is decoded, then base64Decode and strip the leading '$' as
before; update domainFromClerkKey to handle missing '_' or decode errors
gracefully (return null) and reference domainFromClerkKey and base64Decode when
making the change.

In `@client/packages/cli/src/commands/auth/client/update.ts`:
- Around line 308-327: The code still triggers interactive prompts when the CLI
is run non-interactively with --yes; update the prompt logic so prompts are
suppressed when the non-interactive flag is present and instead surface an error
if required credentials are missing. Specifically, change the promptIf
expressions used in optOrPromptIf calls (for client-id, client-secret, redirect
URI) to also check that hasFlag(opts, 'yes') is false (e.g., promptIf:
shouldPromptClientId && !hasFlag(opts, 'yes')), and add an explicit pre-check
before calling optOrPromptIf (or set required accordingly) that throws or
returns a clear error when mustCollectCredentials is true but hasFlag(opts,
'yes') is true and the corresponding opts flag (client-id/client-secret) is not
provided; touch the variables shouldPromptClientId, shouldPromptClientSecret,
shouldPromptRedirectUri and the optOrPromptIf invocations to implement this
behavior.

---

Nitpick comments:
In `@client/packages/cli/__tests__/authClientUpdate.test.ts`:
- Around line 265-278: The test "interactive Google web update can select dev
credentials" currently asserts properties on updatedClients[0] but omits
clientId and clientSecret; update the assertion in that test to also require
clientId: null and clientSecret: null so the test will fail if
updateGoogleToDevCredentials stops passing those fields — modify the
expect(updatedClients[0]).toMatchObject call in the test to include clientId:
null and clientSecret: null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: df11328a-e64e-4d86-9fa9-b15daa4dd853

📥 Commits

Reviewing files that changed from the base of the PR and between 48fff27 and 81ad218.

📒 Files selected for processing (14)
  • client/packages/cli/__tests__/authClientAddGoogle.test.ts
  • client/packages/cli/__tests__/authClientList.test.ts
  • client/packages/cli/__tests__/authClientUpdate.test.ts
  • client/packages/cli/__tests__/oauthMock.ts
  • client/packages/cli/src/commands/auth/client/add.ts
  • client/packages/cli/src/commands/auth/client/delete.ts
  • client/packages/cli/src/commands/auth/client/list.ts
  • client/packages/cli/src/commands/auth/client/shared.ts
  • client/packages/cli/src/commands/auth/client/update.ts
  • client/packages/cli/src/index.ts
  • client/packages/cli/src/lib/oauth.ts
  • server/src/instant/dash/routes.clj
  • server/src/instant/model/app_oauth_client.clj
  • server/test/instant/dash/routes_test.clj

Comment thread client/packages/cli/src/commands/auth/client/shared.ts Outdated
Comment thread client/packages/cli/src/commands/auth/client/update.ts
Comment thread server/src/instant/model/app_oauth_client.clj
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
client/www/components/dash/auth/Clerk.tsx (1)

457-472: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Abort submit when Clerk domain parsing fails.

After the toast on Line 459, this handler still falls through to addClient and builds https://${domain}/.well-known/openid-configuration with domain === null. That turns a client-side validation failure into a broken server request. Return immediately once parsing fails.

💡 Suggested fix
     const domain = clerkDomainFromPublishableKey(publishableKey);
     if (!domain) {
       errorToast(
         'Could not determine Clerk domain from key. Ping us in Discord for help.',
         { autoClose: 5000 },
       );
+      return;
     }
     try {
       setIsLoading(true);
       const resp = await addClient({
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/www/components/dash/auth/Clerk.tsx` around lines 457 - 472, When
clerkDomainFromPublishableKey(publishableKey) returns falsy, abort the submit
flow immediately instead of continuing to build the discoveryEndpoint and
calling addClient; after calling errorToast(...) return early from the handler
so domain is never used (this prevents building
`https://${domain}/.well-known/openid-configuration` with a null/undefined
domain). Ensure the early return occurs before setIsLoading(true) and
addClient(...) so no server request is attempted when domain parsing fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/packages/platform/src/clerk.ts`:
- Around line 17-23: The decoded domain from clerkDomainFromPublishableKey must
be validated as a bare host/domain before returning; after calling
base64DecodeUtf8 and trimming the trailing '$', verify the value matches a
hostname regex (e.g. one or more dot-separated DNS labels: labels start/end with
alphanumeric, may contain hyphens, no
scheme/paths/userinfo/ports/colons/slashes) and return null if it does not; keep
the existing try/catch around base64DecodeUtf8 and only return the decoded value
when it passes this hostname validation, otherwise return null.

---

Outside diff comments:
In `@client/www/components/dash/auth/Clerk.tsx`:
- Around line 457-472: When clerkDomainFromPublishableKey(publishableKey)
returns falsy, abort the submit flow immediately instead of continuing to build
the discoveryEndpoint and calling addClient; after calling errorToast(...)
return early from the handler so domain is never used (this prevents building
`https://${domain}/.well-known/openid-configuration` with a null/undefined
domain). Ensure the early return occurs before setIsLoading(true) and
addClient(...) so no server request is attempted when domain parsing fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d4ed5683-3f45-458f-acdf-dfb9e4940f9b

📥 Commits

Reviewing files that changed from the base of the PR and between dceffa6 and ca9d57b.

📒 Files selected for processing (7)
  • client/packages/cli/__tests__/authClientUpdate.test.ts
  • client/packages/cli/src/commands/auth/client/shared.ts
  • client/packages/cli/src/commands/auth/client/update.ts
  • client/packages/platform/__tests__/src/clerk.test.ts
  • client/packages/platform/src/clerk.ts
  • client/packages/platform/src/index.ts
  • client/www/components/dash/auth/Clerk.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • client/packages/cli/src/commands/auth/client/shared.ts
  • client/packages/cli/tests/authClientUpdate.test.ts
  • client/packages/cli/src/commands/auth/client/update.ts

Comment thread client/packages/platform/src/clerk.ts Outdated
Comment on lines +17 to +23
export function clerkDomainFromPublishableKey(key: string): string | null {
try {
const match = key.match(/^pk_[^_]+_(.+)$/);
const domainPartB64 = match?.[1];
if (!domainPartB64) return null;
const domainPart = base64DecodeUtf8(domainPartB64);
return domainPart.replace(/\$$/, '');
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 4, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Only return a validated host from the decoded Clerk key.

Lines 19-23 currently accept any base64url payload after the pk_*_ prefix and return it unchanged apart from trimming the trailing $. The new callers treat that value as a host when they build the discovery endpoint, so a crafted key can resolve to a malformed target or an arbitrary internal hostname. Please reject anything that is not a bare Clerk host/domain and return null instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/packages/platform/src/clerk.ts` around lines 17 - 23, The decoded
domain from clerkDomainFromPublishableKey must be validated as a bare
host/domain before returning; after calling base64DecodeUtf8 and trimming the
trailing '$', verify the value matches a hostname regex (e.g. one or more
dot-separated DNS labels: labels start/end with alphanumeric, may contain
hyphens, no scheme/paths/userinfo/ports/colons/slashes) and return null if it
does not; keep the existing try/catch around base64DecodeUtf8 and only return
the decoded value when it passes this hostname validation, otherwise return
null.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this is fine.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@stopachka, understood — thanks for the clarification! I'll drop this suggestion.

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.

♻️ Duplicate comments (1)
client/packages/platform/src/clerk.ts (1)

14-23: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Hostname validation is still missing — decoded value is returned unsanitised.

The past review comment flagging this SSRF risk remains unresolved. The value returned by base64Decode is accepted verbatim (after stripping $) and used by callers to build discovery endpoint URLs, so a crafted publishable key can direct requests to arbitrary internal hosts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/packages/platform/src/clerk.ts` around lines 14 - 23, In
clerkDomainFromPublishableKey, stop returning the raw base64-decoded value:
after decoding and stripping '$', validate and sanitize the hostname — trim it,
reject any string containing ':' or '/' or other URI characters, ensure it
matches a strict hostname regex (e.g. /^[A-Za-z0-9.-]+$/), reject "localhost"
and any IP literal (use net.isIP or equivalent) and optionally block
private/reserved IP ranges; if validation fails, log and return null so callers
cannot build URLs to arbitrary/internal hosts.
🧹 Nitpick comments (1)
client/packages/platform/src/clerk.ts (1)

6-12: ⚡ Quick win

The catch branch of base64Decode is dead code — Buffer.from(s, 'base64') never throws.

Buffer.from with 'base64' silently ignores decoding errors rather than throwing, so the catch block on lines 9–11 is never reached. The stated rationale — falling back to url-safe decoding on error — cannot work via try/catch here.

Additionally, Node.js's 'base64' encoding already correctly accepts the "URL and Filename Safe Alphabet" as specified in RFC 4648, Section 5, making the fallback doubly unnecessary on modern Node.js. The entire helper can be simplified to a single expression:

♻️ Proposed simplification
-// Base64 decode, switching to url-safe decode if we hit an error
-// Can't be sure which method Clerk uses because you can't generate
-// `+` or `/` with characters that go in a normal host. Urls with
-// chinese characters exist, they might encode to `+` or `/`, and
-// Clerk might support them, so we'll be safe and do both.
-function base64Decode(s: string) {
-  try {
-    return Buffer.from(s, 'base64').toString('utf-8');
-  } catch (e) {
-    return Buffer.from(s, 'base64url').toString('utf-8');
-  }
-}

Then inline Buffer.from(domainPartB64, 'base64').toString('utf-8') directly in clerkDomainFromPublishableKey.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/packages/platform/src/clerk.ts` around lines 6 - 12, The base64Decode
function contains dead code because Buffer.from(s, 'base64') does not throw and
already accepts URL-safe base64; remove the try/catch and simplify by replacing
base64Decode(s) with a single expression Buffer.from(s,
'base64').toString('utf-8') (or collapse the helper entirely) and inline that
expression where used (e.g., in clerkDomainFromPublishableKey) so the fallback
branch is eliminated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@client/packages/platform/src/clerk.ts`:
- Around line 14-23: In clerkDomainFromPublishableKey, stop returning the raw
base64-decoded value: after decoding and stripping '$', validate and sanitize
the hostname — trim it, reject any string containing ':' or '/' or other URI
characters, ensure it matches a strict hostname regex (e.g. /^[A-Za-z0-9.-]+$/),
reject "localhost" and any IP literal (use net.isIP or equivalent) and
optionally block private/reserved IP ranges; if validation fails, log and return
null so callers cannot build URLs to arbitrary/internal hosts.

---

Nitpick comments:
In `@client/packages/platform/src/clerk.ts`:
- Around line 6-12: The base64Decode function contains dead code because
Buffer.from(s, 'base64') does not throw and already accepts URL-safe base64;
remove the try/catch and simplify by replacing base64Decode(s) with a single
expression Buffer.from(s, 'base64').toString('utf-8') (or collapse the helper
entirely) and inline that expression where used (e.g., in
clerkDomainFromPublishableKey) so the fallback branch is eliminated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d6342db2-1db6-48a2-adeb-0ebbe36af905

📥 Commits

Reviewing files that changed from the base of the PR and between ca9d57b and 3ebe658.

📒 Files selected for processing (2)
  • client/packages/platform/__tests__/src/clerk.test.ts
  • client/packages/platform/src/clerk.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • client/packages/platform/tests/src/clerk.test.ts

const redirectPrompt = redirectUriPrompt({
heading: 'Custom redirect URI (optional):',
});
const newRedirectPrompt = redirectUriPrompt({ heading: 'New redirect URI:' });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Checking for an Instant SDK...
Found @instantdb/core in your package.json.

Select a client to update:
● jlaksdfj (4eee42ad-9f77-4149-bc7e-0a972fa972e8)

Current mode: custom credentials
What do you want to update?
● Update redirect URI

New redirect URI:

[error] Missing required value for --custom-redirect-uri
hint: run `instant-cli auth client update --help` for available arguments

Press Enter to close

Pressing "update redirect uri" and then hitting enter without typing it, gives an error that looks like a flag is missing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it's ~somewhat reasonable to expect an error like a flag is missing in this case. Here we are explicitly going through "Update redirect URI", but then it is not provided.

It does expose a problem: what if someone wants to remove the redirect uri?

Right now we don't allow this.

Perhaps we should, but I worry it could lead to weird edge cases (like accidentally removing a redirect uri, when the user did not intend it)

provider_name: string;
};

const redirectPrompt = redirectUriPrompt({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Image

Might be nice for this to have a "skipped" indicator in the "answered" state

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

image

fixed!

Copy link
Copy Markdown
Contributor

@drew-harris drew-harris left a comment

Choose a reason for hiding this comment

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

functionality works

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.

2 participants