Skip to content

[claude] shared dev credentials in instant-cli + auth client update#2623

Closed
stopachka wants to merge 1 commit into
mainfrom
claude/cli-shared-dev-credentials
Closed

[claude] shared dev credentials in instant-cli + auth client update#2623
stopachka wants to merge 1 commit into
mainfrom
claude/cli-shared-dev-credentials

Conversation

@stopachka
Copy link
Copy Markdown
Contributor

Mirrors the dashboard's shared dev credentials feature (#2553) in instant-cli, plus a generalized auth client update that works across providers.

What's new

auth client add — Google web

  • New --use-shared-credentials flag. With it, --client-id / --client-secret / --custom-redirect-uri may be omitted; the server fills credentials in at runtime and auto-allows localhost / Expo redirects.
  • Interactive: after picking app-type=web, prompt "How do you want to set up credentials?" with Use Instant's shared dev credentials as the default.
  • --yes (agent / create-instant-app UX): with no creds and no flag, auto-defaults to shared and prints Using Instant's shared dev credentials. Native app types still require --client-id.
  • Mutually-exclusive validation: shared rejects --client-id / --client-secret / --custom-redirect-uri; native app types reject --use-shared-credentials.

Boxen output (shared mode):

```
Google OAuth client created: google-web
App type: web Mode: shared dev credentials
ID:

Redirect origins enabled: http://localhost, https://localhost, exp://
Capped at 100 sign-ups. Run `instant-cli auth client update` with --client-id and --client-secret before going to production.
```

auth client update (new) — works across providers

Identifies a client by `--id` or `--name` (mirrors `auth client delete`). Per-provider dispatch:

  • Google: rotate creds, upgrade shared → custom, downgrade custom → shared, update redirect.
  • GitHub / LinkedIn: rotate `--client-id` / `--client-secret`, update `--custom-redirect-uri`.
  • Apple: rotate `--services-id` / `--private-key-file`, update `--team-id` / `--key-id` (meta), update redirect.
  • Clerk: rotate `--publishable-key` (CLI re-derives the discovery endpoint).
  • Firebase: rotate `--project-id` (CLI re-derives the discovery endpoint).

Interactive picker when no `--id` / `--name` is given (non-`--yes`). `--yes` is strict — pass at least one update flag or get "Nothing to update".

`auth client list`

Adds `Mode: shared dev credentials` under `Client id:` when applicable. JSON output passes the field through via the schema.

Server changes

To make `update` work for Clerk / Firebase, `update-oauth-client` now accepts `discovery_endpoint`. `redirect_to` is also added to the response `select-keys` (was missing). The `app_oauth_client` model's `update!` plumbs the new field through with the same discovery validation as `create!`.

Testing

  • `pnpm --filter instant-cli test` — 132 tests pass, including:
    • 6 new tests for shared dev credentials in `authClientAddGoogle.test.ts` (auto-default, explicit flag, mutually-exclusive errors, native rejection, interactive selector).
    • 16 new tests in `authClientUpdate.test.ts` covering all six providers and identification edge cases.
  • Server `make lint` clean (0 errors, 0 warnings).
  • The pre-existing `@instantdb/react-native` typesTests failures on `main` are unrelated to these changes.

Manual smoke matrix (against a local dev server)

  • `instant-cli auth client add --type google` (interactive) → pick web → pick shared → confirm boxen.
  • `instant-cli auth client add --type google --app-type web --yes` → auto-shared with log line.
  • `instant-cli auth client add --type google --app-type web --use-shared-credentials --yes` → explicit shared.
  • `instant-cli auth client add --type google --app-type web --client-id X --client-secret Y --yes` → custom path unchanged.
  • `instant-cli auth client add --type google --app-type ios --yes` → existing missing-flag error.
  • `instant-cli auth client add --type google --use-shared-credentials --client-id X --yes` → mutually-exclusive error.
  • `instant-cli auth client list` → shared client shows `Mode: shared dev credentials`; custom unchanged.
  • `instant-cli auth client update --name google-web --client-id X --client-secret Y --yes` → upgrades to custom; subsequent list drops the Mode line.
  • `instant-cli auth client update --name google-web --use-shared-credentials --yes` → downgrades to shared.
  • `instant-cli auth client update --name clerk-web --publishable-key pk_test_... --yes` → Clerk discovery endpoint regenerated.
  • End-to-end via `bunx create-instant-app` + `auth client add --type google --yes` → sign in works on `localhost:3000`.

