Repository navigation
Make clerk link claim the app a signed-out clerk init created - #600
shane-kercheval wants to merge 15 commits into
Conversation
- `clerk deploy` and `clerk deploy status` now pick their `not_linked` message by what the folder holds: a claim token from a signed-out `clerk init` (`.clerk/keyless.json`) says to run `clerk link` to claim it; an SDK keyless file (`.clerk/.tmp/keyless.json`) says a person claims it with `clerk open` first; anything else keeps today's text. The error code is unchanged. - Agents hit `not_linked` in these folders and ran `clerk link`, which can't list an application no account owns, so they linked the wrong app or gave up. - New helper `lib/unlinked-advice.ts` reads the breadcrumb without side effects, so an error message never deletes a malformed file. - The SDK keyless file path moves into `lib/keyless.ts` so the helper can use it without importing the Backend API client.
- After a successful claim, `attemptAutoclaim` compares the folder's publishable keys (env var, `.env`, `.env.local`) with the claimed app's development key. On any mismatch it links nothing and pulls nothing, and returns `keys_mismatch` with the sources. - Before, `clerk auth login` linked the claimed app and its env pull wrote the claimed app's secret key next to another app's publishable key. - `clerk auth login` prints the mismatch as a warning, exits 0, and shows the manual-link next steps. The breadcrumb stays cleared because the claim already happened. - The claim warning texts move from `login.ts` into `lib/autoclaim.ts` so `clerk link` can print the same ones.
- `clerk link` with no link and no `--app`, in a folder holding a claim token from a signed-out `clerk init`, now signs in if needed, claims that application into the account, links the folder, and writes its keys. It prints "Claimed and linked application: ..." with no prompt in either mode. - Before, `clerk link` listed only owned applications, so a person got a picker that couldn't show the app and an agent got the usage error. - When the folder's publishable key belongs to another application, `clerk link` stops with `key_pair_mismatch` and changes nothing. Other claim failures print the same warning as `clerk auth login` and continue with the usual flow. - Only the `clerk link` command claims: it sets the internal `claimKeylessApp` option on `link()`. `clerk init`, `clerk migrate` and the `clerk doctor` link fix call `link()` without it. - `login()` takes `claim: false`, which `clerk link` passes so it is the one step that claims and sees the result, including a key mismatch.
- Adds a minor changeset for `clerk link` claiming the `clerk init` application, the per-folder `not_linked` messages in `clerk deploy` and `clerk deploy status`, and the publishable-key check after a claim.
- The `not_linked` message for a folder a Clerk SDK set up now appears only when `.clerk/.tmp/keyless.json` holds a well-formed https claim link. A file with no link, a non-https link or malformed JSON gets the plain message, because `clerk open` would fail there with "no accountless application was found". - The claim-link reader moves from `commands/open/keyless-claim.ts` into `lib/keyless.ts`, so `clerk open` and the message use the same check. `clerk open` behaves as before; only its debug line's prefix changes. - The message now says what the CLI found, hedges on whether the app is still unowned, and gives agents runnable steps (`clerk auth login` if needed, `clerk apps list --json`, `clerk link --app <app_id>`).
- A claim that succeeds but doesn't link the folder (a failed config write, or an app with no development instance) no longer prints "Claimed and linked". `clerk link` prints "Claimed application" and links the claimed app directly, as `--app <its id>` would; `clerk auth login` says the folder wasn't linked and to run `clerk link`. - Both commands print the claim result, including the Dashboard URL, from one `reportClaimed` in `lib/autoclaim.ts`. - The publishable-key check runs only for the `clerk auth login` command, its `clerk login` alias and `clerk link`'s own claim. `clerk init`, `clerk migrate` and the `clerk doctor` fix sign in through `login()` without it and behave as before. `attemptAutoclaim` now requires callers to choose. - An env file the check can't read no longer counts as "no keys": the folder is linked but no keys are written, and a warning names the file and says to run `clerk env pull` once it's fixed. - A temporary claim failure or a managed workspace now stops `clerk link` with the original error and says to rerun `clerk link` or use `--app`, instead of offering a list that can't contain the claimed app. - Tests clear and restore the shell variables they depend on and stub the credential store in the autoclaim tests.
- `clerk link` still stops when a claim fails with the token kept, but the advice now depends on the cause: a 5xx, 429 or network failure says it is temporary and to rerun `clerk link`; any other 4xx says Clerk rejected the claim and to run `clerk link --app <app_id>`, because it would repeat on every run. - For Platform API failures the advice is the error's message prefix, so agent JSON carries it next to the API's own code, printed once. Network failures are reported as before, after a warning with the advice. - An unreadable env file no longer hides an exported publishable key for another app: the claim stops with `key_pair_mismatch` as it does when the files are readable. - `clerk link` tests set their mock defaults before each test, so each passes when run alone. - Removes an unused import from `commands/open/keyless-claim.ts`.
- `clerk auth login` warned "It will be retried on your next `clerk auth login`" for every claim failure that kept the token, and its next steps said "Run `clerk link` to connect your application manually", but a bare `clerk link` now retries the claim and stops if it fails again. - The 5xx/429/network test and both advice texts move into `lib/autoclaim.ts`, so login and link word a failure the same way: a temporary failure says to rerun `clerk link`; any other 4xx says Clerk rejected the claim, with the status and reason, and to run `clerk link --app <app_id>`. - Login's next steps offer "Run `clerk auth login` again to retry auto-claim" only after a temporary failure; both cases show "Run `clerk link --app <app_id>` to link a different application". - Adds an agent-mode `clerk link` test for a spent token (404) that continues into autolink. - Updates the auth README and the changeset to match.
- The `not_linked` message for a folder holding a signed-out `clerk init`'s claim token said the application was one "no account owns yet", but a Dashboard claim can leave the token file behind, so that wasn't known. - It now says what the CLI found and hedges the same way the SDK message does: "If no account owns that application yet, `clerk link` claims it into your account. Run `clerk link` to link this folder, then rerun `clerk deploy`." Running `clerk link` is the right step either way.
- The key check read only `.env` and `.env.local`, but the claim's env pull writes `.env.development.local` first when it exists, so a claim could overwrite another app's keys there. The check now reads the exported variable and `.env.development.local`, `.env.local`, `.env.development` and `.env`, each on its own.
- An unreadable file no longer hides a mismatch found in another file: any mismatch refuses with `key_pair_mismatch`, and an unreadable file only leads to "linked, no keys written" when nothing mismatches.
- The claim's env pull now writes the folder the check read (`pull({ cwd })`), not the process's working directory. CLI callers pass the current directory, so their behavior is the same.
- A 401 from the claim means the saved sign-in was rejected: both commands say to run `clerk auth login` and rerun `clerk link`, with no `--app` advice, which would fail the same way.
- Adds tests with the real link and env pull, a 401 in each command, the registered `clerk link` command with no or a malformed token file, no-organization continuing, and a failed key write.
- An expired saved session during the claim was classed as a temporary error, so `clerk link` printed "Rerun `clerk link` to retry" next to the session error saying to sign in. It now counts as a rejected sign-in: `clerk link` throws the session error alone, and `clerk auth login` gives the sign-in advice. - With `CLERK_PLATFORM_API_KEY` set, the Platform API client uses that key before any saved sign-in, so a 401 means the key was rejected. Both commands now say to fix or unset it instead of signing in again, and login's next steps match. - Any other error the CLI raises itself (e.g. `invalid_key_format`) is no longer called temporary. `clerk link` rethrows it unchanged; `clerk auth login` warns with its message and shows the fix-the-key step only when `CLERK_PLATFORM_API_KEY` is set, and no next steps otherwise. "Temporary" now covers only network failures, 5xx and 429. - The key check matched key lines with the shared env parser, which reads a CRLF line, an indented line or spaces around `=` as a comment, so another app's key on such a line was never seen. It now converts line endings, matches key lines with its own pattern, and compares values trimmed, unquoted and without an inline comment. The shared parser is unchanged. - The reader's comment and the READMEs say why `.env.development` is checked: `clerk env pull` never writes it, but it outranks `.env` in development. - `login.test.ts` clears and restores `CLERK_PLATFORM_API_KEY` around each test.
🦋 Changeset detectedLatest commit: 0468dc1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Note Reviews pausedIt 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
Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough
Merge Risk:
|
- `readSdkClaimUrl` checked that the SDK's claim link parsed as https but returned the original string. On Windows, `clerk open` launches the browser with `cmd /c start "" "<url>"`, so an https link containing a double quote, such as `https://a.com/" & calc & "`, passed the check and its quote ended the argument. - It now returns the parsed URL's `href`, which percent-encodes quotes and spaces, so no raw quote reaches `cmd.exe`. A normal claim link such as `https://dashboard.clerk.com/apps/claim?token=tok_sdk` is unchanged. - Adds a test that an https claim link with an embedded quote and spaces comes back percent-encoded.
- `clerk link` and `clerk auth login` no longer show a raw TypeError when a claim response has no `instances` array. `link()` reads the instances defensively, so `clerk link` ends in "Application has no development instance" (`instance_not_found`). The claim step checks for a development instance before calling `linkApp`, so `clerk auth login` says "This folder wasn't linked: the application has no development instance". The claim response is untrusted JSON, so a missing array must not fail a claim that already succeeded server-side. - After a key mismatch, `clerk auth login`'s only next step is "Run `clerk link --app <app_id>` with the application this folder should use", matching its warning. It used to suggest a bare `clerk link` and then `clerk env pull`. The folder isn't linked yet, so there is nothing to pull until `--app` picks the application, and a bare `clerk link` that picks the claimed app, followed by a pull, would write its keys over the other app's. - Corrects two comments that said `clerk link` can't link an unclaimed app. It claims an app a signed-out `clerk init` created; an SDK-created app still needs `clerk open`, which is why `whoami` keeps its keyless next steps. - Defines the SDK keyless file path once, in `lib/keyless.ts`. `lib/keyless-target.ts` imports it, so `clerk open`, the deploy advice, `env pull`, `whoami` and `doctor` can't drift apart if the SDK moves the file. - `ensureAuth` passes `claim` straight to `login()`. `runLogin` treats an undefined `claim` as `true`, so the ternary that dropped the key changed nothing. - Shortens the claim code's doc comments to the constraint each states. The reasoning moved here: - Why a claim failed decides its advice. An expired saved session or a 401 means the credentials were rejected. Any other `CliError` (e.g. `invalid_key_format`) is the CLI's own check, repeats on every run, and its message already says what to fix. Any other 4xx also repeats on every run. - The key check covers the exported variable and the development env files. `clerk env pull` writes `.env.development.local`, `.env.local` or `.env`. Each file is read on its own. - `clerk init`, `clerk migrate` and the `clerk doctor` fix sign in through `login()` without the key check, so they keep linking whatever they claim. They call `link()` without `claimKeylessApp`. A signed-out `clerk link --app` or relink also claims during sign-in, without the check. - For an integration-managed workspace, the user switches to a workspace they own and the next `clerk auth login` or `clerk link` retries. - `login.test.ts` sets the browser opener's default before each test as well as after, so the first test no longer depends on an earlier one.
- The auth README said `clerk link` always signs in with claiming turned off. Only its claim step does; a signed-out `clerk link --app` or relink signs in with claiming on, so that sign-in claims, without the key check. Its list of which commands check keys now includes that case. - The deploy README said the `not_linked` message names the kind of application because only one kind is listed by `clerk link`. The picker lists neither; the reason is that `clerk link` can claim only the kind `clerk init` created. - The `key_pair_mismatch` comment named only `clerk env pull`'s use. `clerk link` now also returns the code when the folder's publishable key belongs to an app other than the one it just claimed, so an agent branching on the code meets both situations. - The changeset adds that `clerk auth login`'s claim-failure messages now say why the claim failed and what to do, and that `clerk init`, `clerk migrate` and `clerk doctor` print them too when they sign in.
- The sentence listing who signs in without the key check said "a signed-out `clerk link --app` or relink sign in", which read as if "relink" were a verb; it now says "signed-out `clerk link --app` and relink flows call `login()`".
What this PR does and why
Before this PR.
clerk initrun without signing in creates a Clerk app that no account owns and writes its keys into the project. That part is unchanged by this PR. The bug:clerk deployin that project fails with "No Clerk project linked to this directory. Runclerk link", butclerk linkcan't help a person who is already signed in. It shows a picker of the apps their account owns, and this app has no owner, so it isn't in the list. An agent gets "Cannot select an application in agent mode. Pass --app ". A signed-out person is the exception: their sign-in claims the app before the picker opens. Either way the project ends up linked to the wrong app or not linked at all.After this PR:
clerk linkin that project claims the app into your account, links the project to it, and writes the app's keys. If an env file can't be read, it links but writes no keys, and says "Runclerk env pullonce the file is fixed." No picker, no prompt. It prints "Claimed and linked application:<name>" and the app's Dashboard URL.clerk deployandclerk deploy statusin that project still fail with error codenot_linked, but the message now says: "This folder holds a claim token a signed-outclerk initwrote to.clerk/keyless.json. If no account owns that application yet,clerk linkclaims it into your account. Runclerk linkto link this folder, then rerunclerk deploy." Forclerk deploy status, the message ends "then rerunclerk deploy status."clerk init(the SDK writes a claim link to.clerk/.tmp/keyless.json), gets a different message: a person must claim the app withclerk openfirst, then runclerk link. The CLI can't claim those itself.clerk linkclaims the app but refuses to link the project or touch the env file, and says to runclerk link --app <app_id>.clerk auth login, which already claimed apps this way, now refuses the same way. Before, it wrote the claimed app's keys over the other app's.clerk linkexits with an error that says what to do. Before, it showed the picker, which couldn't contain the right app.clerk openin a project whose app a Clerk SDK created passes the claim link from.clerk/.tmp/keyless.jsonto the browser in its parsed form, which turns a"or a space in the link into%22or%20. Before, it passed the file's raw text, and on Windows a"in that text could end the launcher's command line. Thehttps-only check is unchanged.clerk init,clerk migrate, andclerk doctorpick apps and write keys exactly as before. When their sign-in claims an app, they printclerk auth login's claim messages, which now say why a claim failed and what to do. Before, every failure said "Auto-claim failed due to a temporary error. It will be retried on your nextclerk auth login."Decisions
clerk linkclaims without asking.--app. The ticket asks for a yes/no prompt before claiming; left out to keep this PR small.clerk linkstops instead of showing the picker. A token Clerk reports as no longer valid is deleted, andclerk linkcontinues as onmain..env.development.local,.env.local,.env.development,.env, and the shell variable, where the CLI's existing key reader reads only.envand.env.local. It also reads lines the CLI's env parser skips (Windows line endings, indented lines, spaces around=).clerk env pullwrites.env.development.localfirst when that file exists, so a check that skipped it would overwrite keys there. The first version used the existing reader; review found both gaps. The env parser itself is unchanged.clerk linkandclerk auth loginonly.clerk init,clerk migrate, andclerk doctorchoose the app and write keys themselves. A refusal in the middle of their sign-in would leave them half done.clerk link --app <id>still claims the project'sclerk initapp during sign-in, as onmain.clerk auth loginto claim and relink.Notes for the reviewer
checkLocalKeysinlib/autoclaim.ts) is where a bug hurts most. A missed key pairs one app's secret key with another app's publishable key, the exact thing the check exists to prevent. A false match blocks a correct link, and the only way past it is--app. Known gap: it readsKEY=valuelines asclerk initandclerk env pullwrite them, not every line dotenv accepts. AKEY: valueassignment is skipped, and a#commentwith no space before it or a backtick-quoted value is compared as part of the key.clerk linkstill gets the browser sign-in and its 15-minute wait, as onmain. The newdeploymessage sends agents toclerk link, so this is the next gap.clerk initfor each framework,clerk link --app,clerk migrate, and API, config and users commands, but never a signed-outclerk initfollowed by a claim, so CI does not exercise the claim.How it was tested
bun run format:check,bun run lint,bun run typecheck, andbun run testpass at 837f811: 6,560 tests, 0 failures (my run). CI's Lint, Test, and E2E Test jobs pass at 0468dc1, the current head. The new tests were not run againstmain; they test a claim thatmain'sclerk linknever makes, so I expect them to fail there.clerk initwhile signed out, thenclerk deployprinted thenot_linkedmessage quoted above. After signing in,clerk linkprinted "Claimed and linked application:handson-app-a" and the Dashboard URL.clerk initwhile signed out, then another app's publishable key written into.env.local, thenclerk linkprinted:.env.localwas unchanged.Drafted by Claude, reviewed by Shane