Skip to content

Commit

Permalink
Add support for revoking specific installations (#770)
Browse files Browse the repository at this point in the history
* Upgrade node and WASM bindings

* Update Node SDK

* Update Browser SDK

* Add changeset
  • Loading branch information
rygine authored Jan 7, 2025
1 parent da12be0 commit d09ec27
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 87 deletions.
6 changes: 6 additions & 0 deletions .changeset/angry-poems-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@xmtp/browser-sdk": patch
"@xmtp/node-sdk": patch
---

Add support for revoking specific installations
2 changes: 1 addition & 1 deletion sdks/browser-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@xmtp/content-type-primitives": "^2.0.0",
"@xmtp/content-type-text": "^2.0.0",
"@xmtp/proto": "^3.72.3",
"@xmtp/wasm-bindings": "^0.0.9",
"@xmtp/wasm-bindings": "^0.0.11",
"uuid": "^11.0.3"
},
"devDependencies": {
Expand Down
37 changes: 33 additions & 4 deletions sdks/browser-sdk/src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,17 @@ export class Client extends ClientWorkerClass {
return this.sendMessage("removeAccountSignatureText", { accountAddress });
}

async #revokeInstallationsSignatureText() {
return this.sendMessage("revokeInstallationsSignatureText", undefined);
async #revokeAllOtherInstallationsSignatureText() {
return this.sendMessage(
"revokeAllOtherInstallationsSignatureText",
undefined,
);
}

async #revokeInstallationsSignatureText(installationIds: Uint8Array[]) {
return this.sendMessage("revokeInstallationsSignatureText", {
installationIds,
});
}

async #addSignature(
Expand Down Expand Up @@ -208,8 +217,28 @@ export class Client extends ClientWorkerClass {
await this.#applySignatures();
}

async revokeInstallations() {
const signatureText = await this.#revokeInstallationsSignatureText();
async revokeAllOtherInstallations() {
const signatureText =
await this.#revokeAllOtherInstallationsSignatureText();

if (!signatureText) {
throw new Error(
"Unable to generate revoke all other installations signature text",
);
}

await this.#addSignature(
SignatureRequestType.RevokeInstallations,
signatureText,
this.#signer,
);

await this.#applySignatures();
}