🤖 Generated with Claude Code

Mirrors the dashboard's shared dev credentials feature (#2553) in
instant-cli, plus a new `auth client update` command that works
across providers.

## auth client add (Google web)

- New `--use-shared-credentials` flag. With it, `--client-id` /
  `--client-secret` / `--custom-redirect-uri` may be omitted; the
  server fills creds in at runtime and auto-allows localhost / Expo
  redirects.
- Interactive: after picking app-type=web, prompt "How do you want
  to set up credentials?" with shared as the default.
- `--yes` with neither shared nor custom flags auto-defaults to
  shared with a clear `Using Instant's shared dev credentials.`
  log line. Native app types still require `--client-id`.
- Mutually-exclusive validation: shared rejects --client-id /
  --client-secret / --custom-redirect-uri; native rejects shared.

## auth client update (new)

Identifies a client by `--id` or `--name`, dispatches per-provider:

- Google: rotate creds, switch shared <-> custom, update redirect.
- GitHub / LinkedIn: rotate creds, update redirect.
- Apple: rotate services-id / private-key-file, update team-id /
  key-id meta, update redirect.
- Clerk: rotate publishable-key (re-derives discovery_endpoint).
- Firebase: rotate project-id (re-derives discovery_endpoint).

Interactive picker when --id/--name omitted (non-yes). Strict
"nothing to update" error in --yes without flags.

## auth client list

Shows `Mode: shared dev credentials` when applicable. JSON output
passes through via the new schema field.

## Server

`update-oauth-client` (dash/routes.clj) now accepts
`discovery_endpoint` so Clerk / Firebase clients can be updated in
place. `redirect_to` is added to the response select-keys (it was
missing). `app_oauth_client` model's `update!` plumbs the new field
through with the same discovery validation as create!.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

This PR introduces shared developer credentials support for Google OAuth clients, enables switching between credential modes (shared vs. custom) in the add command, and implements a new authClientUpdateCmd for updating existing OAuth clients across multiple providers with provider-specific field handling and validation.

Changes

Cohort / File(s) Summary
Add Command - Shared Credentials Tests
client/packages/cli/__tests__/authClientAddGoogle.test.ts
New test suite verifying shared credentials mode auto-selection with --yes, explicit flag handling, mutual exclusivity validation with --client-id, app-type restrictions, and interactive prompts for credential-mode selection.
Update Command Tests
client/packages/cli/__tests__/authClientUpdate.test.ts
Comprehensive test suite covering authClientUpdateCmd across multiple OAuth providers (Google, GitHub, Apple, Clerk, Firebase) with flag validation, provider-client fixtures, mocked OAuth layer, and assertions for credential switches, secret rotation, redirect URI updates, metadata handling, and discovery endpoint derivation.
Add Command Implementation
client/packages/cli/src/commands/auth/client/add.ts
Adds credential-mode selection logic (shared vs. custom) for Google web clients; parses/validates --use-shared-credentials flag, enforces mutual exclusivity, auto-selects shared mode with --yes, prompts interactively otherwise, and skips client-id/secret/redirect prompts in shared mode.
List Command Output
client/packages/cli/src/commands/auth/client/list.ts
Extends output to conditionally display Mode: shared dev credentials field when client has use_shared_credentials enabled.
Update Command Implementation & CLI Wiring
client/packages/cli/src/commands/auth/client/update.ts, client/packages/cli/src/index.ts
New authClientUpdateCmd (817 lines) supporting OAuth client updates by ID or name with provider-specific handling (Google credential mode/secret/redirect, Apple metadata/key, Clerk domain/discovery, Firebase project-id/discovery); includes flag validation, interactive picker fallback, and provider-labeled summaries. Also adds auth client update subcommand definition and --use-shared-credentials flag to add command.
OAuth Library Infrastructure
client/packages/cli/src/lib/oauth.ts
Extends OAuthClient schema with optional use_shared_credentials field; adds OAuthClientType export; updates addOAuthClient to accept and send useSharedCredentials; introduces updateOAuthClient function for POST-based client updates; adds findClientByIdOrName helper enforcing mutual exclusivity for client lookup.
Server Route Handler
server/src/instant/dash/routes.clj
Updates update-oauth-client route handler to parse optional discovery_endpoint from request and include :redirect_to in response payload.
Server Model Update
server/src/instant/model/app_oauth_client.clj
Enhances update! to validate discovery endpoints by fetching provider documents and verifying :issuer field; persists discovery endpoint updates to the database.

