Skip to content

[DX-1123] fix(auth): redesign revoke-token to match Ably revocation API semantics#364

Merged
sacOO7 merged 10 commits intomainfrom
fix/revoke-token-with-confirmation-prompt
Apr 30, 2026
Merged

[DX-1123] fix(auth): redesign revoke-token to match Ably revocation API semantics#364
sacOO7 merged 10 commits intomainfrom
fix/revoke-token-with-confirmation-prompt

Conversation

@sacOO7
Copy link
Copy Markdown
Contributor

@sacOO7 sacOO7 commented Apr 21, 2026

  1. Issue token => pnpm cli auth issue-ably-token --capability '{"*":["*"]}'
  2. Subscribe to channel using token => ABLY_TOKEN="Issued Token" pnpm cli channels subscribe my-channel --pretty-json
  3. Revoke token => pnpm cli auth revoke-token --client-id "client-id-from-issued-token"

Summary

  • auth revoke-token: Added --force flag and interactive confirmation prompt — this was the only hard-delete command missing the safety pattern. In JSON mode, --force is required to prevent accidental scripted revocations.
  • Removed misleading positional token arg — the Ably token revocation API
    does not support revoking a single token by its string value. It only supports
    clientId and revocationKey target specifiers
    (see docs).
  • Added --client-id and --revocation-key flags — mutually exclusive,
    at least one required. Validated in code with a helpful error message linking
    to the docs.
  • Added --allow-reauth-margin flag — maps to the API's allowReauthMargin
    parameter, giving connected clients a 30-second grace period to obtain a new
    token before disconnection.
  • Removed dead code — the command was creating an Ably Realtime client
    (createAblyRealtimeClient) that was never used; the actual API call is a
    raw HTTPS request.
  • Fixed component string inconsistency — standardized all this.fail()
    calls to use "revokeToken".
  • auth keys revoke: Replaced interactiveHelper.confirm() with promptForConfirmation() to match the pattern used by all other destructive commands.
  • Updated tests: all existing revoke-token tests now pass --force, added coverage for JSON mode guard and user cancellation flow.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cli-web-cli Ready Ready Preview, Comment Apr 30, 2026 0:11am

Request Review

@claude-code-ably-assistant
Copy link
Copy Markdown

Walkthrough

This PR closes a safety gap in auth revoke-token, which was the only hard-delete command missing the --force / interactive confirmation pattern used by every other destructive command. It also replaces the bespoke interactiveHelper.confirm() call in auth keys revoke with the shared promptForConfirmation() utility to standardise the prompting pattern across both revoke commands.

Changes

Area Files Summary
Commands src/commands/auth/revoke-token.ts Add --force flag, JSON-mode guard (errors without --force), interactive confirmation prompt before revocation, and updated examples
Commands src/commands/auth/keys/revoke.ts Swap interactiveHelper.confirm()promptForConfirmation() and align prompt wording
Tests test/unit/commands/auth/revoke-token.test.ts Add --force to all existing test invocations; add two new tests: JSON-mode guard and user-cancellation flow

Review Notes

  • Breaking change for scripted use: auth revoke-token now requires --force when --json is passed. Any existing scripts calling ably auth revoke-token TOKEN --json will now fail with an error envelope instead of silently revoking. This is intentional and consistent with the pattern on other destructive commands, but warrants a mention in release notes.
  • Interactive confirmation wording: revoke-token now shows a truncated token preview before asking for confirmation. Worth checking that the truncation (token.slice(0, 15) + "...") is sufficient to identify the token without leaking sensitive material.
  • Stdin patching in tests: The cancellation test directly mutates process.stdin to simulate user input. This is a side-effectful approach — confirm it doesn't leave state that could affect other tests in the suite (there is a restore at the end, but the queueMicrotask timing is worth scrutinising).
  • No new dependencies: uses existing promptForConfirmation utility and forceFlag — no package changes required.

claude-code-ably-assistant[bot]

This comment was marked as outdated.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 --force flag + interactive confirmation prompt to auth revoke-token, including a JSON-mode guard that requires --force.
  • Switched auth keys revoke confirmation from interactiveHelper.confirm() to promptForConfirmation() for consistency.
  • Updated/extended unit tests to pass --force where 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.

Comment thread src/commands/auth/revoke-token.ts Outdated
Comment thread test/unit/commands/auth/revoke-token.test.ts Outdated
@sacOO7 sacOO7 force-pushed the fix/revoke-token-with-confirmation-prompt branch from 0ab3a37 to 736ff38 Compare April 21, 2026 18:27
…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
@sacOO7 sacOO7 force-pushed the fix/revoke-token-with-confirmation-prompt branch from 736ff38 to e4c0028 Compare April 22, 2026 07:48
@sacOO7 sacOO7 requested review from AndyTWF and umair-ably April 22, 2026 07:51
@sacOO7
Copy link
Copy Markdown
Contributor Author

