Skip to content

Commit

Permalink
Finished implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
NachoSoto committed Jul 7, 2023
1 parent 863cc50 commit d2eb42b
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 17 deletions.
9 changes: 6 additions & 3 deletions Sources/Networking/Operations/LogInOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ private extension LogInOperation {
completion: IdentityAPI.LogInResponseHandler) {
let result: Result<(info: CustomerInfo, created: Bool), BackendError> = result
.map { response in
(response.body, created: response.statusCode == .createdSuccess)
(
response.body.copy(with: response.verificationResult),
created: response.statusCode == .createdSuccess
)
}
.mapError(BackendError.networkError)

Expand All @@ -94,11 +97,12 @@ private extension LogInOperation {
}
}

private extension LogInOperation {
extension LogInOperation {

struct Body: Encodable {

// These need to be explicit for `contentForSignature`
// swiftlint:disable:next nesting
fileprivate enum CodingKeys: String, CodingKey {
case appUserID = "app_user_id"
case newAppUserID = "new_app_user_id"
Expand All @@ -113,7 +117,6 @@ private extension LogInOperation {

extension LogInOperation.Body: HTTPRequestBody {

// TODO: verify these in snapshot tests
var contentForSignature: [(key: String, value: String)] {
return [
(Self.CodingKeys.appUserID.stringValue, self.appUserID),
Expand Down
72 changes: 66 additions & 6 deletions Tests/UnitTests/Networking/Backend/BackendLoginTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ import XCTest

@testable import RevenueCat

class BackendLoginTests: BaseBackendTests {
class BaseBackendLoginTests: BaseBackendTests {

override func createClient() -> MockHTTPClient {
super.createClient(#file)
}

}

class BackendLoginTests: BaseBackendLoginTests {

func testLoginMakesRightCalls() {
let newAppUserID = "new id"
let currentAppUserID = "old id"
Expand Down Expand Up @@ -170,13 +174,69 @@ class BackendLoginTests: BaseBackendTests {

}

private extension BackendLoginTests {
// swiftlint:disable:next type_name
class BackendLoginWithSignatureVerificationTests: BaseBackendLoginTests {

func mockLoginRequest(appUserID: String,
statusCode: HTTPStatusCode = .success,
response: [String: Any] = [:]) -> HTTPRequest.Path {
override var verificationMode: Configuration.EntitlementVerificationMode { .informational }

func testLoginWithVerifiedResponse() {
let newAppUserID = "new id"
let currentAppUserID = "old id"

_ = self.mockLoginRequest(appUserID: currentAppUserID,
statusCode: .createdSuccess,
response: Self.mockCustomerInfoData,
verificationResult: .verified)

let result = waitUntilValue { completed in
self.identity.logIn(currentAppUserID: currentAppUserID,
newAppUserID: newAppUserID,
completion: completed)
}

expect(result).to(beSuccess())

if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.2, *) {
expect(result?.value?.info.entitlements.verification) == .verified
}
}

func testLoginWithFailedVerification() {
let newAppUserID = "F72BF276-CD70-4C27-BCD2-FC1EFD988FA3"
let currentAppUserID = "$RCAnonymousID:6b2787de2fb848a8b403a45f695ee74f"

_ = self.mockLoginRequest(appUserID: currentAppUserID,
statusCode: .createdSuccess,
response: Self.mockCustomerInfoData,
verificationResult: .failed)

let result = waitUntilValue { completed in
self.identity.logIn(currentAppUserID: currentAppUserID,
newAppUserID: newAppUserID,
completion: completed)
}

expect(result).to(beSuccess())

if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.2, *) {
expect(result?.value?.info.entitlements.verification) == .failed
}
}

}

private extension BaseBackendLoginTests {

func mockLoginRequest(
appUserID: String,
statusCode: HTTPStatusCode = .success,
response: [String: Any] = [:],
verificationResult: VerificationResult = .notRequested
) -> HTTPRequest.Path {
let path: HTTPRequest.Path = .logIn
let response = MockHTTPClient.Response(statusCode: statusCode, response: response)
let response = MockHTTPClient.Response(statusCode: statusCode,
response: response,
verificationResult: verificationResult)

self.httpClient.mock(requestPath: path, response: response)

Expand Down
19 changes: 11 additions & 8 deletions Tests/UnitTests/Security/SigningTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -472,36 +472,39 @@ class SigningTests: TestCase {
}

func testVerifyKnownSignatureForPostRequest() throws {
// swiftlint:disable line_length
/*
Signature retrieved with:
curl -v 'https://api.revenuecat.com/v1/subscribers/identify' \
-X POST \
-H 'X-Nonce: MTIzNDU2Nzg5MGFi' \
-H 'X-Post-Params-Hash: current_user_id,new_user_id:sha256:633e3581dc391a2ef66e4b2b39c9d63e4ab82e801d8f17436774e65ae2a513e9' \
-H 'X-Post-Params-Hash: app_user_id,new_app_user_id:sha256:6fa58b9e3bdb1ca187ac082d128c19f04da8711fe6b17873a48bc7ca37bbf95a' \
-H 'Authorization: Bearer appl_fFVBVAoYujMZJnepIziGKVjnZBz' \
-H 'Content-Type: application/json' \
--data-raw '{"new_app_user_id":"F72BF276-CD70-4C27-BCD2-FC1EFD988FA3","app_user_id":"$RCAnonymousID:6b2787de2fb848a8b403a45f695ee74f"}'
*/

// swiftlint:disable line_length
let response = """
{"request_date":"2023-07-06T19:25:15Z","request_date_ms":1688671515638,"subscriber":{"entitlements":{},"first_seen":"2023-06-30T23:06:23Z","last_seen":"2023-06-30T23:06:23Z","management_url":null,"non_subscriptions":{},"original_app_user_id":"$RCAnonymousID:1af512a3b9c848899fe427f39dd69f2b","original_application_version":null,"original_purchase_date":null,"other_purchases":{},"subscriptions":{}}}\n
{"request_date":"2023-07-07T19:47:59Z","request_date_ms":1688759279804,"subscriber":{"entitlements":{},"first_seen":"2023-07-06T19:51:18Z","last_seen":"2023-07-06T19:51:18Z","management_url":null,"non_subscriptions":{},"original_app_user_id":"F72BF276-CD70-4C27-BCD2-FC1EFD988FA3","original_application_version":null,"original_purchase_date":null,"other_purchases":{},"subscriptions":{}}}\n
"""
let expectedSignature = "XX8Mh8DTcqPC5A48nncRU3hDkL/v3baxxqLIWnWJzg1tTAAA7ok0iXupT2bjju/BSHVmgxc0XiwTZXBmsGuWEXa9lsyoFi9HMF4aAIOs4Y+lYE2i4USJCP7ev07QZk7D2b6ZBSrxh7Tsw8z/B0jfCUIVOlzAJqMSoDWL3zy1etinl/pU/xzwZ9HdZWwyAgn38I9rv/JM0FSCcYMC2C8KE06wFyQTz+7c9btj/v2ueXRgAJYB"
let expectedSignature = "XX8Mh8DTcqPC5A48nncRU3hDkL/v3baxxqLIWnWJzg1tTAAA7ok0iXupT2bjju/BSHVmgxc0XiwTZXBmsGuWEXa9lsyoFi9HMF4aAIOs4Y+lYE2i4USJCP7ev07QZk7D2b6ZBYArl3A6DzmFY4Yh9CLUnG6RHMuVDFHmhOd4I6L10UiJUyO/vH9prON6j9E0bOyPdq5Cv+5/cQg2f2dA4NKPCFcZ9Ursc6O9c/HQ+qoVfX8H"
// swiftlint:enable line_length

let nonce = try XCTUnwrap(Data(base64Encoded: "MTIzNDU2Nzg5MGFi"))
let requestDate: UInt64 = 1688671515638
let etag = "a896a69e4b31304d"
let requestDate: UInt64 = 1688759279805

expect(
self.signing.verify(
signature: expectedSignature,
with: .init(
path: .getCustomerInfo(appUserID: "$RCAnonymousID:1af512a3b9c848899fe427f39dd69f2b"),
path: .logIn,
message: response.asData,
requestBody: LogInOperation.Body(
appUserID: "$RCAnonymousID:6b2787de2fb848a8b403a45f695ee74f",
newAppUserID: "F72BF276-CD70-4C27-BCD2-FC1EFD988FA3"
),
nonce: nonce,
etag: etag,
etag: nil,
requestDate: requestDate
),
publicKey: Signing.loadPublicKey()
Expand Down

0 comments on commit d2eb42b

Please sign in to comment.