diff --git a/Bitkit.xcodeproj/project.pbxproj b/Bitkit.xcodeproj/project.pbxproj index 7ca9acdc6..b2fd14bbb 100644 --- a/Bitkit.xcodeproj/project.pbxproj +++ b/Bitkit.xcodeproj/project.pbxproj @@ -1175,7 +1175,7 @@ repositoryURL = "https://github.com/pubky/paykit-rs"; requirement = { kind = exactVersion; - version = "0.1.0-rc39"; + version = "0.1.0-rc42"; }; }; 18D65DFE2EB9649F00252335 /* XCRemoteSwiftPackageReference "vss-rust-client-ffi" */ = { diff --git a/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0c3fa0aef..30374f26a 100644 --- a/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pubky/paykit-rs", "state" : { - "revision" : "2fa056570bd5f93c166e43cf16c5356f6407cbbd", - "version" : "0.1.0-rc39" + "revision" : "192fc700897c3792ae4af221591572f3472089e1", + "version" : "0.1.0-rc42" } }, { diff --git a/Bitkit/Constants/Env.swift b/Bitkit/Constants/Env.swift index 54d037750..fe723acc0 100644 --- a/Bitkit/Constants/Env.swift +++ b/Bitkit/Constants/Env.swift @@ -71,7 +71,7 @@ enum Env { (infoPlistValue("E2E_BACKEND") ?? "local").lowercased() } - private static var isLocalE2EBackend: Bool { + static var isLocalE2EBackend: Bool { isE2E && e2eBackend == "local" } diff --git a/Bitkit/Info.plist b/Bitkit/Info.plist index 5ae0d535c..e7804ec2b 100644 --- a/Bitkit/Info.plist +++ b/Bitkit/Info.plist @@ -21,6 +21,8 @@ E2E_BACKEND $(E2E_BACKEND) + E2E_HOMEGATE_URL + $(E2E_HOMEGATE_URL) E2E_NETWORK $(E2E_NETWORK) TREZOR_BRIDGE diff --git a/Bitkit/Models/PubkyAuthRequest.swift b/Bitkit/Models/PubkyAuthRequest.swift index aaca814e5..23efee576 100644 --- a/Bitkit/Models/PubkyAuthRequest.swift +++ b/Bitkit/Models/PubkyAuthRequest.swift @@ -5,7 +5,13 @@ enum PubkyAuthClaim: String, Equatable { case watchOnlyAccountV1 = "watch-only-account-v1" static let queryParameter = "x-bitkit-claim" - static let watchOnlyAccountCapabilities = "/pub/paykit/v0/bitkit/server/:rw" + static let watchOnlyAccountCapabilities = "/pub/paykit/v0/bitkit/server/:rw,/pub/paykit/v0/private/bitkit/server/:rw" + private static let watchOnlyAccountCapabilitySet = Set(watchOnlyAccountCapabilities.split(separator: ",").map(String.init)) + + static func matchesWatchOnlyAccountCapabilities(_ capabilities: String) -> Bool { + let requestedCapabilitySet = Set(capabilities.split(separator: ",").map(String.init)) + return requestedCapabilitySet == watchOnlyAccountCapabilitySet + } } enum PubkyAuthRequestError: Error, Equatable { @@ -79,7 +85,7 @@ struct PubkyAuthRequest { throw PubkyAuthRequestError.duplicateBitkitClaim } guard let claimValue = claimValues.first else { - if capabilities == PubkyAuthClaim.watchOnlyAccountCapabilities { + if PubkyAuthClaim.matchesWatchOnlyAccountCapabilities(capabilities) { throw PubkyAuthRequestError.missingBitkitClaim } return nil @@ -87,7 +93,7 @@ struct PubkyAuthRequest { guard let claim = PubkyAuthClaim(rawValue: claimValue) else { throw PubkyAuthRequestError.unsupportedBitkitClaim(claimValue) } - guard capabilities == PubkyAuthClaim.watchOnlyAccountCapabilities else { + guard PubkyAuthClaim.matchesWatchOnlyAccountCapabilities(capabilities) else { throw PubkyAuthRequestError.invalidBitkitClaimCapabilities } diff --git a/Bitkit/Services/PubkyService.swift b/Bitkit/Services/PubkyService.swift index 3ad14422c..5ce423b8b 100644 --- a/Bitkit/Services/PubkyService.swift +++ b/Bitkit/Services/PubkyService.swift @@ -789,11 +789,12 @@ actor PaykitSdkService { return sdk } - let created = try PaykitSdk.withPaymentAdapter( + let created = try PaykitSdk.withPaymentAdapterAndPubkyClientConfig( stateStore: stateStore, sessionProvider: sessionProvider, paymentAdapter: paymentAdapter, - config: Self.config() + config: Self.config(), + pubkyClient: Self.pubkyClientConfig() ) sdk = created return created @@ -925,7 +926,15 @@ actor PaykitSdkService { } private func bootstrap() throws -> PubkySessionBootstrap { - try PubkySessionBootstrap() + try PubkySessionBootstrap.withPubkyClientConfig(pubkyClient: Self.pubkyClientConfig()) + } + + nonisolated static func pubkyClientConfig(isLocalE2EBackend: Bool = Env.isLocalE2EBackend) -> PubkyClientConfig { + var config = Paykit.defaultPubkyClientConfig() + if isLocalE2EBackend { + config.localTestnetHost = "localhost" + } + return config } private nonisolated static func config() throws -> PaykitSdkConfig { diff --git a/BitkitTests/PaykitSdkClientConfigTests.swift b/BitkitTests/PaykitSdkClientConfigTests.swift new file mode 100644 index 000000000..2384b4108 --- /dev/null +++ b/BitkitTests/PaykitSdkClientConfigTests.swift @@ -0,0 +1,17 @@ +@testable import Bitkit +import Paykit +import XCTest + +final class PaykitSdkClientConfigTests: XCTestCase { + func testProductionUsesDefaultPubkyClient() { + let config = PaykitSdkService.pubkyClientConfig(isLocalE2EBackend: false) + + XCTAssertNil(config.localTestnetHost) + } + + func testLocalE2EUsesLocalPubkyTestnet() { + let config = PaykitSdkService.pubkyClientConfig(isLocalE2EBackend: true) + + XCTAssertEqual(config.localTestnetHost, "localhost") + } +} diff --git a/BitkitTests/PubkyAuthRequestTests.swift b/BitkitTests/PubkyAuthRequestTests.swift index 0539982f5..a45b423ff 100644 --- a/BitkitTests/PubkyAuthRequestTests.swift +++ b/BitkitTests/PubkyAuthRequestTests.swift @@ -33,6 +33,18 @@ final class PubkyAuthRequestTests: XCTestCase { XCTAssertEqual(request.bitkitClaim, .watchOnlyAccountV1) } + func testParseUrlRecognizesWatchOnlyAccountClaimWithReorderedCapabilities() throws { + let capabilities = PubkyAuthClaim.watchOnlyAccountCapabilities + .split(separator: ",") + .reversed() + .joined(separator: ",") + let url = authUrl(capabilities: capabilities, claimValues: [PubkyAuthClaim.watchOnlyAccountV1.rawValue]) + + let request = try PubkyAuthRequest.parse(url: url) + + XCTAssertEqual(request.bitkitClaim, .watchOnlyAccountV1) + } + func testParseUrlWithoutBitkitClaimPreservesNormalAuth() throws { let request = try PubkyAuthRequest.parse(url: authUrl(capabilities: "/pub/bitkit.to/:rw")) @@ -74,6 +86,15 @@ final class PubkyAuthRequestTests: XCTestCase { } } + func testParseUrlRejectsWatchOnlyClaimWithoutPrivateCapability() { + let capabilities = "/pub/paykit/v0/bitkit/server/:rw" + let url = authUrl(capabilities: capabilities, claimValues: [PubkyAuthClaim.watchOnlyAccountV1.rawValue]) + + XCTAssertThrowsError(try PubkyAuthRequest.parse(url: url)) { + XCTAssertEqual($0 as? PubkyAuthRequestError, .invalidBitkitClaimCapabilities) + } + } + // MARK: - parseCapabilities func testParseCapabilitiesSingleEntry() { diff --git a/changelog.d/next/653.fixed.md b/changelog.d/next/653.fixed.md new file mode 100644 index 000000000..5e47caa71 --- /dev/null +++ b/changelog.d/next/653.fixed.md @@ -0,0 +1 @@ +Fixed Paykit Server authorization to require both public and private payment capabilities.