Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support of QR Code & Deeplink - Proximity check #122

Merged
merged 26 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
737cdaa
Added support of QR Code & Deeplink - Proximity check
Hopsaheysa Oct 25, 2023
5223bdc
Return incorrectly removed init for backward compatibility
Hopsaheysa Oct 27, 2023
ded75b2
Rename otp to totp
Hopsaheysa Oct 27, 2023
9ce0f4c
Fix networking serialization tests
Hopsaheysa Oct 27, 2023
3428508
Add offline totp
Hopsaheysa Oct 27, 2023
a5489a3
Test totp authorization
Hopsaheysa Oct 27, 2023
916845c
Fix lint and missing file
Hopsaheysa Oct 27, 2023
b7a909e
Implement remarks
Hopsaheysa Oct 31, 2023
11897f2
Add `TOTPParserTests`
Hopsaheysa Oct 31, 2023
f25cf71
Minor fixes
Hopsaheysa Oct 31, 2023
5a63912
Fix incorrect name of otp in `WMTProximityCheckData`
Hopsaheysa Nov 2, 2023
82e752a
Bump networking dependency to 1.2.0.
Hopsaheysa Nov 7, 2023
89e91ef
Remove Package.swift from repo
Hopsaheysa Nov 8, 2023
e4bf607
Implement remarks
Hopsaheysa Nov 8, 2023
b6a4c03
Remove unused host in TOTPUtils
Hopsaheysa Nov 8, 2023
8b61768
Comment changed
Hopsaheysa Nov 8, 2023
60ae1bd
Bump Networking version to 1.2.0.
Hopsaheysa Nov 8, 2023
48d3114
Increase minimal ios version to 12 + Fix comment on WMTOperationTOTPData
Hopsaheysa Nov 9, 2023
5d33384
Minor naming changes
Hopsaheysa Nov 9, 2023
bd1699d
Remove `Packege.resolved` add `Package`
Hopsaheysa Nov 10, 2023
5fda458
Remove duplicated empty lines
Hopsaheysa Nov 10, 2023
0ef03b0
Update podspec
Hopsaheysa Nov 10, 2023
de3bf05
Add docs
Hopsaheysa Nov 10, 2023
0af2e57
Fix placement of the TOTP WMPTProximityCheck
Hopsaheysa Nov 10, 2023
d6ec093
Remove unnecessary info from docs
Hopsaheysa Nov 10, 2023
fd8b80e
Add info to PowerAuth compatibility table in SDK-Integration.md
Hopsaheysa Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "wultra/networking-apple" "1.1.7"
github "wultra/networking-apple" "1.2.0"
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
binary "https://raw.githubusercontent.com/wultra/powerauth-mobile-sdk-spm/1.7.6/PowerAuth2.json" "1.7.6"
binary "https://raw.githubusercontent.com/wultra/powerauth-mobile-sdk-spm/1.7.6/PowerAuthCore.json" "1.7.6"
github "wultra/networking-apple" "1.1.7"
github "wultra/networking-apple" "1.2.0"
6 changes: 3 additions & 3 deletions Deploy/WultraMobileTokenSDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/wultra/mtoken-sdk-ios.git', :tag => s.version }
# Deployment targets
s.swift_version = '5.7'
s.ios.deployment_target = '11.0'
s.ios.deployment_target = '12.0'

# Sources
s.default_subspec = 'Operations'

# 'Common' subspec
s.subspec 'Common' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Common/**/*.swift'
sub.dependency 'PowerAuth2', '>= 1.7.3'
sub.dependency 'WultraPowerAuthNetworking', '>= 1.1.7'
sub.dependency 'PowerAuth2', '~> 1.7.3'
sub.dependency 'WultraPowerAuthNetworking', '~> 1.2.0'
Hopsaheysa marked this conversation as resolved.
Show resolved Hide resolved
end