async revokeInstallations(installationIds: Uint8Array[]) {
const signatureText =
await this.#revokeInstallationsSignatureText(installationIds);

if (!signatureText) {
throw new Error("Unable to generate revoke installations signature text");
Expand Down
14 changes: 12 additions & 2 deletions sdks/browser-sdk/src/WorkerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,19 @@ export class WorkerClient {
}
}

async revokeInstallationsSignatureText() {
async revokeAllAOtherInstallationsSignatureText() {
try {
return await this.#client.revokeInstallationsSignatureText();
return await this.#client.revokeAllOtherInstallationsSignatureText();
} catch {
return undefined;
}
}

async revokeInstallationsSignatureText(installationIds: Uint8Array[]) {
try {
return await this.#client.revokeInstallationsSignatureText(
installationIds,
);
} catch {
return undefined;
}
Expand Down
20 changes: 9 additions & 11 deletions sdks/browser-sdk/src/WorkerConversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,26 @@ export class WorkerConversations {
}
}

async list(options?: SafeListConversationsOptions) {
const groups = (await this.#conversations.list(
list(options?: SafeListConversationsOptions) {
const groups = this.#conversations.list(
options ? fromSafeListConversationsOptions(options) : undefined,
)) as Conversation[];
) as Conversation[];
return groups.map((group) => new WorkerConversation(this.#client, group));
}

async listGroups(
listGroups(
options?: Omit<SafeListConversationsOptions, "conversation_type">,
) {
const groups = (await this.#conversations.listGroups(
const groups = this.#conversations.listGroups(
options ? fromSafeListConversationsOptions(options) : undefined,
)) as Conversation[];
) as Conversation[];
return groups.map((group) => new WorkerConversation(this.#client, group));
}

async listDms(
options?: Omit<SafeListConversationsOptions, "conversation_type">,
) {
const groups = (await this.#conversations.listDms(
listDms(options?: Omit<SafeListConversationsOptions, "conversation_type">) {
const groups = this.#conversations.listDms(
options ? fromSafeListConversationsOptions(options) : undefined,
)) as Conversation[];
) as Conversation[];
return groups.map((group) => new WorkerConversation(this.#client, group));
}

Expand Down
10 changes: 9 additions & 1 deletion sdks/browser-sdk/src/types/clientEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,19 @@ export type ClientEvents =
};
}
| {
action: "revokeInstallationsSignatureText";
action: "revokeAllOtherInstallationsSignatureText";
id: string;
result: string | undefined;
data: undefined;
}
| {
action: "revokeInstallationsSignatureText";
id: string;
result: string | undefined;
data: {
installationIds: Uint8Array[];
};
}
| {
action: "addSignature";
id: string;
Expand Down
26 changes: 15 additions & 11 deletions sdks/browser-sdk/src/workers/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,19 @@ self.onmessage = async (event: MessageEvent<ClientEventsClientMessageData>) => {
});
break;
}
case "revokeAllOtherInstallationsSignatureText": {
const result = await client.revokeAllAOtherInstallationsSignatureText();
postMessage({
id,
action,
result,
});
break;
}
case "revokeInstallationsSignatureText": {
const result = await client.revokeInstallationsSignatureText();
const result = await client.revokeInstallationsSignatureText(
data.installationIds,
);
postMessage({
id,
action,
Expand Down Expand Up @@ -255,7 +266,7 @@ self.onmessage = async (event: MessageEvent<ClientEventsClientMessageData>) => {
* Conversations actions
*/
case "getConversations": {
const conversations = await client.conversations.list(data.options);
const conversations = client.conversations.list(data.options);
postMessage({
id,
action,
Expand All @@ -268,9 +279,7 @@ self.onmessage = async (event: MessageEvent<ClientEventsClientMessageData>) => {
break;
}
case "getGroups": {
const conversations = await client.conversations.listGroups(
data.options,
);
const conversations = client.conversations.listGroups(data.options);
postMessage({
id,
action,
Expand All @@ -283,7 +292,7 @@ self.onmessage = async (event: MessageEvent<ClientEventsClientMessageData>) => {
break;
}
case "getDms": {
const conversations = await client.conversations.listDms(data.options);
const conversations = client.conversations.listDms(data.options);
postMessage({
id,
action,
Expand All @@ -296,11 +305,6 @@ self.onmessage = async (event: MessageEvent<ClientEventsClientMessageData>) => {
break;
}
case "newGroup": {
// console.log(
// "newGroup",
// fromSafeCreateGroupOptions(data.options!),
// data.options,
// );
const conversation = await client.conversations.newGroup(
data.accountAddresses,
data.options,
Expand Down
33 changes: 31 additions & 2 deletions sdks/browser-sdk/test/Client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ describe.concurrent("Client", () => {
]);
});

it("should revoke all installations", async () => {
it("should revoke all other installations", async () => {
const user = createUser();

const client = await createRegisteredClient(user);
Expand All @@ -126,14 +126,43 @@ describe.concurrent("Client", () => {
expect(installationIds).toContain(client2.installationId);
expect(installationIds).toContain(client3.installationId);

await client3.revokeInstallations();
await client3.revokeAllOtherInstallations();

const inboxState2 = await client3.inboxState(true);

expect(inboxState2.installations.length).toBe(1);
expect(inboxState2.installations[0].id).toBe(client3.installationId);
});

it("should revoke specific installations", async () => {
const user = createUser();

const client = await createRegisteredClient(user);
user.uuid = v4();
const client2 = await createRegisteredClient(user);
user.uuid = v4();
const client3 = await createRegisteredClient(user);

const inboxState = await client3.inboxState(true);
expect(inboxState.installations.length).toBe(3);

const installationIds = inboxState.installations.map((i) => i.id);
expect(installationIds).toContain(client.installationId);
expect(installationIds).toContain(client2.installationId);
expect(installationIds).toContain(client3.installationId);

await client3.revokeInstallations([client.installationIdBytes!]);

const inboxState2 = await client3.inboxState(true);

expect(inboxState2.installations.length).toBe(2);

const installationIds2 = inboxState2.installations.map((i) => i.id);
expect(installationIds2).toContain(client2.installationId);
expect(installationIds2).toContain(client3.installationId);
expect(installationIds2).not.toContain(client.installationId);
});

it("should manage consent states", async () => {
const user1 = createUser();
const user2 = createUser();
Expand Down
2 changes: 1 addition & 1 deletion sdks/node-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@xmtp/content-type-group-updated": "^2.0.0",
"@xmtp/content-type-primitives": "^2.0.0",
"@xmtp/content-type-text": "^2.0.0",
"@xmtp/node-bindings": "^0.0.31",
"@xmtp/node-bindings": "^0.0.33",
"@xmtp/proto": "^3.72.3"
},
"devDependencies": {
Expand Down
40 changes: 36 additions & 4 deletions sdks/node-sdk/src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,22 @@ export class Client {
}
}

async #revokeInstallationsSignatureText() {
async #revokeAllOtherInstallationsSignatureText() {
try {
const signatureText =
await this.#innerClient.revokeInstallationsSignatureText();
await this.#innerClient.revokeAllOtherInstallationsSignatureText();
return signatureText;
} catch {
return null;
}
}

async #revokeInstallationsSignatureText(installationIds: Uint8Array[]) {
try {
const signatureText =
await this.#innerClient.revokeInstallationsSignatureText(
installationIds,
);
return signatureText;
} catch {
return null;
Expand Down Expand Up @@ -288,8 +300,28 @@ export class Client {
await this.#applySignatures();
}

async revokeInstallations() {
const signatureText = await this.#revokeInstallationsSignatureText();
async revokeAllOtherInstallations() {
const signatureText =
await this.#revokeAllOtherInstallationsSignatureText();

if (!signatureText) {
throw new Error(
"Unable to generate revoke all other installations signature text",
);
}

await this.#addSignature(
SignatureRequestType.RevokeInstallations,
signatureText,
this.#signer,
);

await this.#applySignatures();
}

async revokeInstallations(installationIds: Uint8Array[]) {
const signatureText =
await this.#revokeInstallationsSignatureText(installationIds);

if (!signatureText) {
throw new Error("Unable to generate revoke installations signature text");
Expand Down
14 changes: 6 additions & 8 deletions sdks/node-sdk/src/Conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,24 @@ export class Conversations {
return conversation;
}

async list(options?: ListConversationsOptions) {
const groups = await this.#conversations.list(options);
list(options?: ListConversationsOptions) {
const groups = this.#conversations.list(options);
return groups.map((group) => {
const conversation = new Conversation(this.#client, group);
return conversation;
});
}

async listGroups(
options?: Omit<ListConversationsOptions, "conversationType">,
) {
const groups = await this.#conversations.listGroups(options);
listGroups(options?: Omit<ListConversationsOptions, "conversationType">) {
const groups = this.#conversations.listGroups(options);
return groups.map((group) => {
const conversation = new Conversation(this.#client, group);
return conversation;
});
}

async listDms(options?: Omit<ListConversationsOptions, "conversationType">) {
const groups = await this.#conversations.listDms(options);
listDms(options?: Omit<ListConversationsOptions, "conversationType">) {
const groups = this.#conversations.listDms(options);
return groups.map((group) => {
const conversation = new Conversation(this.#client, group);
return conversation;
Expand Down
Loading

0 comments on commit d09ec27

Please sign in to comment.