Skip to content

Commit

Permalink
Revoke installations (#459)
Browse files Browse the repository at this point in the history
* remove client from all serializable objects

* fix up the example app

* add method for static inbox state

* add ability to revoke installtions

* write tests for it
  • Loading branch information
nplasterer authored Jan 14, 2025
1 parent 15b8400 commit 696dc30
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 11 deletions.
53 changes: 42 additions & 11 deletions Sources/XMTPiOS/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -314,14 +314,12 @@ public final class Client {
return inboxId
}

public static func canMessage(
accountAddresses: [String],
api: ClientOptions.Api
) async throws -> [String: Bool] {
let address = "0x0000000000000000000000000000000000000000"
private static func prepareClient(
api: ClientOptions.Api,
address: String = "0x0000000000000000000000000000000000000000"
) async throws -> FfiXmtpClient {
let inboxId = try await getOrCreateInboxId(api: api, address: address)

let ffiClient = try await LibXMTP.createClient(
return try await LibXMTP.createClient(
api: connectToApiBackend(api: api),
db: nil,
encryptionKey: nil,
Expand All @@ -331,11 +329,25 @@ public final class Client {
legacySignedPrivateKeyProto: nil,
historySyncUrl: nil
)
}

let result = try await ffiClient.canMessage(
public static func canMessage(
accountAddresses: [String],
api: ClientOptions.Api
) async throws -> [String: Bool] {
let ffiClient = try await prepareClient(api: api)
return try await ffiClient.canMessage(
accountAddresses: accountAddresses)
}

return result
public static func inboxStatesForInboxIds(
inboxIds: [String],
api: ClientOptions.Api
) async throws -> [InboxState] {
let ffiClient = try await prepareClient(api: api)
let result = try await ffiClient.addressesFromInboxId(
refreshFromNetwork: true, inboxIds: inboxIds)
return result.map { InboxState(ffiInboxState: $0) }
}

init(
Expand Down Expand Up @@ -396,6 +408,23 @@ public final class Client {
}
}

public func revokeInstallations(
signingKey: SigningKey, installationIds: [String]
) async throws {
let installations = installationIds.map { $0.hexToData }
let signatureRequest = try await ffiClient.revokeInstallations(
installationIds: installations)
do {
try await Client.handleSignature(
for: signatureRequest, signingKey: signingKey)
try await ffiClient.applySignatureRequest(
signatureRequest: signatureRequest)
} catch {
throw ClientError.creationError(
"Failed to sign the message: \(error.localizedDescription)")
}
}

public func canMessage(address: String) async throws -> Bool {
let canMessage = try await ffiClient.canMessage(accountAddresses: [
address
Expand Down Expand Up @@ -462,7 +491,8 @@ public final class Client {
do {
return Group(
ffiGroup: try ffiClient.conversation(
conversationId: groupId.hexToData), clientInboxId: self.inboxID)
conversationId: groupId.hexToData),
clientInboxId: self.inboxID)
} catch {
return nil
}
Expand Down Expand Up @@ -507,7 +537,8 @@ public final class Client {
do {
let conversation = try ffiClient.dmConversation(
targetInboxId: inboxId)
return Dm(ffiConversation: conversation, clientInboxId: self.inboxID)
return Dm(
ffiConversation: conversation, clientInboxId: self.inboxID)
} catch {
return nil
}
Expand Down
61 changes: 61 additions & 0 deletions Tests/XMTPTests/ClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,27 @@ class ClientTests: XCTestCase {
"Failed for address: \(address)")
}
}

func testStaticInboxState() async throws {
let fixtures = try await fixtures()

let inboxStates = try await Client.inboxStatesForInboxIds(
inboxIds: [
fixtures.alixClient.inboxID,
fixtures.boClient.inboxID,
],
api: ClientOptions.Api(env: .local, isSecure: false)
)

XCTAssertEqual(
inboxStates.first!.recoveryAddress.lowercased(),
fixtures.alixClient.address.lowercased()
)
XCTAssertEqual(
inboxStates.last!.recoveryAddress.lowercased(),
fixtures.boClient.address.lowercased()
)
}

func testCanDeleteDatabase() async throws {
let key = try Crypto.secureRandomBytes(count: 32)
Expand Down Expand Up @@ -300,6 +321,46 @@ class ClientTests: XCTestCase {

XCTAssertEqual(alixClient2.inboxID, alixClient.inboxID)
}

func testRevokeInstallations() async throws {
let key = try Crypto.secureRandomBytes(count: 32)
let alix = try PrivateKey.generate()

let alixClient = try await Client.create(
account: alix,
options: ClientOptions.init(
api: .init(env: .local, isSecure: false),
dbEncryptionKey: key
)
)

let alixClient2 = try await Client.create(
account: alix,
options: ClientOptions.init(
api: .init(env: .local, isSecure: false),
dbEncryptionKey: key,
dbDirectory: "xmtp_db1"
)
)

let alixClient3 = try await Client.create(
account: alix,
options: ClientOptions.init(
api: .init(env: .local, isSecure: false),
dbEncryptionKey: key,
dbDirectory: "xmtp_db2"
)
)

let state = try await alixClient3.inboxState(refreshFromNetwork: true)
XCTAssertEqual(state.installations.count, 3)

try await alixClient3.revokeInstallations(signingKey: alix, installationIds: [alixClient2.installationID])

let newState = try await alixClient3.inboxState(
refreshFromNetwork: true)
XCTAssertEqual(newState.installations.count, 2)
}

func testRevokesAllOtherInstallations() async throws {
let key = try Crypto.secureRandomBytes(count: 32)
Expand Down

0 comments on commit 696dc30

Please sign in to comment.