fix(ui): replace the key detail URL entry when a virtual key is rotated (#39471)

Regenerating a key repointed ?key= at the rotated hash with a pushed history
entry, so pressing the browser Back button landed on the hash that had just
been revoked. /key/info answers 404 for it and the page shows "Key not found
in database".

The rotated hash now replaces the current entry instead of pushing a new one,
so Back from a just-regenerated key returns to the key list. Opening a key
from the table still pushes, so Back from a normally opened key is unchanged.
This commit is contained in:
yuneng-jiang 2026-09-03 09:47:45 -07:00 committed by GitHub
parent 4990f06acc
commit 342e4470c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 5 additions and 1 deletions

View file

@ -182,6 +182,8 @@ const lastSearchParam = (onUrlUpdate: Mock<OnUrlUpdateFunction>, name: string) =
const lastKeyParam = (onUrlUpdate: Mock<OnUrlUpdateFunction>) => lastSearchParam(onUrlUpdate, "key");
const lastHistoryMode = (onUrlUpdate: Mock<OnUrlUpdateFunction>) => onUrlUpdate.mock.calls.at(-1)?.[0].options.history;
beforeEach(() => {
vi.clearAllMocks();
@ -377,6 +379,7 @@ it("clicking the key cell deep-links via ?key=", async () => {
await waitFor(() => {
expect(lastKeyParam(onUrlUpdate)).toBe(mockKey.token);
});
expect(lastHistoryMode(onUrlUpdate)).toBe("push");
});
it("renders KeyInfoView when the URL has ?key= for a key on the current page, without refetching it", async () => {
@ -417,6 +420,7 @@ it("repoints ?key= to the rotated hash once the regenerate dialog is dismissed",
await waitFor(() => {
expect(lastKeyParam(onUrlUpdate)).toBe("rotated-hash-456");
});
expect(lastHistoryMode(onUrlUpdate)).toBe("replace");
});
it("fetches the key by id when the URL has ?key= for a key not in the loaded page", async () => {

View file

@ -217,7 +217,7 @@ export function VirtualKeysTable({ headerActions }: VirtualKeysTableProps) {
(updated: Partial<KeyResponse>) => {
const rotatedToken = updated.token ?? updated.token_id;
if (!rotatedToken || rotatedToken === selectedKeyId) return;
void setSelectedKeyId(rotatedToken);
void setSelectedKeyId(rotatedToken, { history: "replace" });
void refetch();
},
[refetch, selectedKeyId, setSelectedKeyId],