Sequence Diagram(s)

sequenceDiagram
    participant User as CLI User
    participant CLI as Auth Add Command
    participant Server as API Server
    participant DB as Database
    
    User->>CLI: Run add with --yes (Google web)
    CLI->>CLI: Detect minimal flags, app-type=web
    CLI->>CLI: Auto-select shared credentials mode
    CLI->>Server: POST /oauth-clients with useSharedCredentials:true
    Server->>DB: Create client with use_shared_credentials=true
    DB-->>Server: Client created
    Server-->>CLI: Return client record
    CLI->>User: Log "shared dev credentials" summary
Loading
sequenceDiagram
    participant User as CLI User
    participant CLI as Auth Update Command
    participant UI as Interactive Picker
    participant Server as API Server
    participant DB as Database
    
    User->>CLI: Run update (no --id/--name)
    CLI->>UI: Prompt for client selection
    UI-->>CLI: Return selected client
    CLI->>CLI: Gather provider-specific inputs via prompts
    alt Credential Mode Toggle
        CLI->>Server: POST /oauth-clients/{id} with use_shared_credentials flag
    else Custom Credentials Update
        CLI->>Server: POST /oauth-clients/{id} with client_id/secret
    else Discovery Endpoint Update
        CLI->>Server: POST /oauth-clients/{id} with discovery_endpoint
    end
    Server->>Server: Validate discovery endpoint (fetch provider doc)
    Server->>DB: Update client record
    DB-->>Server: Success
    Server-->>CLI: Return updated client
    CLI->>User: Display boxed provider-labeled summary
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • dwwoelfel
  • nezaj
🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title references both main features added: shared dev credentials and the auth client update command, clearly reflecting the primary changes in the PR.
Description check ✅ Passed The description comprehensively explains the new shared dev credentials feature, the auth client update command, and server changes, and is directly related to the changeset.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/cli-shared-dev-credentials

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
Review rate limit: 6/8 reviews remaining, refill in 14 minutes and 13 seconds.

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

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)
server/src/instant/dash/routes.clj (1)

655-656: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the admin-token-capable app lookup here.

auth client update is wired with allowAdminToken: true, but this handler still goes through req->app-and-user!, which only accepts a user refresh token. As a result, updates authenticated with an app admin token will fail even though the neighboring OAuth client routes work with that auth mode.

