Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR closes a safety gap in Changes
Review Notes
|
There was a problem hiding this comment.
Pull request overview
Adds the standard “destructive action safety” pattern to auth revoke-token and aligns auth keys revoke with the shared confirmation-prompt helper, with corresponding unit test updates.
Changes:
- Added
--forceflag + interactive confirmation prompt toauth revoke-token, including a JSON-mode guard that requires--force. - Switched
auth keys revokeconfirmation frominteractiveHelper.confirm()topromptForConfirmation()for consistency. - Updated/extended unit tests to pass
--forcewhere required and to cover JSON guard + user-cancel flows.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/commands/auth/revoke-token.ts |
Adds --force, JSON guard, and interactive confirmation before revoking tokens. |
src/commands/auth/keys/revoke.ts |
Standardizes the destructive confirmation prompt helper. |
test/unit/commands/auth/revoke-token.test.ts |
Updates existing tests for new --force requirement and adds new coverage for guard/cancel behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
0ab3a37 to
736ff38
Compare
…dize keys revoke prompt - Add `--force` flag and interactive confirmation prompt to `auth revoke-token` (the only hard-delete command missing the safety pattern) - Require `--force` in JSON mode to prevent accidental scripted revocations - Replace `interactiveHelper.confirm()` with `promptForConfirmation()` in `auth keys revoke` for consistency with all other destructive commands - Update tests with new --force flag and add coverage for JSON guard and cancellation flow
736ff38 to
e4c0028
Compare
|
@claude review |
There was a problem hiding this comment.
Review: fix(auth): add --force flag to revoke-token, standardize keys revoke prompt
The core change is correct and necessary. Adding --force to auth revoke-token was the right fix — it was the only hard-delete command missing this safety pattern. The JSON-mode guard and interactive confirmation flow are implemented correctly and mirror what other destructive commands do.
Two issues worth addressing:
1. Incomplete standardization in keys/revoke.ts — second interactiveHelper.confirm() left unchanged
The PR standardizes the primary revocation prompt (line ~76) to promptForConfirmation(), but the secondary prompt at line ~112 still uses this.interactiveHelper.confirm() (inquirer):
// Line ~112 — unchanged in this PR
const shouldRemove = await this.interactiveHelper.confirm(
"The revoked key was your current key for this app. Remove it from configuration?",
);interactiveHelper.confirm uses inquirer, while promptForConfirmation uses readline. These behave differently in non-TTY environments — inquirer may throw or hang, while readline degrades gracefully. In a single command run, the user now hits two different prompt UX implementations back-to-back. If the stated goal is to standardize on promptForConfirmation, this second call should be updated too.
2. Cancellation test uses fragile stdin monkey-patching
it("should cancel when user declines confirmation", async () => {
// replaces process.stdin with a Readable and pushes "n\n" via queueMicrotask
mockStdinAnswer("n");
...
const { stderr } = await runCommand([...]);
expect(stderr).toContain("Revocation cancelled");
expect(revokeNock.isDone()).toBe(false);
});This works due to Node.js Readable stream buffering (data is pushed before readline attaches), but it is fragile: any refactor of promptForConfirmation (e.g., switching to inquirer or a different input mechanism) would silently break this test without a compile error. The idiomatic approach is to mock the function itself:
import * as promptModule from "../../../utils/prompt-confirmation.js";
vi.spyOn(promptModule, "promptForConfirmation").mockResolvedValueOnce(false);This makes the test's intent explicit and decouples it from the implementation details of how stdin is read.
Both issues are fixable before merge. The feature itself is correct.
|
Looks like there's a couple of Claude comments so will hold off the review til those are addressed :) |
|
@claude review |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ee013f5 to
855a69a
Compare
871d9be to
46a8e72
Compare
|
Two issues reported by claude are not real issues. Still I have addressed both. The first one |
|
Tested on the local, all flags working as expected 👍
|
…-confirmation-prompt
| description: "Revoke all tokens issued to this client ID", | ||
| exclusive: ["revocation-key"], | ||
| }), | ||
| "revocation-key": Flags.string({ |
There was a problem hiding this comment.
Question, should these be mandatory mutally exclusive flags, or would it even be better to have separate commands, e.g. revoke-token by-clientid, revoke-token by-key ?
There was a problem hiding this comment.
I looked at OCLIF_AUTOGENERATED_DOC.md for all commands, we have no by-* subcommand pattern anywhere. All commands follow the pattern, and targeting/filtering is always done via flags (--client-id, --device-id, --type, --prefix, etc.). Introducing by-* subcommands here would break that consistency.
There was a problem hiding this comment.
revocation-key is only valid for JWT tokens with following criteria
To designate a revocation key, include the following additional claim in the JWT:
- `x-ably-revocation-key`: a string used to identify which token(s) to revoke in the revocation request.
This target specifier will match tokens that have the specified `revocationKey`.
Each flag is designed for a specific use case, so they are kept mutually exclusive.
There was a problem hiding this comment.
I'm fine with how this is done currently e.g.
- Manual mandatory check (
if (!clientId && !revocationKey)) instead ofexactlyOne- we get slightly better help formatting using our manual check instead of the oclif property... also this is a codebase wide standard we can revisit later if we want (not important for GA) - Correct use of the
exclusiveproperty
I also agree using the flags is better than having separate commands. It just makes more sense in the CLI.
One thing I would change... the fail message needs updating to match what we do elsewhere...
it is currently ""Either --client-id or --revocation-key is required. See https://ably.com/docs/auth/revocation for details."
However:
- We don't add doc urls in any fail messages - this should be removed.
- "Either --client-id or --revocation-key must be provided" follows the language of other similar fail messages
| }), | ||
|
|
||
| "client-id": Flags.string({ | ||
| char: "c", |
There was a problem hiding this comment.
I know you didn't touch this, but we don't actually shorthand client-d anywhere else in the codebase... i'd push to remove this here altogether
| exclusive: ["revocation-key"], | ||
| }), | ||
| "revocation-key": Flags.string({ | ||
| char: "r", |
There was a problem hiding this comment.
given the client-id shorthand will be removed, i'd also remove this tbh
| const confirmed = await this.interactiveHelper.confirm( | ||
| "This will permanently revoke this key and any applications using it will stop working. Continue?", | ||
| const confirmed = await promptForConfirmation( | ||
| "\nThis will permanently revoke this key and any applications using it will stop working. Are you sure?", |
There was a problem hiding this comment.
| "\nThis will permanently revoke this key and any applications using it will stop working. Are you sure?", | |
| "\nThis will permanently revoke this key and any applications using it will stop working. Are you sure you want to continue?", |
| "allow-reauth-margin": Flags.boolean({ | ||
| default: false, | ||
| description: | ||
| "[default: false] Delay enforcement by 30s so connected clients can obtain a new token before disconnection.", |
There was a problem hiding this comment.
The presence of this flag means it is enabled, meaning the absence is a default of false. Having the false default in the description is confusing imo - pretty sure we follow this standard for all default: false flags
| "[default: false] Delay enforcement by 30s so connected clients can obtain a new token before disconnection.", | |
| "Delay enforcement by 30s so connected clients can obtain a new token before disconnection.", |
|
can you ensure you run |
…ages as per review comments
Ran |
e20f8a4 to
c46534f
Compare
Out of date review, all changes are well addressed
Fixes https://ably.atlassian.net/browse/DX-1123
Link to internal discussion -> https://ably-real-time.slack.com/archives/C04KNCEP4RH/p1776855422090079
Removed existing
Tokenargument and added mutually exclusiveclient-idandrevocation-key, optionalallow-reauth-marginflag.Tested on the local, all flags working as expected
pnpm cli auth issue-ably-token --capability '{"*":["*"]}'ABLY_TOKEN="Issued Token" pnpm cli channels subscribe my-channel --pretty-jsonpnpm cli auth revoke-token --client-id "client-id-from-issued-token"Summary
auth revoke-token: Added--forceflag and interactive confirmation prompt — this was the only hard-delete command missing the safety pattern. In JSON mode,--forceis required to prevent accidental scripted revocations.tokenarg — the Ably token revocation APIdoes not support revoking a single token by its string value. It only supports
clientIdandrevocationKeytarget specifiers(see docs).
--client-idand--revocation-keyflags — mutually exclusive,at least one required. Validated in code with a helpful error message linking
to the docs.
--allow-reauth-marginflag — maps to the API'sallowReauthMarginparameter, giving connected clients a 30-second grace period to obtain a new
token before disconnection.
(
createAblyRealtimeClient) that was never used; the actual API call is araw HTTPS request.
this.fail()calls to use
"revokeToken".auth keys revoke: ReplacedinteractiveHelper.confirm()withpromptForConfirmation()to match the pattern used by all other destructive commands.--force, added coverage for JSON mode guard and user cancellation flow.