Skip to content

Commit

Permalink
Implement new Operation Endpoint Claim and extend WMTOperations with …
Browse files Browse the repository at this point in the history
…claim function. Rename TOTPParser for PACParser
  • Loading branch information
Hopsaheysa committed Nov 24, 2023
1 parent 5ab04f7 commit 0e865ad
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 108 deletions.
23 changes: 23 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"pins" : [
{
"identity" : "networking-apple",
"kind" : "remoteSourceControl",
"location" : "https://github.com/wultra/networking-apple.git",
"state" : {
"revision" : "b7ebe23f441e614d13bd6bc803d658e66767e31a",
"version" : "1.2.0"
}
},
{
"identity" : "powerauth-mobile-sdk-spm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/wultra/powerauth-mobile-sdk-spm.git",
"state" : {
"revision" : "095be6adfc057501a7cb9a7351697a1b6ed9e64b",
"version" : "1.7.8"
}
}
],
"version" : 2
}
14 changes: 9 additions & 5 deletions WultraMobileTokenSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 52;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -71,8 +71,9 @@
EA6DDF0F29F8036B0011E234 /* WMTPreApprovalScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6DDF0E29F8036B0011E234 /* WMTPreApprovalScreen.swift */; };
EA6DDF1A29F804D60011E234 /* WMTPostApprovalScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6DDF1929F804D60011E234 /* WMTPostApprovalScreen.swift */; };
EA6DDF1C29F807230011E234 /* OperationUIDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6DDF1B29F807230011E234 /* OperationUIDataTests.swift */; };
EA7A6E582B0E639800C1D4F4 /* WMTClaimData.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7A6E572B0E639800C1D4F4 /* WMTClaimData.swift */; };
EA9CE2BE2AEAA9FD00FE4E35 /* WMTProximityCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9CE2BD2AEAA9FD00FE4E35 /* WMTProximityCheck.swift */; };
EA9CE2C22AEBDB0D00FE4E35 /* WMTTOTPUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9CE2C12AEBDB0D00FE4E35 /* WMTTOTPUtils.swift */; };
EA9CE2C22AEBDB0D00FE4E35 /* WMTPACUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9CE2C12AEBDB0D00FE4E35 /* WMTPACUtils.swift */; };
EAB7054A2AF1161500756AC2 /* TOTPParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB705492AF1161500756AC2 /* TOTPParserTests.swift */; };
EACAF7B02A126B7D0021CA54 /* WMTJsonValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACAF7AF2A126B7D0021CA54 /* WMTJsonValue.swift */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -157,8 +158,9 @@
EA6DDF0E29F8036B0011E234 /* WMTPreApprovalScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPreApprovalScreen.swift; sourceTree = "<group>"; };
EA6DDF1929F804D60011E234 /* WMTPostApprovalScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPostApprovalScreen.swift; sourceTree = "<group>"; };
EA6DDF1B29F807230011E234 /* OperationUIDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationUIDataTests.swift; sourceTree = "<group>"; };
EA7A6E572B0E639800C1D4F4 /* WMTClaimData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTClaimData.swift; sourceTree = "<group>"; };
EA9CE2BD2AEAA9FD00FE4E35 /* WMTProximityCheck.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTProximityCheck.swift; sourceTree = "<group>"; };
EA9CE2C12AEBDB0D00FE4E35 /* WMTTOTPUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTTOTPUtils.swift; sourceTree = "<group>"; };
EA9CE2C12AEBDB0D00FE4E35 /* WMTPACUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTPACUtils.swift; sourceTree = "<group>"; };
EAB705492AF1161500756AC2 /* TOTPParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TOTPParserTests.swift; sourceTree = "<group>"; };
EACAF7AF2A126B7D0021CA54 /* WMTJsonValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTJsonValue.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -295,7 +297,7 @@
DC6E52D4259C959900FC25BE /* Utils */ = {
isa = PBXGroup;
children = (
EA9CE2C12AEBDB0D00FE4E35 /* WMTTOTPUtils.swift */,
EA9CE2C12AEBDB0D00FE4E35 /* WMTPACUtils.swift */,
DC6E52D5259C964600FC25BE /* WMTOperationExpirationWatcher.swift */,
EACAF7AF2A126B7D0021CA54 /* WMTJsonValue.swift */,
);
Expand Down Expand Up @@ -433,6 +435,7 @@
children = (
DCC5CCD7244DBBBD004679AC /* WMTAuthorizationData.swift */,
DCC5CCD9244DBBE2004679AC /* WMTRejectionData.swift */,
EA7A6E572B0E639800C1D4F4 /* WMTClaimData.swift */,
);
path = Requests;
sourceTree = "<group>";
Expand Down Expand Up @@ -626,7 +629,7 @@
BFEEB2092937A2680047941D /* WMTInboxGetList.swift in Sources */,
BFEEB20729379F960047941D /* WMTInboxSetMessageRead.swift in Sources */,
EA44366A29F9294600DDEC1C /* WMTPostApprovaScreenReview.swift in Sources */,
EA9CE2C22AEBDB0D00FE4E35 /* WMTTOTPUtils.swift in Sources */,
EA9CE2C22AEBDB0D00FE4E35 /* WMTPACUtils.swift in Sources */,
EA294F3D29F6A07A00A0494E /* WMTOperationUIData.swift in Sources */,
DCC5CCB32449F8CD004679AC /* WMTOperationAttribute.swift in Sources */,
DCC5CCAC2449F765004679AC /* WMTOperationsImpl.swift in Sources */,
Expand All @@ -650,6 +653,7 @@
EA44366E29F9298100DDEC1C /* WMTPostApprovaScreenGeneric.swift in Sources */,
DC48803D292282FF00DB844B /* WMTInbox.swift in Sources */,
DC488042292282FF00DB844B /* WMTInboxImpl.swift in Sources */,
EA7A6E582B0E639800C1D4F4 /* WMTClaimData.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
29 changes: 29 additions & 0 deletions WultraMobileTokenSDK/Operations/Model/Requests/WMTClaimData.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Copyright 2023 Wultra s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions
// and limitations under the License.
//


import Foundation

/// Claim payload
class WMTClaimData: Codable {

/// Operation Id
let operationId: String

init(operationId: String) {
self.operationId = operationId
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ enum WMTOperationEndpoints {
typealias EndpointType = WPNEndpointSigned<WPNRequest<WMTRejectionData>, WPNResponseBase>
static let endpoint: EndpointType = WPNEndpointSigned(endpointURLPath: "/api/auth/token/app/operation/cancel", uriId: "/operation/cancel")
}

enum Claim {
typealias EndpointType = WPNEndpointBasic<WPNRequest<WMTClaimData>, WPNResponse<WMTUserOperation>>
static let endpoint: EndpointType = WPNEndpointBasic(endpointURLPath: "api/auth/token/app/operation/detail/claim")
}
}
21 changes: 21 additions & 0 deletions WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,27 @@ class WMTOperationsImpl<T: WMTUserOperation>: WMTOperations, WMTService {
}
}

func claim(operationId: String, completion: @escaping(Result<WMTUserOperation, WMTError>) -> Void) -> Operation? {

guard validateActivation(completion) else {
return nil
}

let claimData = WMTClaimData(operationId: operationId)

return networking.post(data: .init(claimData), to: WMTOperationEndpoints.Claim.endpoint) { response, error in
self.processResult(response: response, error: error) { result in
switch result {
case .success(let operation):
self.operationsRegister.replace(with: [operation])
completion(.success(operation))
case .failure(let err):
completion(.failure(self.adjustOperationError(err, auth: false)))
}
}
}
}

func authorize(operation: WMTOperation, with authentication: PowerAuthAuthentication, completion: @escaping (Result<Void, WMTError>) -> Void) -> Operation? {

guard validateActivation(completion) else {
Expand Down
63 changes: 63 additions & 0 deletions WultraMobileTokenSDK/Operations/Utils/WMTPACUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// Copyright 2023 Wultra s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions
// and limitations under the License.
//

import Foundation

/// Utility class used for handling Proximity Antifraud Check
public class WMTPACUtils {

/// Method accepts deeplink URL and returns PAC data
public static func parseDeeplink(url: URL) -> WMTPACData? {

guard let components = URLComponents(string: url.absoluteString) else {
D.error("Failed to get URLComponents: URLString is malformed")
return nil
}

guard let queryItems = components.queryItems else {
D.error("Failed to get URLComponents queryItems")
return nil
}

if let operationId = queryItems.first(where: { $0.name == "oid" })?.value {
let totp = queryItems.first(where: { $0.name == "totp" })?.value
return WMTPACData(operationId: operationId, totp: totp)
} else {
D.error("Failed to get operationId from query items")
return nil
}
}

/// Method accepts scanned code as a String and returns PAC data
public static func parseQRCode(code: String) -> WMTPACData? {
if let encodedURLString = code.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedURLString) {
return parseDeeplink(url: url)
} else {
D.error("Failed to created URL from QR code String.")
return nil
}
}
}

/// Data which is return after parsing PAC code
public struct WMTPACData {

/// The ID of the operation associated with the PAC
public let operationId: String

/// Time-based one time password used for Proximity antifraud check
public let totp: String?
}
91 changes: 0 additions & 91 deletions WultraMobileTokenSDK/Operations/Utils/WMTTOTPUtils.swift

This file was deleted.

9 changes: 9 additions & 0 deletions WultraMobileTokenSDK/Operations/WMTOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ public protocol WMTOperations: AnyObject {
@discardableResult
func getHistory(authentication: PowerAuthAuthentication, completion: @escaping(Result<[WMTOperationHistoryEntry], WMTError>) -> Void) -> Operation?

/// Claims the "anonymous" operation to be assigned to the user
/// - Parameters:
/// - operationId: Operation ID which will be claimed to belong to the user
/// - completion: Result completion.
/// This completion is always called on the main thread.
/// - Returns: Operation object for its state observation.
@discardableResult
func claim(operationId: String, completion: @escaping(Result<WMTUserOperation, WMTError>) -> Void) -> Operation?

/// Authorize operation with given PowerAuth authentication object.
///
/// - Parameters:
Expand Down
24 changes: 12 additions & 12 deletions WultraMobileTokenSDKTests/TOTPParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,38 @@ final class TOTPParserTest: XCTestCase {
func testQRTOTPParserWithEmptyCode() {
let code = ""

XCTAssertNil(WMTTOTPUtils.parseQRCode(code: code))
XCTAssertNil(WMTPACUtils.parseQRCode(code: code))
}

func testQRTOTPParserWithShortCode() {
let code = "abc"

XCTAssertNil(WMTTOTPUtils.parseQRCode(code: code))
XCTAssertNil(WMTPACUtils.parseQRCode(code: code))
}

func testQRTOTPParserWithValidCode() {
let code = "eyJhbGciOiJub25lIiwidHlwZSI6IkpXVCJ9.eyJvaWQiOiI2YTFjYjAwNy1mZjc1LTRmNDAtYTIxYi0wYjU0NmYwZjZjYWQiLCJ0b3RwIjoiNzM3NDMxOTQifQ=="
let code = "mtoken://login?oid=6a1cb007-ff75-4f40-a21b-0b546f0f6cad&totp=73743194"

XCTAssertEqual(WMTTOTPUtils.parseQRCode(code: code)?.totp, "73743194", "Parsing of totp failed")
XCTAssertEqual(WMTTOTPUtils.parseQRCode(code: code)?.operationId, "6a1cb007-ff75-4f40-a21b-0b546f0f6cad", "Parsing of operationId failed")
XCTAssertEqual(WMTPACUtils.parseQRCode(code: code)?.totp, "73743194", "Parsing of totp failed")
XCTAssertEqual(WMTPACUtils.parseQRCode(code: code)?.operationId, "6a1cb007-ff75-4f40-a21b-0b546f0f6cad", "Parsing of operationId failed")
}


func testDeeplinkTOTPParserWithInvalidURL() {
let url = URL(string: "mtoken://an-invalid-url.com")!
XCTAssertNil(WMTTOTPUtils.parseDeeplink(url: url))
XCTAssertNil(WMTPACUtils.parseDeeplink(url: url))
}

func testDeeplinkTOTPParserWithInvalidJWTCode() {
func testDeeplinkParserWithInvalidPACCode() {
let url = URL(string: "mtoken://login?code=abc")!

XCTAssertNil(WMTTOTPUtils.parseDeeplink(url: url))
XCTAssertNil(WMTPACUtils.parseDeeplink(url: url))
}

func testDeeplinkTOTPParserWithValidJWTCode() {
let url = URL(string: "mtoken://login?code=eyJhbGciOiJub25lIiwidHlwZSI6IkpXVCJ9.eyJvaWQiOiJkZjYxMjhmYy1jYTUxLTQ0YjctYmVmYS1jYTBlMTQwOGFhNjMiLCJ0b3RwIjoiNTY3MjU0OTQifQ==")!
func testDeeplinkParserWithValidPACCode() {
let url = URL(string: "mtoken://login?oid=df6128fc-ca51-44b7-befa-ca0e1408aa63&totp=56725494")!

XCTAssertEqual(WMTTOTPUtils.parseDeeplink(url: url)?.totp, "56725494", "Parsing of totp failed")
XCTAssertEqual(WMTTOTPUtils.parseDeeplink(url: url)?.operationId, "df6128fc-ca51-44b7-befa-ca0e1408aa63", "Parsing of operationId failed")
XCTAssertEqual(WMTPACUtils.parseDeeplink(url: url)?.totp, "56725494", "Parsing of totp failed")
XCTAssertEqual(WMTPACUtils.parseDeeplink(url: url)?.operationId, "df6128fc-ca51-44b7-befa-ca0e1408aa63", "Parsing of operationId failed")
}
}

0 comments on commit 0e865ad

Please sign in to comment.