# 'Operations' subspec
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import PackageDescription
let package = Package(
name: "WultraMobileTokenSDK",
platforms: [
.iOS(.v11)
.iOS(.v12)
],
products: [
.library(name: "WultraMobileTokenSDK", targets: ["WultraMobileTokenSDK"])
],
dependencies: [
.package(url: "https://github.com/wultra/powerauth-mobile-sdk-spm.git", .upToNextMinor(from: "1.7.8")),
.package(url: "https://github.com/wultra/networking-apple.git", .upToNextMinor(from: "1.1.7"))
.package(url: "https://github.com/wultra/networking-apple.git", .upToNextMinor(from: "1.2.0"))
],
targets: [
.target(
Expand Down
6 changes: 3 additions & 3 deletions WultraMobileTokenSDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/wultra/mtoken-sdk-ios.git', :tag => s.version }
# Deployment targets
s.swift_version = '5.7'
s.ios.deployment_target = '11.0'
s.ios.deployment_target = '12.0'

# Sources
s.default_subspec = 'Operations'

# 'Common' subspec
s.subspec 'Common' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Common/**/*.swift'
sub.dependency 'PowerAuth2', '>= 1.7.3'
sub.dependency 'WultraPowerAuthNetworking', '>= 1.1.7'
sub.dependency 'PowerAuth2', '~> 1.7.3'
sub.dependency 'WultraPowerAuthNetworking', '~> 1.2.0'
end

# 'Operations' subspec
Expand Down
14 changes: 13 additions & 1 deletion WultraMobileTokenSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -71,6 +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 */; };
EA9CE2BE2AEAA9FD00FE4E35 /* WMTProximityCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9CE2BD2AEAA9FD00FE4E35 /* WMTProximityCheck.swift */; };
EA9CE2C22AEBDB0D00FE4E35 /* WMTTOTPUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9CE2C12AEBDB0D00FE4E35 /* WMTTOTPUtils.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 @@ -154,6 +157,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>"; };
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>"; };
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 @@ -223,6 +229,7 @@
DC8CB205244DD007009DDAA3 /* WMTAllowedOperationSignature.swift */,
DCE5EAAF26BD81150061861A /* WMTOperationHistoryEntry.swift */,
EA294F3C29F6A07A00A0494E /* WMTOperationUIData.swift */,
EA9CE2BD2AEAA9FD00FE4E35 /* WMTProximityCheck.swift */,
);
path = UserOperation;
sourceTree = "<group>";
Expand Down Expand Up @@ -270,6 +277,7 @@
DC395C0924E55B9B0007C36E /* PushParserTests.swift */,
DC6EDB7825A49ED900A229E4 /* OperationExpirationTests.swift */,
DC616235248508F8000DED17 /* QROperationParserTests.swift */,
EAB705492AF1161500756AC2 /* TOTPParserTests.swift */,
);
path = WultraMobileTokenSDKTests;
sourceTree = "<group>";
Expand All @@ -287,6 +295,7 @@
DC6E52D4259C959900FC25BE /* Utils */ = {
isa = PBXGroup;
children = (
EA9CE2C12AEBDB0D00FE4E35 /* WMTTOTPUtils.swift */,
DC6E52D5259C964600FC25BE /* WMTOperationExpirationWatcher.swift */,
EACAF7AF2A126B7D0021CA54 /* WMTJsonValue.swift */,
);
Expand Down Expand Up @@ -576,6 +585,7 @@
DC61624224852B6D000DED17 /* NetworkingObjectsTests.swift in Sources */,
DC395C0A24E55B9B0007C36E /* PushParserTests.swift in Sources */,
DC6EDB7925A49ED900A229E4 /* OperationExpirationTests.swift in Sources */,
EAB7054A2AF1161500756AC2 /* TOTPParserTests.swift in Sources */,
DC616236248508F8000DED17 /* QROperationParserTests.swift in Sources */,
EA6DDF1C29F807230011E234 /* OperationUIDataTests.swift in Sources */,
DCE660D124CEBECA00870E53 /* IntegrationTests.swift in Sources */,
Expand Down Expand Up @@ -616,6 +626,7 @@
BFEEB2092937A2680047941D /* WMTInboxGetList.swift in Sources */,
BFEEB20729379F960047941D /* WMTInboxSetMessageRead.swift in Sources */,
EA44366A29F9294600DDEC1C /* WMTPostApprovaScreenReview.swift in Sources */,
EA9CE2C22AEBDB0D00FE4E35 /* WMTTOTPUtils.swift in Sources */,
EA294F3D29F6A07A00A0494E /* WMTOperationUIData.swift in Sources */,
DCC5CCB32449F8CD004679AC /* WMTOperationAttribute.swift in Sources */,
DCC5CCAC2449F765004679AC /* WMTOperationsImpl.swift in Sources */,
Expand All @@ -632,6 +643,7 @@
DCAB7BCA24580BAC0006989D /* WMTQROperation.swift in Sources */,
DCC5CCBF2449F981004679AC /* WMTOperationAttributePartyInfo.swift in Sources */,
DC81D1CB244F451E00F80CD6 /* WMTPushImpl.swift in Sources */,
EA9CE2BE2AEAA9FD00FE4E35 /* WMTProximityCheck.swift in Sources */,
DC488041292282FF00DB844B /* WMTInboxEndpoints.swift in Sources */,
BF53DFC82971905600829814 /* WMTInboxContentType.swift in Sources */,
DCA43C6D2993F63E0059A163 /* WMTOperationAttributeImage.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion WultraMobileTokenSDK/ConfigFiles/Config.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off

// SDK
SDKROOT = iphoneos
IPHONEOS_DEPLOYMENT_TARGET = 10.0
IPHONEOS_DEPLOYMENT_TARGET = 12.0

// FRAMEWORKS
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)/Carthage/Build/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,45 @@ class WMTAuthorizationData: Codable {
/// Operation id
let id: String

init(operationId: String, operationData: String) {
/// Proximity OTP data
let proximityCheck: WMTProximityCheckData?

init(operationId: String, operationData: String, proximityCheck: WMTProximityCheckData? = nil) {
self.id = operationId
self.data = operationData
self.id = operationId
self.proximityCheck = proximityCheck
}

init(operation: WMTOperation, timestampSigned: Date = Date()) {
self.id = operation.id
self.data = operation.data

guard let proximityCheck = operation.proximityCheck else {
self.proximityCheck = nil
return
}

self.proximityCheck = WMTProximityCheckData(
otp: proximityCheck.totp,
type: proximityCheck.type,
timestampRequested: proximityCheck.timestampRequested,
timestampSigned: timestampSigned
)
}
}

/// Internal proximity check data used for authorization
struct WMTProximityCheckData: Codable {

/// Tha actual OTP code
let otp: String

/// Type of the Proximity check
let type: WMTProximityCheckType

/// Timestamp when the operation was delivered to the app
let timestampRequested: Date

/// Timestamp when the operation was signed
let timestampSigned: Date
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// 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

/// Object which is used to hold data about proximity check
///
/// Data shall be assigned to the operation when obtained
public class WMTProximityCheck: Codable {

/// Tha actual Time-based one time password
public let totp: String

/// Type of the Proximity check
public let type: WMTProximityCheckType

/// Timestamp when the operation was scanned (qrCode) or delivered to the device (deeplink)
public let timestampRequested: Date

public init(totp: String, type: WMTProximityCheckType, timestampRequested: Date = Date()) {
self.totp = totp
self.type = type
self.timestampRequested = timestampRequested
}
}

/// Types of possible Proximity Checks
public enum WMTProximityCheckType: String, Codable {
case qrCode = "QR_CODE"
case deeplink = "DEEPLINK"
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,7 @@ open class WMTUserOperation: WMTOperation, Codable {
///
/// Additional UI data such as Pre-Approval Screen or Post-Approval Screen should be presented.
public let ui: WMTOperationUIData?

/// Proximity Check Data to be passed when OTP is handed to the app
public var proximityCheck: WMTProximityCheck?
}
8 changes: 8 additions & 0 deletions WultraMobileTokenSDK/Operations/Model/WMTOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ public protocol WMTOperation {

/// Data for signing
var data: String { get }

/// Additional information with proximity check data
var proximityCheck: WMTProximityCheck? { get }
}

/// WMTOperation extension which sets proximityCheck to be nil for backwards compatibility
public extension WMTOperation {
var proximityCheck: WMTProximityCheck? { nil }
}
9 changes: 8 additions & 1 deletion WultraMobileTokenSDK/Operations/QR/WMTQROperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public struct WMTQROperation {
/// Flags associated with the operation
public let flags: QROperationFlags

/// Additional Time-based one time password for proximity check
public let totp: String?

/// Data for signature validation
public let signedData: Data

Expand All @@ -52,7 +55,11 @@ public struct WMTQROperation {
}

internal var dataForOfflineSigning: Data {
return "\(operationId)&\(operationData.sourceString)".data(using: .utf8)!
if let totp = totp {
return "\(operationId)&\(operationData.sourceString)&\(totp)".data(using: .utf8)!
} else {
return "\(operationId)&\(operationData.sourceString)".data(using: .utf8)!
}
}
}

Expand Down
8 changes: 5 additions & 3 deletions WultraMobileTokenSDK/Operations/QR/WMTQROperationParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ public class WMTQROperationParser {
private static let minimumAttributeFields = 7

/// Current number of lines in input string, supported by this parser
private static let currentAttributeFields = 7
private static let currentAttributeFields = 8
Hopsaheysa marked this conversation as resolved.
Show resolved Hide resolved

/// Maximum number of operation data fields supported in this version.
private static let maximumDataFields = 5
private static let maximumDataFields = 6

/// Parses input string into `WMTQROperationData` structure.
public func parse(string: String) -> WMTQROperationParseResult {
Expand All @@ -47,6 +47,7 @@ public class WMTQROperationParser {
let message = parseAttributeText(from: String(attributes[2]))
let dataString = String(attributes[3])
let flagsString = String(attributes[4])
let totp = attributes.count > WMTQROperationParser.minimumAttributeFields ? String(attributes[5]) : nil
// Signature and nonce are always located at last lines
let nonce = String(attributes[attributes.count - 2])
let signatureString = attributes[attributes.count - 1]
Expand Down Expand Up @@ -74,7 +75,7 @@ public class WMTQROperationParser {

// Parse flags
let flags = parseOperationFlags(string: flagsString)
let isNewerFormat = attributes.count > WMTQROperationParser.currentAttributeFields
let isNewerFormat = attributes.count > WMTQROperationParser.currentAttributeFields

// Build final structure
return .success(WMTQROperation(
Expand All @@ -84,6 +85,7 @@ public class WMTQROperationParser {
operationData: formData,
nonce: nonce,
flags: flags,
totp: totp,
signedData: signedData,
signature: signature,
isNewerFormat: isNewerFormat)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ public extension WMTErrorReason {
static let operations_authExpired = WMTErrorReason(rawValue: "operations_authExpired")
/// Operation has expired when trying to reject the operation.
static let operations_rejectExpired = WMTErrorReason(rawValue: "operations_rejectExpired")
/// Operation action failed.
static let operations_failed = WMTErrorReason(rawValue: "operations_failed")

/// Couldn't sign QR operation.
static let operations_QROperationFailed = WMTErrorReason(rawValue: "operations_QRFailed")
Expand Down Expand Up @@ -241,7 +243,7 @@ class WMTOperationsImpl<T: WMTUserOperation>: WMTOperations, WMTService {
return nil
}

let data = WMTAuthorizationData(operationId: operation.id, operationData: operation.data)
let data = WMTAuthorizationData(operation: operation, timestampSigned: currentServerDate ?? Date())

return networking.post(data: .init(data), signedWith: authentication, to: WMTOperationEndpoints.Authorize.endpoint) { response, error in
self.processResult(response: response, error: error) { result in
Expand Down Expand Up @@ -436,6 +438,8 @@ class WMTOperationsImpl<T: WMTUserOperation>: WMTOperations, WMTService {
} else {
reason = .operations_rejectExpired
}
case .operationFailed:
reason = .operations_failed
default:
break
}
Expand Down
Loading
Loading