// // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation public struct DeviceProvisioningTokenId { public let id: String fileprivate init(id: String) { self.id = id } } public struct DeviceProvisioningCodeResponse: Decodable { /// An opaque token to send to a new linked device that authorizes the /// new device to link itself to the account that requested this token. public var verificationCode: String /// An opaque identifier for the generated token that the caller may use /// to watch for a new device to complete the linking process. var tokenIdentifier: String public var tokenId: DeviceProvisioningTokenId { .init(id: tokenIdentifier) } } public protocol DeviceProvisioningService { func requestDeviceProvisioningCode() async throws -> DeviceProvisioningCodeResponse func provisionDevice(messageBody: Data, ephemeralDeviceId: String) async throws } public class DeviceProvisioningServiceImpl: DeviceProvisioningService { private let networkManager: any NetworkManagerProtocol public init(networkManager: any NetworkManagerProtocol) { self.networkManager = networkManager } public func requestDeviceProvisioningCode() async throws -> DeviceProvisioningCodeResponse { do { let request = OWSRequestFactory.deviceProvisioningCode() let httpResponse = try await networkManager.asyncRequest(request) guard let httpResponseData = httpResponse.responseBodyData else { throw OWSAssertionError("Missing responseBodyData.") } let response = try JSONDecoder().decode(DeviceProvisioningCodeResponse.self, from: httpResponseData) guard response.verificationCode.nilIfEmpty != nil else { throw OWSAssertionError("Empty verificationCode.") } return response } catch { throw DeviceLimitExceededError(error) ?? error } } public func provisionDevice(messageBody: Data, ephemeralDeviceId: String) async throws { let request = OWSRequestFactory.provisionDevice( withMessageBody: messageBody, ephemeralDeviceId: ephemeralDeviceId, ) do { _ = try await networkManager.asyncRequest(request) } catch { owsFailDebugUnlessNetworkFailure(error) throw error } } }