fix: rehydrate Matrix DM verifications

This commit is contained in:
Gustavo Madeira Santana
2026-04-23 00:21:46 -04:00
parent 51f622473f
commit 0460e66b2e
4 changed files with 421 additions and 29 deletions

View File

@@ -388,6 +388,53 @@ describe("matrix CLI verification commands", () => {
);
});
it("prints DM lookup details in Matrix verification follow-up commands", async () => {
requestMatrixVerificationMock.mockResolvedValue(
mockMatrixVerificationSummary({
id: "dm-verify-1",
transactionId: "txn-dm",
roomId: "!room-'$(x):example.org",
otherUserId: "@alice:example.org",
isSelfVerification: false,
hasSas: false,
sas: undefined,
}),
);
const program = buildProgram();
await program.parseAsync(
[
"matrix",
"verify",
"request",
"--user-id",
"@alice:example.org",
"--room-id",
"!room-'$(x):example.org",
],
{ from: "user" },
);
expect(requestMatrixVerificationMock).toHaveBeenCalledWith({
accountId: "default",
cfg: {},
ownUser: undefined,
userId: "@alice:example.org",
deviceId: undefined,
roomId: "!room-'$(x):example.org",
});
expect(consoleLogMock).toHaveBeenCalledWith("Room id: !room-'$(x):example.org");
expect(consoleLogMock).toHaveBeenCalledWith(
"- Then run openclaw matrix verify start txn-dm --user-id @alice:example.org --room-id '!room-'\\''$(x):example.org' to start SAS verification.",
);
expect(consoleLogMock).toHaveBeenCalledWith(
"- Run openclaw matrix verify sas txn-dm --user-id @alice:example.org --room-id '!room-'\\''$(x):example.org' to display the SAS emoji or decimals.",
);
expect(consoleLogMock).toHaveBeenCalledWith(
"- When the SAS matches, run openclaw matrix verify confirm-sas txn-dm --user-id @alice:example.org --room-id '!room-'\\''$(x):example.org'.",
);
});
it("rejects ambiguous Matrix verification request targets", async () => {
const program = buildProgram();
@@ -529,6 +576,45 @@ describe("matrix CLI verification commands", () => {
);
});
it("passes DM lookup details through Matrix verification follow-up commands", async () => {
startMatrixVerificationMock.mockResolvedValue(
mockMatrixVerificationSummary({
id: "dm-verify-1",
transactionId: "txn-dm",
roomId: "!dm:example.org",
otherUserId: "@alice:example.org",
}),
);
const program = buildProgram();
await program.parseAsync(
[
"matrix",
"verify",
"start",
"txn-dm",
"--user-id",
"@alice:example.org",
"--room-id",
"!dm:example.org",
"--account",
"ops",
],
{ from: "user" },
);
expect(startMatrixVerificationMock).toHaveBeenCalledWith("txn-dm", {
accountId: "ops",
cfg: {},
method: "sas",
verificationDmUserId: "@alice:example.org",
verificationDmRoomId: "!dm:example.org",
});
expect(consoleLogMock).toHaveBeenCalledWith(
"- If they match, run openclaw matrix verify confirm-sas txn-dm --user-id @alice:example.org --room-id '!dm:example.org' --account ops.",
);
});
it("prints stable transaction ids in follow-up commands after accepting verification", async () => {
acceptMatrixVerificationMock.mockResolvedValue(
mockMatrixVerificationSummary({

View File

@@ -544,6 +544,8 @@ type MatrixCliVerificationStatus = {
type MatrixCliVerificationCommandOptions = {
account?: string;
userId?: string;
roomId?: string;
verbose?: boolean;
json?: boolean;
};
@@ -557,6 +559,7 @@ type MatrixCliSelfVerificationCommandOptions = {
type MatrixCliVerificationSummary = {
id: string;
transactionId?: string;
roomId?: string;
otherUserId: string;
otherDeviceId?: string;
isSelfVerification: boolean;
@@ -789,6 +792,9 @@ function printMatrixVerificationSummary(summary: MatrixCliVerificationSummary):
if (summary.transactionId) {
console.log(`Transaction id: ${sanitizeMatrixCliText(summary.transactionId)}`);
}
if (summary.roomId) {
console.log(`Room id: ${sanitizeMatrixCliText(summary.roomId)}`);
}
console.log(`Other user: ${sanitizeMatrixCliText(summary.otherUserId)}`);
console.log(`Other device: ${sanitizeMatrixCliText(summary.otherDeviceId ?? "unknown")}`);
console.log(`Self-verification: ${summary.isSelfVerification ? "yes" : "no"}`);
@@ -837,11 +843,87 @@ function printMatrixVerificationSas(sas: MatrixCliVerificationSas): void {
}
}
function printMatrixVerificationSasGuidance(requestId: string, accountId?: string): void {
function matrixCliVerificationDmLookupOptions(options: MatrixCliVerificationCommandOptions): {
verificationDmRoomId?: string;
verificationDmUserId?: string;
} {
const lookup: {
verificationDmRoomId?: string;
verificationDmUserId?: string;
} = {};
if (options.roomId !== undefined) {
lookup.verificationDmRoomId = options.roomId;
}
if (options.userId !== undefined) {
lookup.verificationDmUserId = options.userId;
}
return lookup;
}
function formatMatrixVerificationDmFollowupParts(params: {
roomId?: string;
userId?: string;
}): string[] {
if (!params.roomId || !params.userId) {
return [];
}
return [
"--user-id",
sanitizeMatrixCliText(params.userId),
"--room-id",
sanitizeMatrixCliText(params.roomId),
];
}
function formatMatrixVerificationSummaryDmFollowupParts(
summary: MatrixCliVerificationSummary,
): string[] {
return formatMatrixVerificationDmFollowupParts({
roomId: summary.roomId,
userId: summary.otherUserId,
});
}
function formatMatrixVerificationOptionsDmFollowupParts(
options: MatrixCliVerificationCommandOptions,
): string[] {
return formatMatrixVerificationDmFollowupParts({
roomId: options.roomId,
userId: options.userId,
});
}
function formatMatrixVerificationPreferredDmFollowupParts(
summary: MatrixCliVerificationSummary,
options: MatrixCliVerificationCommandOptions,
): string[] {
const summaryParts = formatMatrixVerificationSummaryDmFollowupParts(summary);
return summaryParts.length
? summaryParts
: formatMatrixVerificationOptionsDmFollowupParts(options);
}
function formatMatrixVerificationFollowupCommand(params: {
action: string;
requestId: string;
accountId?: string;
dmParts?: string[];
}): string {
return formatMatrixCliCommandParts(
["verify", params.action, params.requestId, ...(params.dmParts ?? [])],
params.accountId,
);
}
function printMatrixVerificationSasGuidance(
requestId: string,
accountId?: string,
dmParts: string[] = [],
): void {
printGuidance([
`Compare the emoji or decimals with the other Matrix client.`,
`If they match, run ${formatMatrixCliCommandParts(["verify", "confirm-sas", requestId], accountId)}.`,
`If they do not match, run ${formatMatrixCliCommandParts(["verify", "mismatch-sas", requestId], accountId)}.`,
`If they match, run ${formatMatrixVerificationFollowupCommand({ action: "confirm-sas", requestId, accountId, dmParts })}.`,
`If they do not match, run ${formatMatrixVerificationFollowupCommand({ action: "mismatch-sas", requestId, accountId, dmParts })}.`,
]);
}
@@ -863,12 +945,17 @@ async function promptMatrixVerificationSasMatch(): Promise<boolean> {
}
}
function printMatrixVerificationRequestGuidance(requestId: string, accountId?: string): void {
function printMatrixVerificationRequestGuidance(
summary: MatrixCliVerificationSummary,
accountId?: string,
): void {
const requestId = formatMatrixVerificationCommandId(summary);
const dmParts = formatMatrixVerificationSummaryDmFollowupParts(summary);
printGuidance([
`Accept the verification request in another Matrix client for this account.`,
`Then run ${formatMatrixCliCommandParts(["verify", "start", requestId], accountId)} to start SAS verification.`,
`Run ${formatMatrixCliCommandParts(["verify", "sas", requestId], accountId)} to display the SAS emoji or decimals.`,
`When the SAS matches, run ${formatMatrixCliCommandParts(["verify", "confirm-sas", requestId], accountId)}.`,
`Then run ${formatMatrixVerificationFollowupCommand({ action: "start", requestId, accountId, dmParts })} to start SAS verification.`,
`Run ${formatMatrixVerificationFollowupCommand({ action: "sas", requestId, accountId, dmParts })} to display the SAS emoji or decimals.`,
`When the SAS matches, run ${formatMatrixVerificationFollowupCommand({ action: "confirm-sas", requestId, accountId, dmParts })}.`,
]);
}
@@ -1357,10 +1444,7 @@ export function registerMatrixCli(params: { program: Command }): void {
onText: (summary) => {
printAccountLabel(accountId);
printMatrixVerificationSummary(summary);
printMatrixVerificationRequestGuidance(
formatMatrixVerificationCommandId(summary),
accountId,
);
printMatrixVerificationRequestGuidance(summary, accountId);
},
errorPrefix: "Verification request failed",
});
@@ -1371,15 +1455,24 @@ export function registerMatrixCli(params: { program: Command }): void {
.command("accept <id>")
.description("Accept an inbound Matrix verification request")
.option("--account <id>", "Account ID (for multi-account setups)")
.option("--user-id <id>", "Matrix user ID for DM verification follow-up")
.option("--room-id <id>", "Matrix direct-message room ID for verification follow-up")
.option("--verbose", "Show detailed diagnostics")
.option("--json", "Output as JSON")
.action(async (id: string, options: MatrixCliVerificationCommandOptions) => {
await runMatrixCliVerificationSummaryCommand({
options,
run: async (accountId, cfg) => await acceptMatrixVerification(id, { accountId, cfg }),
run: async (accountId, cfg) =>
await acceptMatrixVerification(id, {
accountId,
cfg,
...matrixCliVerificationDmLookupOptions(options),
}),
afterText: (summary, accountId) => {
const requestId = formatMatrixVerificationCommandId(summary);
const dmParts = formatMatrixVerificationPreferredDmFollowupParts(summary, options);
printGuidance([
`Run ${formatMatrixCliCommandParts(["verify", "start", formatMatrixVerificationCommandId(summary)], accountId)} to start SAS verification.`,
`Run ${formatMatrixVerificationFollowupCommand({ action: "start", requestId, accountId, dmParts })} to start SAS verification.`,
]);
},
errorPrefix: "Verification accept failed",
@@ -1390,15 +1483,26 @@ export function registerMatrixCli(params: { program: Command }): void {
.command("start <id>")
.description("Start SAS verification for a Matrix verification request")
.option("--account <id>", "Account ID (for multi-account setups)")
.option("--user-id <id>", "Matrix user ID for DM verification follow-up")
.option("--room-id <id>", "Matrix direct-message room ID for verification follow-up")
.option("--verbose", "Show detailed diagnostics")
.option("--json", "Output as JSON")
.action(async (id: string, options: MatrixCliVerificationCommandOptions) => {
await runMatrixCliVerificationSummaryCommand({
options,
run: async (accountId, cfg) =>
await startMatrixVerification(id, { accountId, cfg, method: "sas" }),
await startMatrixVerification(id, {
accountId,
cfg,
method: "sas",
...matrixCliVerificationDmLookupOptions(options),
}),
afterText: (summary, accountId) =>
printMatrixVerificationSasGuidance(formatMatrixVerificationCommandId(summary), accountId),
printMatrixVerificationSasGuidance(
formatMatrixVerificationCommandId(summary),
accountId,
formatMatrixVerificationPreferredDmFollowupParts(summary, options),
),
errorPrefix: "Verification start failed",
});
});
@@ -1407,6 +1511,8 @@ export function registerMatrixCli(params: { program: Command }): void {
.command("sas <id>")
.description("Show SAS emoji or decimals for a Matrix verification request")
.option("--account <id>", "Account ID (for multi-account setups)")
.option("--user-id <id>", "Matrix user ID for DM verification follow-up")
.option("--room-id <id>", "Matrix direct-message room ID for verification follow-up")
.option("--verbose", "Show detailed diagnostics")
.option("--json", "Output as JSON")
.action(async (id: string, options: MatrixCliVerificationCommandOptions) => {
@@ -1414,12 +1520,22 @@ export function registerMatrixCli(params: { program: Command }): void {
await runMatrixCliCommand({
verbose: options.verbose === true,
json: options.json === true,
run: async () => await getMatrixVerificationSas(id, { accountId, cfg }),
run: async () =>
await getMatrixVerificationSas(id, {
accountId,
cfg,
...matrixCliVerificationDmLookupOptions(options),
}),
onText: (sas) => {
const requestId = formatMatrixCliText(id);
printAccountLabel(accountId);
console.log(`Verification id: ${formatMatrixCliText(id)}`);
console.log(`Verification id: ${requestId}`);
printMatrixVerificationSas(sas);
printMatrixVerificationSasGuidance(id, accountId);
printMatrixVerificationSasGuidance(
requestId,
accountId,
formatMatrixVerificationOptionsDmFollowupParts(options),
);
},
errorPrefix: "Verification SAS lookup failed",
});
@@ -1429,12 +1545,19 @@ export function registerMatrixCli(params: { program: Command }): void {
.command("confirm-sas <id>")
.description("Confirm matching SAS emoji or decimals for a Matrix verification request")
.option("--account <id>", "Account ID (for multi-account setups)")
.option("--user-id <id>", "Matrix user ID for DM verification follow-up")
.option("--room-id <id>", "Matrix direct-message room ID for verification follow-up")
.option("--verbose", "Show detailed diagnostics")
.option("--json", "Output as JSON")
.action(async (id: string, options: MatrixCliVerificationCommandOptions) => {
await runMatrixCliVerificationSummaryCommand({
options,
run: async (accountId, cfg) => await confirmMatrixVerificationSas(id, { accountId, cfg }),
run: async (accountId, cfg) =>
await confirmMatrixVerificationSas(id, {
accountId,
cfg,
...matrixCliVerificationDmLookupOptions(options),
}),
errorPrefix: "Verification SAS confirm failed",
});
});
@@ -1443,12 +1566,19 @@ export function registerMatrixCli(params: { program: Command }): void {
.command("mismatch-sas <id>")
.description("Reject a Matrix SAS verification when the emoji or decimals do not match")
.option("--account <id>", "Account ID (for multi-account setups)")
.option("--user-id <id>", "Matrix user ID for DM verification follow-up")
.option("--room-id <id>", "Matrix direct-message room ID for verification follow-up")
.option("--verbose", "Show detailed diagnostics")
.option("--json", "Output as JSON")
.action(async (id: string, options: MatrixCliVerificationCommandOptions) => {
await runMatrixCliVerificationSummaryCommand({
options,
run: async (accountId, cfg) => await mismatchMatrixVerificationSas(id, { accountId, cfg }),
run: async (accountId, cfg) =>
await mismatchMatrixVerificationSas(id, {
accountId,
cfg,
...matrixCliVerificationDmLookupOptions(options),
}),
errorPrefix: "Verification SAS mismatch failed",
});
});
@@ -1457,6 +1587,8 @@ export function registerMatrixCli(params: { program: Command }): void {
.command("cancel <id>")
.description("Cancel a Matrix verification request")
.option("--account <id>", "Account ID (for multi-account setups)")
.option("--user-id <id>", "Matrix user ID for DM verification follow-up")
.option("--room-id <id>", "Matrix direct-message room ID for verification follow-up")
.option("--reason <text>", "Cancellation reason")
.option("--code <code>", "Matrix cancellation code")
.option("--verbose", "Show detailed diagnostics")
@@ -1477,6 +1609,7 @@ export function registerMatrixCli(params: { program: Command }): void {
cfg,
reason: options.reason,
code: options.code,
...matrixCliVerificationDmLookupOptions(options),
}),
errorPrefix: "Verification cancel failed",
});

View File

@@ -36,6 +36,7 @@ let getMatrixEncryptionStatus: typeof import("./verification.js").getMatrixEncry
let getMatrixRoomKeyBackupStatus: typeof import("./verification.js").getMatrixRoomKeyBackupStatus;
let getMatrixVerificationStatus: typeof import("./verification.js").getMatrixVerificationStatus;
let runMatrixSelfVerification: typeof import("./verification.js").runMatrixSelfVerification;
let startMatrixVerification: typeof import("./verification.js").startMatrixVerification;
describe("matrix verification actions", () => {
beforeAll(async () => {
@@ -45,6 +46,7 @@ describe("matrix verification actions", () => {
getMatrixVerificationStatus,
listMatrixVerifications,
runMatrixSelfVerification,
startMatrixVerification,
} = await import("./verification.js"));
});
@@ -250,6 +252,63 @@ describe("matrix verification actions", () => {
expect(withStartedActionClientMock).not.toHaveBeenCalled();
});
it("rehydrates DM verification requests before follow-up actions", async () => {
const tracked = {
completed: false,
hasSas: false,
id: "verification-1",
phaseName: "requested",
transactionId: "txn-dm",
};
const started = {
...tracked,
chosenMethod: "m.sas.v1",
phaseName: "started",
};
const crypto = {
ensureVerificationDmTracked: vi.fn(async () => tracked),
startVerification: vi.fn(async () => started),
};
withStartedActionClientMock.mockImplementation(async (_opts, run) => {
return await run({ crypto });
});
await expect(
startMatrixVerification("txn-dm", {
verificationDmRoomId: "!dm:example.org",
verificationDmUserId: "@alice:example.org",
}),
).resolves.toMatchObject({
id: "verification-1",
phaseName: "started",
});
expect(crypto.ensureVerificationDmTracked).toHaveBeenCalledWith({
roomId: "!dm:example.org",
userId: "@alice:example.org",
});
expect(crypto.startVerification).toHaveBeenCalledWith("txn-dm", "sas");
});
it("requires complete DM lookup details for verification follow-up actions", async () => {
const crypto = {
ensureVerificationDmTracked: vi.fn(),
startVerification: vi.fn(),
};
withStartedActionClientMock.mockImplementation(async (_opts, run) => {
return await run({ crypto });
});
await expect(
startMatrixVerification("txn-dm", {
verificationDmRoomId: "!dm:example.org",
}),
).rejects.toThrow("--user-id and --room-id must be provided together");
expect(crypto.ensureVerificationDmTracked).not.toHaveBeenCalled();
expect(crypto.startVerification).not.toHaveBeenCalled();
});
it("keeps self-verification in one started Matrix client session", async () => {
const requested = {
completed: false,
@@ -420,6 +479,47 @@ describe("matrix verification actions", () => {
expect(crypto.startVerification).not.toHaveBeenCalled();
});
it("fails immediately when an already-started self-verification uses a non-SAS method", async () => {
const requested = {
completed: false,
hasSas: false,
id: "verification-1",
phaseName: "requested",
transactionId: "tx-self",
};
const started = {
...requested,
chosenMethod: "m.reciprocate.v1",
phaseName: "started",
};
const cancelled = {
...started,
phaseName: "cancelled",
};
const crypto = {
cancelVerification: vi.fn(async () => cancelled),
listVerifications: vi.fn(async () => [started]),
requestVerification: vi.fn(async () => requested),
startVerification: vi.fn(),
};
withStartedActionClientMock.mockImplementation(async (_opts, run) => {
return await run({ crypto });
});
await expect(
runMatrixSelfVerification({ confirmSas: vi.fn(async () => true), timeoutMs: 500 }),
).rejects.toThrow(
"Matrix self-verification started without SAS while waiting to show SAS emoji or decimals (method: m.reciprocate.v1)",
);
expect(crypto.listVerifications).toHaveBeenCalledTimes(1);
expect(crypto.startVerification).not.toHaveBeenCalled();
expect(crypto.cancelVerification).toHaveBeenCalledWith("verification-1", {
code: "m.user",
reason: "OpenClaw self-verification did not complete",
});
});
it("finalizes completed non-SAS self-verification without waiting for SAS", async () => {
const completed = {
completed: true,

View File

@@ -12,6 +12,10 @@ const DEFAULT_MATRIX_SELF_VERIFICATION_TIMEOUT_MS = 180_000;
type MatrixCryptoActionFacade = NonNullable<import("../sdk.js").MatrixClient["crypto"]>;
type MatrixActionClient = import("../sdk.js").MatrixClient;
type MatrixVerificationDmLookupOpts = {
verificationDmRoomId?: string;
verificationDmUserId?: string;
};
export type MatrixSelfVerificationResult = MatrixVerificationSummary & {
deviceOwnerVerified: boolean;
@@ -42,6 +46,26 @@ function resolveVerificationId(input: string): string {
return normalized;
}
async function ensureMatrixVerificationDmTracked(
crypto: MatrixCryptoActionFacade,
opts: MatrixVerificationDmLookupOpts,
): Promise<void> {
const roomId = normalizeOptionalString(opts.verificationDmRoomId);
const userId = normalizeOptionalString(opts.verificationDmUserId);
if (Boolean(roomId) !== Boolean(userId)) {
throw new Error("--user-id and --room-id must be provided together for Matrix DM verification");
}
if (!roomId || !userId) {
return;
}
const tracked = await crypto.ensureVerificationDmTracked({ roomId, userId });
if (!tracked) {
throw new Error(
`Matrix DM verification request not found for room ${roomId} and user ${userId}`,
);
}
}
function isSameMatrixVerification(
left: MatrixVerificationSummary,
right: MatrixVerificationSummary,
@@ -69,12 +93,38 @@ function isMatrixVerificationCancelled(summary: MatrixVerificationSummary): bool
return summary.phaseName === "cancelled";
}
function isMatrixSasMethod(method: string | null | undefined): boolean {
return method === "m.sas.v1" || method === "sas";
}
function getMatrixVerificationSasWaitFailure(
summary: MatrixVerificationSummary,
label: string,
): string | null {
if (summary.hasSas || summary.phaseName === "cancelled") {
return null;
}
const method = summary.chosenMethod ? ` (method: ${summary.chosenMethod})` : "";
if (summary.completed) {
return `Matrix self-verification completed without SAS while waiting to ${label}${method}`;
}
if (
summary.phaseName === "started" &&
summary.chosenMethod &&
!isMatrixSasMethod(summary.chosenMethod)
) {
return `Matrix self-verification started without SAS while waiting to ${label}${method}`;
}
return null;
}
async function waitForMatrixVerificationSummary(params: {
crypto: MatrixCryptoActionFacade;
label: string;
request: MatrixVerificationSummary;
timeoutMs: number;
predicate: (summary: MatrixVerificationSummary) => boolean;
reject?: (summary: MatrixVerificationSummary) => string | null;
}): Promise<MatrixVerificationSummary> {
const startedAt = Date.now();
let last: MatrixVerificationSummary | undefined;
@@ -93,6 +143,10 @@ async function waitForMatrixVerificationSummary(params: {
}`,
);
}
const rejection = params.reject?.(found);
if (rejection) {
throw new Error(rejection);
}
}
await sleep(Math.min(250, Math.max(25, params.timeoutMs - (Date.now() - startedAt))));
}
@@ -246,12 +300,21 @@ export async function runMatrixSelfVerification(
: ready;
let sasSummary = started;
if (!sasSummary.hasSas) {
const sasFailure = getMatrixVerificationSasWaitFailure(
sasSummary,
"show SAS emoji or decimals",
);
if (sasFailure) {
throw new Error(sasFailure);
}
sasSummary = await waitForMatrixVerificationSummary({
crypto,
label: "show SAS emoji or decimals",
request: started,
timeoutMs,
predicate: (summary) => summary.hasSas,
reject: (summary) =>
getMatrixVerificationSasWaitFailure(summary, "show SAS emoji or decimals"),
});
}
if (!sasSummary.sas) {
@@ -289,20 +352,23 @@ export async function runMatrixSelfVerification(
export async function acceptMatrixVerification(
requestId: string,
opts: MatrixActionClientOpts = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.acceptVerification(resolveVerificationId(requestId));
});
}
export async function cancelMatrixVerification(
requestId: string,
opts: MatrixActionClientOpts & { reason?: string; code?: string } = {},
opts: MatrixActionClientOpts &
MatrixVerificationDmLookupOpts & { reason?: string; code?: string } = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.cancelVerification(resolveVerificationId(requestId), {
reason: normalizeOptionalString(opts.reason),
code: normalizeOptionalString(opts.code),
@@ -312,20 +378,22 @@ export async function cancelMatrixVerification(
export async function startMatrixVerification(
requestId: string,
opts: MatrixActionClientOpts & { method?: "sas" } = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts & { method?: "sas" } = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.startVerification(resolveVerificationId(requestId), opts.method ?? "sas");
});
}
export async function generateMatrixVerificationQr(
requestId: string,
opts: MatrixActionClientOpts = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.generateVerificationQr(resolveVerificationId(requestId));
});
}
@@ -333,10 +401,11 @@ export async function generateMatrixVerificationQr(
export async function scanMatrixVerificationQr(
requestId: string,
qrDataBase64: string,
opts: MatrixActionClientOpts = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
const payload = qrDataBase64.trim();
if (!payload) {
throw new Error("Matrix QR data is required");
@@ -347,40 +416,44 @@ export async function scanMatrixVerificationQr(
export async function getMatrixVerificationSas(
requestId: string,
opts: MatrixActionClientOpts = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.getVerificationSas(resolveVerificationId(requestId));
});
}
export async function confirmMatrixVerificationSas(
requestId: string,
opts: MatrixActionClientOpts = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.confirmVerificationSas(resolveVerificationId(requestId));
});
}
export async function mismatchMatrixVerificationSas(
requestId: string,
opts: MatrixActionClientOpts = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.mismatchVerificationSas(resolveVerificationId(requestId));
});
}
export async function confirmMatrixVerificationReciprocateQr(
requestId: string,
opts: MatrixActionClientOpts = {},
opts: MatrixActionClientOpts & MatrixVerificationDmLookupOpts = {},
) {
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
await ensureMatrixVerificationDmTracked(crypto, opts);
return await crypto.confirmVerificationReciprocateQr(resolveVerificationId(requestId));
});
}