Suggested fix
-(defn update-oauth-client [req]
-  (let [{{app-id :id} :app} (req->app-and-user! :collaborator req)
+(defn update-oauth-client [req]
+  (let [{{app-id :id} :app} (req->app-accepting-superadmin-or-ref-token!
+                              :collaborator
+                              :apps/write
+                              req)
         id (ex/get-param! req [:params :id] uuid-util/coerce)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/src/instant/dash/routes.clj` around lines 655 - 656, The handler
currently uses req->app-and-user! which only accepts a user refresh token;
replace that call with the admin-token-capable app lookup used by neighboring
OAuth client routes (e.g. req->app-and-user-or-admin! or the project’s
req->app-or-admin! helper) so the route honors allowAdminToken: true for auth
client update; keep the same destructuring ({ {app-id :id} :app } ...) and id
parsing (ex/get-param! ...) but obtain the app via the admin-capable lookup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/packages/cli/src/commands/auth/client/update.ts`:
- Around line 176-183: The code drops the prompted redirect URI when rotating an
existing custom Google client because the intent created for rotation ({ kind:
'rotate', clientId, clientSecret }) omits redirect; update the intent creation
so the redirect is preserved for custom clients: when isShared is true and we
are rotating (i.e., clientId/clientSecret provided), include the redirect field
on the intent object (e.g., { kind: 'rotate', clientId, clientSecret, redirect
}) or otherwise ensure both branches produced by intent (set-custom and rotate)
carry the redirect from promptCustomRedirectUri/redirectFlag.

---

Outside diff comments:
In `@server/src/instant/dash/routes.clj`:
- Around line 655-656: The handler currently uses req->app-and-user! which only
accepts a user refresh token; replace that call with the admin-token-capable app
lookup used by neighboring OAuth client routes (e.g. req->app-and-user-or-admin!
or the project’s req->app-or-admin! helper) so the route honors allowAdminToken:
true for auth client update; keep the same destructuring ({ {app-id :id} :app }
...) and id parsing (ex/get-param! ...) but obtain the app via the admin-capable
lookup.
🪄 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: 68dccee3-5436-4752-8dda-a493ebfdc8ee

📥 Commits

Reviewing files that changed from the base of the PR and between 9988b73 and 2b38daf.

📒 Files selected for processing (9)
  • client/packages/cli/__tests__/authClientAddGoogle.test.ts
  • client/packages/cli/__tests__/authClientUpdate.test.ts
  • client/packages/cli/src/commands/auth/client/add.ts
  • client/packages/cli/src/commands/auth/client/list.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

Comment on lines +176 to +183
const redirect = redirectFlag
? (((yield* promptCustomRedirectUri(opts)) || undefined) as
| string
| undefined)
: undefined;
intent = isShared
? { kind: 'set-custom', clientId, clientSecret, redirect }
: { kind: 'rotate', clientId, clientSecret };
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve --custom-redirect-uri when rotating custom Google credentials.

For an existing custom Google client, this branch prompts for a redirect URI but then throws it away by emitting kind: 'rotate', so --custom-redirect-uri is silently ignored whenever --client-id/--client-secret are also passed.

Suggested fix
   type Intent =
     | {
         kind: 'set-custom';
         clientId: string;
         clientSecret: string;
         redirect?: string;
       }
     | { kind: 'switch-to-shared' }
-    | { kind: 'rotate'; clientId: string; clientSecret: string }
+    | {
+        kind: 'rotate';
+        clientId: string;
+        clientSecret: string;
+        redirect?: string;
+      }
     | { kind: 'update-redirect'; redirect: string };
@@
     const redirect = redirectFlag
       ? (((yield* promptCustomRedirectUri(opts)) || undefined) as
           | string
           | undefined)
       : undefined;
     intent = isShared
       ? { kind: 'set-custom', clientId, clientSecret, redirect }
-      : { kind: 'rotate', clientId, clientSecret };
+      : { kind: 'rotate', clientId, clientSecret, redirect };
@@
     case 'rotate':
       body.client_id = intent.clientId;
       body.client_secret = intent.clientSecret;
+      if (intent.redirect) body.redirect_to = intent.redirect;
       summaryLines.push(`Google Client ID: ${intent.clientId}`);
+      if (intent.redirect) {
+        summaryLines.push(`Redirect URI: ${intent.redirect}`);
+      }
       break;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/packages/cli/src/commands/auth/client/update.ts` around lines 176 -
183, The code drops the prompted redirect URI when rotating an existing custom
Google client because the intent created for rotation ({ kind: 'rotate',
clientId, clientSecret }) omits redirect; update the intent creation so the
redirect is preserved for custom clients: when isShared is true and we are
rotating (i.e., clientId/clientSecret provided), include the redirect field on
the intent object (e.g., { kind: 'rotate', clientId, clientSecret, redirect })
or otherwise ensure both branches produced by intent (set-custom and rotate)
carry the redirect from promptCustomRedirectUri/redirectFlag.

@github-actions
Copy link
Copy Markdown
Contributor

@stopachka stopachka closed this Apr 30, 2026
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