sacOO7 commented Apr 22, 2026

@claude review

Copy link
Copy Markdown

@claude-code-ably-assistant claude-code-ably-assistant Bot left a comment

Choose a reason for hiding this comment

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

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.

@AndyTWF
Copy link
Copy Markdown
Contributor

AndyTWF commented Apr 22, 2026

Looks like there's a couple of Claude comments so will hold off the review til those are addressed :)

@sacOO7 sacOO7 changed the title fix(auth): add --force flag to revoke-token, standardize keys revoke prompt [DX-1123] fix(auth): add --force flag to revoke-token, standardize keys revoke prompt Apr 22, 2026
@sacOO7
Copy link
Copy Markdown
Contributor Author

sacOO7 commented Apr 22, 2026

@claude review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment thread src/commands/auth/revoke-token.ts Outdated
Comment thread src/commands/auth/revoke-token.ts
claude-code-ably-assistant[bot]

This comment was marked as off-topic.

@sacOO7 sacOO7 changed the title [DX-1123] fix(auth): add --force flag to revoke-token, standardize keys revoke prompt [DX-1123] fix(auth): redesign revoke-token to match Ably revocation API semantics Apr 22, 2026
@sacOO7 sacOO7 force-pushed the fix/revoke-token-with-confirmation-prompt branch from ee013f5 to 855a69a Compare April 22, 2026 13:52
@sacOO7 sacOO7 force-pushed the fix/revoke-token-with-confirmation-prompt branch from 871d9be to 46a8e72 Compare April 22, 2026 15:14
@sacOO7
Copy link
Copy Markdown
Contributor Author

sacOO7 commented Apr 23, 2026

Two issues reported by claude are not real issues. Still I have addressed both. The first one Pre-confirmation context uses this.log() is already used by other commands like apps/delete, queues/delete etc

@sacOO7
Copy link
Copy Markdown
Contributor Author

sacOO7 commented Apr 23, 2026

Tested on the local, all flags working as expected 👍
Commands

  1. Issue token => pnpm cli auth issue-ably-token --capability '{"*":["*"]}'
  2. Subscribe to channel using token => ABLY_TOKEN="Issued Token" pnpm cli channels subscribe my-channel --pretty-json
  3. Revoke token => pnpm cli auth revoke-token --client-id "client-id-from-issued-token"

description: "Revoke all tokens issued to this client ID",
exclusive: ["revocation-key"],
}),
"revocation-key": Flags.string({
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.

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 ?

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 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.

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.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm fine with how this is done currently e.g.

  • Manual mandatory check (if (!clientId && !revocationKey)) instead of exactlyOne - 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 exclusive property

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:

  1. We don't add doc urls in any fail messages - this should be removed.
  2. "Either --client-id or --revocation-key must be provided" follows the language of other similar fail messages

Copy link
Copy Markdown
Contributor Author

@sacOO7 sacOO7 Apr 30, 2026

Choose a reason for hiding this comment

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

Fixed a3e41c2

Comment thread src/commands/auth/revoke-token.ts Outdated
}),

"client-id": Flags.string({
char: "c",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

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.

Comment thread src/commands/auth/revoke-token.ts Outdated
exclusive: ["revocation-key"],
}),
"revocation-key": Flags.string({
char: "r",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

given the client-id shorthand will be removed, i'd also remove this tbh

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.

Comment thread src/commands/auth/keys/revoke.ts Outdated
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?",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
"\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?",

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.

Comment thread src/commands/auth/revoke-token.ts Outdated
"allow-reauth-margin": Flags.boolean({
default: false,
description:
"[default: false] Delay enforcement by 30s so connected clients can obtain a new token before disconnection.",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

Suggested change
"[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.",

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.

@umair-ably
Copy link
Copy Markdown
Collaborator

can you ensure you run /ably-review locally... there's a couple of issues around swallowing error context and formatting it should pick up (Opus 4.7 xHigh Effort)

@sacOO7
Copy link
Copy Markdown
Contributor Author

sacOO7 commented Apr 30, 2026

can you ensure you run /ably-review locally... there's a couple of issues around swallowing error context and formatting it should pick up (Opus 4.7 xHigh Effort)

Ran /ably-review with Opus 4.7 xHigh Effort, found minor issues, fixed -> e20f8a4

@sacOO7 sacOO7 force-pushed the fix/revoke-token-with-confirmation-prompt branch from e20f8a4 to c46534f Compare April 30, 2026 12:10
@sacOO7 sacOO7 dismissed claude-code-ably-assistant[bot]’s stale review April 30, 2026 12:31

Out of date review, all changes are well addressed

@sacOO7 sacOO7 merged commit 80d8cf0 into main Apr 30, 2026
11 checks passed
@sacOO7 sacOO7 deleted the fix/revoke-token-with-confirmation-prompt branch April 30, 2026 12:31
@sacOO7 sacOO7 mentioned this pull request May 1, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants