diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 47f64ff..4cad100 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,10 +26,10 @@ jobs: platform: - iOS steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v4 - name: Remove Development.xcworkspace to avoid tooling confusion run: rm -rf Development.xcworkspace - - uses: mxcl/xcodebuild@v1 + - uses: mxcl/xcodebuild@v3.0.0 with: platform: ${{ matrix.platform }} action: test diff --git a/CustomAuth.podspec b/CustomAuth.podspec index 023db6e..28ddf2b 100644 --- a/CustomAuth.podspec +++ b/CustomAuth.podspec @@ -10,6 +10,6 @@ Pod::Spec.new do |spec| spec.module_name = "CustomAuth" spec.source = { :git => "https://github.com/torusresearch/customauth-swift-sdk.git", :tag => spec.version } spec.source_files = "Sources/CustomAuth/*.{swift}","Sources/CustomAuth/**/*.{swift}" - spec.dependency 'Torus-utils', '~> 8.1.0' + spec.dependency 'Torus-utils', '~> 9.0.0' spec.dependency 'JWTDecode', '~> 3.1.0' end diff --git a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj index f62f369..208ada2 100644 --- a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj +++ b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.pbxproj @@ -7,16 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - 511CEACC2452D4EC00A7ACE9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511CEACB2452D4EC00A7ACE9 /* AppDelegate.swift */; }; - 511CEACE2452D4EC00A7ACE9 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511CEACD2452D4EC00A7ACE9 /* SceneDelegate.swift */; }; 511CEAD02452D4EC00A7ACE9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511CEACF2452D4EC00A7ACE9 /* ContentView.swift */; }; 511CEAD22452D4EF00A7ACE9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 511CEAD12452D4EF00A7ACE9 /* Assets.xcassets */; }; 511CEAD52452D4EF00A7ACE9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 511CEAD42452D4EF00A7ACE9 /* Preview Assets.xcassets */; }; - 511CEAD82452D4EF00A7ACE9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 511CEAD62452D4EF00A7ACE9 /* LaunchScreen.storyboard */; }; 511CEAE32452D4EF00A7ACE9 /* CustomAuthDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511CEAE22452D4EF00A7ACE9 /* CustomAuthDemoTests.swift */; }; 511CEAEE2452D4EF00A7ACE9 /* CustomAuthDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511CEAED2452D4EF00A7ACE9 /* CustomAuthDemoUITests.swift */; }; 51775EB72484B66A00A29680 /* CustomAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 51775EB62484B66A00A29680 /* CustomAuth */; }; - B36D0EFF2BA033AD008F4C71 /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = B36D0EFE2BA033AD008F4C71 /* PromiseKit */; }; + B37E3CEE2C1CF30F00B63F41 /* CustomAuthDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37E3CED2C1CF30F00B63F41 /* CustomAuthDemo.swift */; }; + B37E3CF12C1E1C6E00B63F41 /* CustomAuth in Frameworks */ = {isa = PBXBuildFile; productRef = B37E3CF02C1E1C6E00B63F41 /* CustomAuth */; }; + B37E3CF32C1E23B700B63F41 /* CustomAuth in Frameworks */ = {isa = PBXBuildFile; productRef = B37E3CF22C1E23B700B63F41 /* CustomAuth */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -38,12 +37,9 @@ /* Begin PBXFileReference section */ 511CEAC82452D4EC00A7ACE9 /* CustomAuthDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CustomAuthDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 511CEACB2452D4EC00A7ACE9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 511CEACD2452D4EC00A7ACE9 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 511CEACF2452D4EC00A7ACE9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 511CEAD12452D4EF00A7ACE9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 511CEAD42452D4EF00A7ACE9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 511CEAD72452D4EF00A7ACE9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 511CEAD92452D4EF00A7ACE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 511CEADE2452D4EF00A7ACE9 /* CustomAuthDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CustomAuthDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 511CEAE22452D4EF00A7ACE9 /* CustomAuthDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAuthDemoTests.swift; sourceTree = ""; }; @@ -51,9 +47,8 @@ 511CEAE92452D4EF00A7ACE9 /* CustomAuthDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CustomAuthDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 511CEAED2452D4EF00A7ACE9 /* CustomAuthDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAuthDemoUITests.swift; sourceTree = ""; }; 511CEAEF2452D4EF00A7ACE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 51721D842474E4B700C22BAD /* torus-direct-swift-sdk */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "torus-direct-swift-sdk"; path = ..; sourceTree = ""; }; - 517C5F5D24693F9D006D5A43 /* CustomAuthDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CustomAuthDemo.entitlements; sourceTree = ""; }; A3C24F222A70FAF9002F4FC9 /* customauth-swift-sdk */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "customauth-swift-sdk"; path = ..; sourceTree = ""; }; + B37E3CED2C1CF30F00B63F41 /* CustomAuthDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAuthDemo.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,7 +57,6 @@ buildActionMask = 2147483647; files = ( 51775EB72484B66A00A29680 /* CustomAuth in Frameworks */, - B36D0EFF2BA033AD008F4C71 /* PromiseKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -70,6 +64,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B37E3CF32C1E23B700B63F41 /* CustomAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -77,20 +72,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B37E3CF12C1E1C6E00B63F41 /* CustomAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 18B4F998A7F88C302F18BA73 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 51721D842474E4B700C22BAD /* torus-direct-swift-sdk */, - ); - name = Frameworks; - sourceTree = ""; - }; 511CEABF2452D4EC00A7ACE9 = { isa = PBXGroup; children = ( @@ -99,8 +87,7 @@ 511CEAE12452D4EF00A7ACE9 /* CustomAuthDemoTests */, 511CEAEC2452D4EF00A7ACE9 /* CustomAuthDemoUITests */, 511CEAC92452D4EC00A7ACE9 /* Products */, - A84257F51AACA6652F347CFD /* Pods */, - 18B4F998A7F88C302F18BA73 /* Frameworks */, + B37E3CEF2C1E1C6E00B63F41 /* Frameworks */, ); sourceTree = ""; }; @@ -117,12 +104,9 @@ 511CEACA2452D4EC00A7ACE9 /* CustomAuthDemo */ = { isa = PBXGroup; children = ( - 517C5F5D24693F9D006D5A43 /* CustomAuthDemo.entitlements */, - 511CEACB2452D4EC00A7ACE9 /* AppDelegate.swift */, - 511CEACD2452D4EC00A7ACE9 /* SceneDelegate.swift */, + B37E3CED2C1CF30F00B63F41 /* CustomAuthDemo.swift */, 511CEACF2452D4EC00A7ACE9 /* ContentView.swift */, 511CEAD12452D4EF00A7ACE9 /* Assets.xcassets */, - 511CEAD62452D4EF00A7ACE9 /* LaunchScreen.storyboard */, 511CEAD92452D4EF00A7ACE9 /* Info.plist */, 511CEAD32452D4EF00A7ACE9 /* Preview Content */, ); @@ -163,11 +147,11 @@ name = Packages; sourceTree = ""; }; - A84257F51AACA6652F347CFD /* Pods */ = { + B37E3CEF2C1E1C6E00B63F41 /* Frameworks */ = { isa = PBXGroup; children = ( ); - path = Pods; + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ @@ -188,7 +172,6 @@ name = CustomAuthDemo; packageProductDependencies = ( 51775EB62484B66A00A29680 /* CustomAuth */, - B36D0EFE2BA033AD008F4C71 /* PromiseKit */, ); productName = CustomAuthDemo; productReference = 511CEAC82452D4EC00A7ACE9 /* CustomAuthDemo.app */; @@ -208,6 +191,9 @@ 511CEAE02452D4EF00A7ACE9 /* PBXTargetDependency */, ); name = CustomAuthDemoTests; + packageProductDependencies = ( + B37E3CF22C1E23B700B63F41 /* CustomAuth */, + ); productName = CustomAuthDemoTests; productReference = 511CEADE2452D4EF00A7ACE9 /* CustomAuthDemoTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; @@ -226,6 +212,9 @@ 511CEAEB2452D4EF00A7ACE9 /* PBXTargetDependency */, ); name = CustomAuthDemoUITests; + packageProductDependencies = ( + B37E3CF02C1E1C6E00B63F41 /* CustomAuth */, + ); productName = CustomAuthDemoUITests; productReference = 511CEAE92452D4EF00A7ACE9 /* CustomAuthDemoUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; @@ -236,8 +225,9 @@ 511CEAC02452D4EC00A7ACE9 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1540; ORGANIZATIONNAME = Shubham; TargetAttributes = { 511CEAC72452D4EC00A7ACE9 = { @@ -263,7 +253,6 @@ ); mainGroup = 511CEABF2452D4EC00A7ACE9; packageReferences = ( - B36D0EFD2BA033AD008F4C71 /* XCRemoteSwiftPackageReference "PromiseKit" */, ); productRefGroup = 511CEAC92452D4EC00A7ACE9 /* Products */; projectDirPath = ""; @@ -281,7 +270,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 511CEAD82452D4EF00A7ACE9 /* LaunchScreen.storyboard in Resources */, 511CEAD52452D4EF00A7ACE9 /* Preview Assets.xcassets in Resources */, 511CEAD22452D4EF00A7ACE9 /* Assets.xcassets in Resources */, ); @@ -308,9 +296,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 511CEACC2452D4EC00A7ACE9 /* AppDelegate.swift in Sources */, - 511CEACE2452D4EC00A7ACE9 /* SceneDelegate.swift in Sources */, 511CEAD02452D4EC00A7ACE9 /* ContentView.swift in Sources */, + B37E3CEE2C1CF30F00B63F41 /* CustomAuthDemo.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -345,22 +332,12 @@ }; /* End PBXTargetDependency section */ -/* Begin PBXVariantGroup section */ - 511CEAD62452D4EF00A7ACE9 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 511CEAD72452D4EF00A7ACE9 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ 511CEAF02452D4EF00A7ACE9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -394,6 +371,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -423,6 +401,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -456,6 +435,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -485,7 +465,7 @@ DEVELOPMENT_TEAM = 2Q63NCPY55; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = CustomAuthDemo/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.3; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -507,7 +487,7 @@ DEVELOPMENT_TEAM = 2Q63NCPY55; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = CustomAuthDemo/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.3; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -526,7 +506,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CustomAuthDemoTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -547,7 +527,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CustomAuthDemoTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -567,6 +547,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CustomAuthDemoUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -586,6 +567,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CustomAuthDemoUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -640,26 +622,18 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - B36D0EFD2BA033AD008F4C71 /* XCRemoteSwiftPackageReference "PromiseKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/mxcl/PromiseKit"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 8.1.1; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ 51775EB62484B66A00A29680 /* CustomAuth */ = { isa = XCSwiftPackageProductDependency; productName = CustomAuth; }; - B36D0EFE2BA033AD008F4C71 /* PromiseKit */ = { + B37E3CF02C1E1C6E00B63F41 /* CustomAuth */ = { isa = XCSwiftPackageProductDependency; - package = B36D0EFD2BA033AD008F4C71 /* XCRemoteSwiftPackageReference "PromiseKit" */; - productName = PromiseKit; + productName = CustomAuth; + }; + B37E3CF22C1E23B700B63F41 /* CustomAuth */ = { + isa = XCSwiftPackageProductDependency; + productName = CustomAuth; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index f9b0d7c..a6f6fb2 100644 --- a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -2,6 +2,8 @@ + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + PreviewsEnabled diff --git a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3a07809..91491fa 100644 --- a/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CustomAuthDemo/CustomAuthDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,97 +1,68 @@ { - "object": { - "pins": [ - { - "package": "AnyCodable", - "repositoryURL": "https://github.com/Flight-School/AnyCodable", - "state": { - "branch": null, - "revision": "862808b2070cd908cb04f9aafe7de83d35f81b05", - "version": "0.6.7" - } - }, - { - "package": "BigInt", - "repositoryURL": "https://github.com/attaswift/BigInt.git", - "state": { - "branch": null, - "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", - "version": "5.3.0" - } - }, - { - "package": "CryptoSwift", - "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift", - "state": { - "branch": null, - "revision": "7892a123f7e8d0fe62f9f03728b17bbd4f94df5c", - "version": "1.8.1" - } - }, - { - "package": "curvelib.swift", - "repositoryURL": "https://github.com/tkey/curvelib.swift", - "state": { - "branch": null, - "revision": "7dad3bf1793de263f83406c08c18c9316abf082f", - "version": "0.1.2" - } - }, - { - "package": "FetchNodeDetails", - "repositoryURL": "https://github.com/torusresearch/fetch-node-details-swift", - "state": { - "branch": null, - "revision": "d591af500f32ce3c88d04af9bb74d746585acfea", - "version": "5.1.0" - } - }, - { - "package": "jwt-kit", - "repositoryURL": "https://github.com/vapor/jwt-kit.git", - "state": { - "branch": null, - "revision": "e05513b5aec24f88012b6e3034115b6bc915356a", - "version": "4.13.2" - } - }, - { - "package": "JWTDecode", - "repositoryURL": "https://github.com/auth0/JWTDecode.swift.git", - "state": { - "branch": null, - "revision": "58af7278797871e460d79496621b3e5366b865b2", - "version": "3.1.0" - } - }, - { - "package": "PromiseKit", - "repositoryURL": "https://github.com/mxcl/PromiseKit", - "state": { - "branch": null, - "revision": "cb70b070cde06837cd10a1febdf6105c1a3bb348", - "version": "8.1.1" - } - }, - { - "package": "swift-crypto", - "repositoryURL": "https://github.com/apple/swift-crypto.git", - "state": { - "branch": null, - "revision": "f0525da24dc3c6cbb2b6b338b65042bc91cbc4bb", - "version": "3.3.0" - } - }, - { - "package": "TorusUtils", - "repositoryURL": "https://github.com/torusresearch/torus-utils-swift.git", - "state": { - "branch": null, - "revision": "04c62fd5f73f21bd01b7c07e08f6135db26c5940", - "version": "8.0.0" - } + "pins" : [ + { + "identity" : "bigint", + "kind" : "remoteSourceControl", + "location" : "https://github.com/attaswift/BigInt.git", + "state" : { + "revision" : "0ed110f7555c34ff468e72e1686e59721f2b0da6", + "version" : "5.3.0" } - ] - }, - "version": 1 + }, + { + "identity" : "curvelib.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tkey/curvelib.swift", + "state" : { + "revision" : "9f88bd5e56d1df443a908f7a7e81ae4f4d9170ea", + "version" : "1.0.1" + } + }, + { + "identity" : "fetch-node-details-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/torusresearch/fetch-node-details-swift", + "state" : { + "revision" : "22bfadf7491d77a0bc1953af002cadbd61383e7d", + "version" : "6.0.2" + } + }, + { + "identity" : "jwt-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/jwt-kit.git", + "state" : { + "revision" : "c2595b9ad7f512d7f334830b4df1fed6e917946a", + "version" : "4.13.4" + } + }, + { + "identity" : "jwtdecode.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/JWTDecode.swift.git", + "state" : { + "revision" : "58af7278797871e460d79496621b3e5366b865b2", + "version" : "3.1.0" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "bc1c29221f6dfeb0ebbfbc98eb95cd3d4967868e", + "version" : "3.4.0" + } + }, + { + "identity" : "torus-utils-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/torusresearch/torus-utils-swift.git", + "state" : { + "branch" : "feature_updates", + "revision" : "976da309c9e28670af763a82947276864c27c08e" + } + } + ], + "version" : 2 } diff --git a/CustomAuthDemo/CustomAuthDemo.xcodeproj/xcshareddata/xcschemes/CustomAuthDemo.xcscheme b/CustomAuthDemo/CustomAuthDemo.xcodeproj/xcshareddata/xcschemes/CustomAuthDemo.xcscheme index 87ee52d..5723986 100644 --- a/CustomAuthDemo/CustomAuthDemo.xcodeproj/xcshareddata/xcschemes/CustomAuthDemo.xcscheme +++ b/CustomAuthDemo/CustomAuthDemo.xcodeproj/xcshareddata/xcschemes/CustomAuthDemo.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CustomAuthDemo/CustomAuthDemo.xcodeproj/xcshareddata/xcschemes/CustomAuthDemoUITests.xcscheme b/CustomAuthDemo/CustomAuthDemo.xcodeproj/xcshareddata/xcschemes/CustomAuthDemoUITests.xcscheme new file mode 100644 index 0000000..23e22d8 --- /dev/null +++ b/CustomAuthDemo/CustomAuthDemo.xcodeproj/xcshareddata/xcschemes/CustomAuthDemoUITests.xcscheme @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CustomAuthDemo/CustomAuthDemo/AppDelegate.swift b/CustomAuthDemo/CustomAuthDemo/AppDelegate.swift deleted file mode 100644 index 39ad318..0000000 --- a/CustomAuthDemo/CustomAuthDemo/AppDelegate.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// AppDelegate.swift -// CustomAuthDemo -// -// Created by Shubham on 24/4/20. -// Copyright © 2020 Shubham. All rights reserved. -// - -import UIKit - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate, UISceneDelegate { - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - // MARK: UISceneSession Lifecycle - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - -} diff --git a/CustomAuthDemo/CustomAuthDemo/Base.lproj/LaunchScreen.storyboard b/CustomAuthDemo/CustomAuthDemo/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e932..0000000 --- a/CustomAuthDemo/CustomAuthDemo/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CustomAuthDemo/CustomAuthDemo/ContentView.swift b/CustomAuthDemo/CustomAuthDemo/ContentView.swift index 3839f54..53496dc 100644 --- a/CustomAuthDemo/CustomAuthDemo/ContentView.swift +++ b/CustomAuthDemo/CustomAuthDemo/ContentView.swift @@ -1,338 +1,372 @@ -// -// ContentView.swift -// CustomAuthDemo -// -// Created by Shubham on 24/4/20. -// Copyright © 2020 Shubham. All rights reserved. -// - -import CryptoSwift import CustomAuth -import FetchNodeDetails -import PromiseKit -import SafariServices import SwiftUI struct ContentView: View { - @State var showSafari = false - var body: some View { - NavigationView { - List { - Section(header: Text("Single Logins")) { - // Group { - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .google, - clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", - verifier: "google-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") - - let tdsdk = CustomAuth(web3AuthClientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", aggregateVerifierType: .singleLogin, aggregateVerifier: "google-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Google Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .reddit, - clientId: "rXIp6g2y3h1wqg", - verifier: "reddit-shubs", - redirectURL: "tdsdk://tdsdk/oauthCallback") - let tdsdk = CustomAuth(web3AuthClientId: "rXIp6g2y3h1wqg", aggregateVerifierType: .singleLogin, aggregateVerifier: "reddit-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Reddit Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .discord, - clientId: "700259843063152661", - verifier: "discord-shubs", - redirectURL: "tdsdk://tdsdk/oauthCallback") - let tdsdk = CustomAuth(web3AuthClientId: "700259843063152661", aggregateVerifierType: .singleLogin, aggregateVerifier: "discord-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Discord Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .facebook, - clientId: "659561074900150", - verifier: "facebook-shubs", - redirectURL: "tdsdk://tdsdk/oauthCallback", browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") - - let tdsdk = CustomAuth(web3AuthClientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", aggregateVerifierType: .singleLogin, aggregateVerifier: "facebook-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Facebook Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .twitch, - clientId: "p560duf74b2bidzqu6uo0b3ot7qaao", - verifier: "twitch-shubs", - redirectURL: "tdsdk://tdsdk/oauthCallback") - let tdsdk = CustomAuth(web3AuthClientId: "p560duf74b2bidzqu6uo0b3ot7qaao", aggregateVerifierType: .singleLogin, aggregateVerifier: "twitch-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Twitch Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .twitter, - clientId: "A7H8kkcmyFRlusJQ9dZiqBLraG2yWIsO", - verifier: "torus-auth0-twitter-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - jwtParams: ["domain": "torus-test.auth0.com"]) - - let tdsdk = CustomAuth(web3AuthClientId: "A7H8kkcmyFRlusJQ9dZiqBLraG2yWIsO", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-twitter-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Twitter Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .github, - clientId: "PC2a4tfNRvXbT48t89J5am0oFM21Nxff", - verifier: "torus-auth0-github-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - browserRedirectURL: "https://scripts.toruswallet.io/redirect.html", - jwtParams: ["domain": "torus-test.auth0.com"]) - - let tdsdk = CustomAuth(web3AuthClientId: "PC2a4tfNRvXbT48t89J5am0oFM21Nxff", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-github-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Github Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .linkedin, - clientId: "59YxSgx79Vl3Wi7tQUBqQTRTxWroTuoc", - verifier: "torus-auth0-linkedin-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - jwtParams: ["domain": "torus-test.auth0.com"]) - - let tdsdk = CustomAuth(web3AuthClientId: "59YxSgx79Vl3Wi7tQUBqQTRTxWroTuoc", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-linkedin-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Linkedin Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .apple, - clientId: "m1Q0gvDfOyZsJCZ3cucSQEe9XMvl9d9L", - verifier: "torus-auth0-apple-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - jwtParams: ["domain": "torus-test.auth0.com"]) - - let tdsdk = CustomAuth(web3AuthClientId: "m1Q0gvDfOyZsJCZ3cucSQEe9XMvl9d9L", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-apple-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Apple Login") - }) - // } - - // Group { - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .jwt, - clientId: "P7PJuBCXIHP41lcyty0NEb7Lgf7Zme8Q", - verifier: "torus-auth0-email-passwordless", - redirectURL: "tdsdk://tdsdk/oauthCallback", - jwtParams: ["domain": "torus-test.auth0.com", "verifier_id_field": "name"]) - - let tdsdk = CustomAuth(web3AuthClientId: "P7PJuBCXIHP41lcyty0NEb7Lgf7Zme8Q", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-email-passwordless", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Email-password Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .kakao, - clientId: "wpkcc7alGJjEgjaL6q5AWRqgRWHFsdTL", - verifier: "torus-auth0-kakao-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - jwtParams: ["domain": "torus-test.auth0.com"]) - - let tdsdk = CustomAuth(web3AuthClientId: "wpkcc7alGJjEgjaL6q5AWRqgRWHFsdTL", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-kakao-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Kakao Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .apple, - clientId: "dhFGlWQMoACOI5oS5A1jFglp772OAWr1", - verifier: "torus-auth0-weibo-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - jwtParams: ["domain": "torus-test.auth0.com"]) - - let tdsdk = CustomAuth(web3AuthClientId: "dhFGlWQMoACOI5oS5A1jFglp772OAWr1", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-weibo-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Weibo Login") - }) - - Button(action: { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .wechat, - clientId: "cewDD3i6F1vtHeV1KIbaxUZ8vJQjJZ8V", - verifier: "torus-auth0-wechat-lrc", - redirectURL: "tdsdk://tdsdk/oauthCallback", - jwtParams: ["domain": "torus-test.auth0.com"]) - - let tdsdk = CustomAuth(web3AuthClientId: "cewDD3i6F1vtHeV1KIbaxUZ8vJQjJZ8V", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-wechat-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Wechat Login") - }) - } - // } - - Section(header: Text("Single ID verifier")) { - Button(action: { - let sub = SubVerifierDetails(loginType: .installed, - loginProvider: .google, - clientId: "238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4.apps.googleusercontent.com", - verifier: "google-ios", - redirectURL: "com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4:/oauthredirect") - let tdsdk = CustomAuth(web3AuthClientId: "238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4.apps.googleusercontent.com", aggregateVerifierType: .singleIdVerifier, aggregateVerifier: "multigoogle-torus", subVerifierDetails: [sub], network: .legacy(.TESTNET)) - Task { - do { - let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! - print(loginData) - } catch { - print("Error occured") - } - } - }, label: { - Text("Google Login - Deep link flow") - }) + VStack { + Button(action: { + Task { + do { + let sub = SingleLoginParams(typeOfLogin: .google, verifier: "google-lrc", clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", redirectURL: "https://scripts.toruswallet.io/redirect.html") + + let customAuthArgs = CustomAuthArgs(urlScheme: "tdsdk://tdsdk/oauthCallback", network: .sapphire(.SAPPHIRE_DEVNET), enableOneKey: true, web3AuthClientId: "BAh0_c0G8U8GoMUIYDcX_f65fU_N9O0mWz6xM6RqBfaaAlYsTha8oOef7ifXPjd_bCTJdfWQemmrbY6KepC7XNA") + + let customAuth = try CustomAuth(config: customAuthArgs) + let torusLoginResponse = try await customAuth.triggerLogin(args: sub) + let encoded = try JSONEncoder().encode(torusLoginResponse) + debugPrint(String(data: encoded, encoding: .utf8)!) + } catch { + debugPrint(error) + } + } + }, label: { + Text("Google Login") + }) - }.navigationBarTitle(Text("DirectAuth app")) - } - } -} + Button(action: { + Task { + do { + let sub = SingleLoginParams(typeOfLogin: .discord, verifier: "dhruv-discord", clientId: "1034724991972954172", redirectURL: "https://scripts.toruswallet.io/redirect.html") -struct SafariView: UIViewControllerRepresentable { - typealias UIViewControllerType = SFSafariViewController + let customAuthArgs = CustomAuthArgs(urlScheme: "tdsdk://tdsdk/oauthCallback", network: .sapphire(.SAPPHIRE_DEVNET), enableOneKey: true, web3AuthClientId: "BAh0_c0G8U8GoMUIYDcX_f65fU_N9O0mWz6xM6RqBfaaAlYsTha8oOef7ifXPjd_bCTJdfWQemmrbY6KepC7XNA") - var url: URL? + let customAuth = try CustomAuth(config: customAuthArgs) + let torusLoginResponse = try await customAuth.triggerLogin(args: sub) + let encoded = try JSONEncoder().encode(torusLoginResponse) + debugPrint(String(data: encoded, encoding: .utf8)!) + } catch { + debugPrint(error) + } - func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { - return SFSafariViewController(url: url!) - } + } + }, label: { + Text("Discord Login") + }) + + /* + Button(action: { + Task { + do { + let sub = SingleLoginParams(typeOfLogin: .apple, verifier: "torus-auth0-apple-lrc", clientId: "m1Q0gvDfOyZsJCZ3cucSQEe9XMvl9d9L", redirectURL: "tdsdk://tdsdk/oauthCallback", jwtParams: Auth0ClientOptions(display: nil, prompt: nil, max_age: nil, ui_locales: nil, id_token_hint: nil, arc_values: nil, scope: nil, audience: nil, connection: "apple", domain: "torus-test.auth0.com", client_id: nil, redirect_url: nil, leeway: nil, verifierIdField: nil, isVerifierIdCaseSensitive: true, id_token: nil, access_token: nil, user_info_route: nil)) + + let customAuthArgs = CustomAuthArgs(urlScheme: "tdsdk://tdsdk/oauthCallback", network: .sapphire(.SAPPHIRE_DEVNET), enableOneKey: true, web3AuthClientId: "BAh0_c0G8U8GoMUIYDcX_f65fU_N9O0mWz6xM6RqBfaaAlYsTha8oOef7ifXPjd_bCTJdfWQemmrbY6KepC7XNA") + + let customAuth = try CustomAuth(config: customAuthArgs) + let torusLoginResponse = try await customAuth.triggerLogin(args: sub) + let encoded = try JSONEncoder().encode(torusLoginResponse) + debugPrint(String(data: encoded, encoding: .utf8)!) + } catch { + debugPrint(error) + } - func updateUIViewController(_ safariViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) { + } + }, label: { + Text("Apple Login") + }) + */ + } } } -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} +// TODO: Update with all different combinations +/* + struct ContentView: View { + + var body: some View { + NavigationView { + List { + Section(header: Text("Single Logins")) { + Button(action: { + let sub = SingleLoginParams(typeOfLogin: .google, verifier: "google-lrc", clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", redirectURL: "tdsdk://tdsdk/oauthCallback") + + let customAuthArgs = CustomAuthArgs(network: .sapphire(.SAPPHIRE_DEVNET), enableOneKey: true, web3AuthClientId: "BAh0_c0G8U8GoMUIYDcX_f65fU_N9O0mWz6xM6RqBfaaAlYsTha8oOef7ifXPjd_bCTJdfWQemmrbY6KepC7XNA") + + let customAuth = CustomAuth(config: customAuthArgs) + + Task { + do { + let torusLoginResponse = try await customAuth.triggerLogin(args: sub) + let encoded = JSONEncoder().encode(torusLoginResponse) + debugPrint(encoded) + } catch { + debugPrint(error) + } + } + }, label: { + Text("Google Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .reddit, + clientId: "rXIp6g2y3h1wqg", + verifier: "reddit-shubs", + redirectURL: "tdsdk://tdsdk/oauthCallback") + let tdsdk = CustomAuth(web3AuthClientId: "rXIp6g2y3h1wqg", aggregateVerifierType: .singleLogin, aggregateVerifier: "reddit-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Reddit Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .discord, + clientId: "700259843063152661", + verifier: "discord-shubs", + redirectURL: "tdsdk://tdsdk/oauthCallback") + let tdsdk = CustomAuth(web3AuthClientId: "700259843063152661", aggregateVerifierType: .singleLogin, aggregateVerifier: "discord-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Discord Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .facebook, + clientId: "659561074900150", + verifier: "facebook-shubs", + redirectURL: "tdsdk://tdsdk/oauthCallback", browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") + + let tdsdk = CustomAuth(web3AuthClientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", aggregateVerifierType: .singleLogin, aggregateVerifier: "facebook-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Facebook Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .twitch, + clientId: "p560duf74b2bidzqu6uo0b3ot7qaao", + verifier: "twitch-shubs", + redirectURL: "tdsdk://tdsdk/oauthCallback") + let tdsdk = CustomAuth(web3AuthClientId: "p560duf74b2bidzqu6uo0b3ot7qaao", aggregateVerifierType: .singleLogin, aggregateVerifier: "twitch-shubs", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Twitch Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .twitter, + clientId: "A7H8kkcmyFRlusJQ9dZiqBLraG2yWIsO", + verifier: "torus-auth0-twitter-lrc", + redirectURL: "tdsdk://tdsdk/oauthCallback", + jwtParams: ["domain": "torus-test.auth0.com"]) + + let tdsdk = CustomAuth(web3AuthClientId: "A7H8kkcmyFRlusJQ9dZiqBLraG2yWIsO", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-twitter-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Twitter Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .github, + clientId: "PC2a4tfNRvXbT48t89J5am0oFM21Nxff", + verifier: "torus-auth0-github-lrc", + redirectURL: "tdsdk://tdsdk/oauthCallback", + browserRedirectURL: "https://scripts.toruswallet.io/redirect.html", + jwtParams: ["domain": "torus-test.auth0.com"]) + + let tdsdk = CustomAuth(web3AuthClientId: "PC2a4tfNRvXbT48t89J5am0oFM21Nxff", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-github-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Github Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .linkedin, + clientId: "59YxSgx79Vl3Wi7tQUBqQTRTxWroTuoc", + verifier: "torus-auth0-linkedin-lrc", + redirectURL: "tdsdk://tdsdk/oauthCallback", + jwtParams: ["domain": "torus-test.auth0.com"]) + + let tdsdk = CustomAuth(web3AuthClientId: "59YxSgx79Vl3Wi7tQUBqQTRTxWroTuoc", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-linkedin-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Linkedin Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .apple, + clientId: "m1Q0gvDfOyZsJCZ3cucSQEe9XMvl9d9L", + verifier: "torus-auth0-apple-lrc", + redirectURL: "tdsdk://tdsdk/oauthCallback", + jwtParams: ["domain": "torus-test.auth0.com"]) + + let tdsdk = CustomAuth(web3AuthClientId: "m1Q0gvDfOyZsJCZ3cucSQEe9XMvl9d9L", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-apple-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Apple Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .jwt, + clientId: "P7PJuBCXIHP41lcyty0NEb7Lgf7Zme8Q", + verifier: "torus-auth0-email-passwordless", + redirectURL: "tdsdk://tdsdk/oauthCallback", + jwtParams: ["domain": "torus-test.auth0.com", "verifier_id_field": "name"]) + + let tdsdk = CustomAuth(web3AuthClientId: "P7PJuBCXIHP41lcyty0NEb7Lgf7Zme8Q", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-email-passwordless", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Email-password Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .kakao, + clientId: "wpkcc7alGJjEgjaL6q5AWRqgRWHFsdTL", + verifier: "torus-auth0-kakao-lrc", + redirectURL: "tdsdk://tdsdk/oauthCallback", + jwtParams: ["domain": "torus-test.auth0.com"]) + + let tdsdk = CustomAuth(web3AuthClientId: "wpkcc7alGJjEgjaL6q5AWRqgRWHFsdTL", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-kakao-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Kakao Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .apple, + clientId: "dhFGlWQMoACOI5oS5A1jFglp772OAWr1", + verifier: "torus-auth0-weibo-lrc", + redirectURL: "tdsdk://tdsdk/oauthCallback", + jwtParams: ["domain": "torus-test.auth0.com"]) + + let tdsdk = CustomAuth(web3AuthClientId: "dhFGlWQMoACOI5oS5A1jFglp772OAWr1", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-weibo-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Weibo Login") + }) + + Button(action: { + let sub = SubVerifierDetails(loginType: .web, + loginProvider: .wechat, + clientId: "cewDD3i6F1vtHeV1KIbaxUZ8vJQjJZ8V", + verifier: "torus-auth0-wechat-lrc", + redirectURL: "tdsdk://tdsdk/oauthCallback", + jwtParams: ["domain": "torus-test.auth0.com"]) + + let tdsdk = CustomAuth(web3AuthClientId: "cewDD3i6F1vtHeV1KIbaxUZ8vJQjJZ8V", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-auth0-wechat-lrc", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Wechat Login") + }) + } + + Section(header: Text("Single ID verifier")) { + Button(action: { + let sub = SubVerifierDetails(loginType: .installed, + loginProvider: .google, + clientId: "238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4.apps.googleusercontent.com", + verifier: "google-ios", + redirectURL: "com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4:/oauthredirect") + let tdsdk = CustomAuth(web3AuthClientId: "238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4.apps.googleusercontent.com", aggregateVerifierType: .singleIdVerifier, aggregateVerifier: "multigoogle-torus", subVerifierDetails: [sub], network: .legacy(.TESTNET)) + Task { + do { + let loginData = try await tdsdk.triggerLogin().torusKey.finalKeyData! + print(loginData) + } catch { + print("Error occured") + } + } + }, label: { + Text("Google Login - Deep link flow") + }) + } + + }.navigationBarTitle(Text("DirectAuth app")) + } + } + } + */ diff --git a/CustomAuthDemo/CustomAuthDemo/CustomAuthDemo.swift b/CustomAuthDemo/CustomAuthDemo/CustomAuthDemo.swift new file mode 100644 index 0000000..feb4574 --- /dev/null +++ b/CustomAuthDemo/CustomAuthDemo/CustomAuthDemo.swift @@ -0,0 +1,12 @@ +import SwiftUI + +@main +struct CustomAuthDemo: App { + var body: some Scene { + WindowGroup { + NavigationStack { + ContentView() + } + } + } +} diff --git a/CustomAuthDemo/CustomAuthDemo/Info.plist b/CustomAuthDemo/CustomAuthDemo/Info.plist index 46d87e4..4152026 100644 --- a/CustomAuthDemo/CustomAuthDemo/Info.plist +++ b/CustomAuthDemo/CustomAuthDemo/Info.plist @@ -20,13 +20,12 @@ CFBundleTypeRole - Editor + Viewer + CFBundleURLName + tdsdk CFBundleURLSchemes - tdsdk-swift - tdsdk - com.torus.CustomAuthDemo - com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4 + tdsdk://tdsdk/oauthCallback @@ -45,8 +44,6 @@ UISceneConfigurationName Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate diff --git a/CustomAuthDemo/CustomAuthDemo/SceneDelegate.swift b/CustomAuthDemo/CustomAuthDemo/SceneDelegate.swift deleted file mode 100644 index e0f970e..0000000 --- a/CustomAuthDemo/CustomAuthDemo/SceneDelegate.swift +++ /dev/null @@ -1,84 +0,0 @@ -// SceneDelegate.swift -// CustomAuthDemo -// -// Created by Shubham on 24/4/20. -// Copyright © 2020 Shubham. All rights reserved. -// - -import UIKit -import SwiftUI -import CustomAuth - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - var window: UIWindow? - - // Handle Universal logins - func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { - guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, - let urlToOpen = userActivity.webpageURL else { - return - } - CustomAuth.handle(url: urlToOpen) - } - - // Hanlde Deep linkings - func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { - guard let url = URLContexts.first?.url else { - return - } - CustomAuth.handle(url: url) - } - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - - // Create the SwiftUI view that provides the window contents. - let contentView = ContentView() - - // Use a UIHostingController as window root view controller. - if let windowScene = scene as? UIWindowScene { - let window = UIWindow(windowScene: windowScene) - window.rootViewController = UIHostingController(rootView: contentView) - self.window = window - window.makeKeyAndVisible() - } - } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } - -} - -struct SceneDelegate_Previews: PreviewProvider { - static var previews: some View { - /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/ - } -} diff --git a/CustomAuthDemo/CustomAuthDemoTests/CustomAuthDemoTests.swift b/CustomAuthDemo/CustomAuthDemoTests/CustomAuthDemoTests.swift index e90dfa1..2a1647c 100644 --- a/CustomAuthDemo/CustomAuthDemoTests/CustomAuthDemoTests.swift +++ b/CustomAuthDemo/CustomAuthDemoTests/CustomAuthDemoTests.swift @@ -1,33 +1,19 @@ -// -// CustomAuthDemoTests.swift -// CustomAuthDemoTests -// -// Created by Shubham on 24/4/20. -// Copyright © 2020 Shubham. All rights reserved. -// - import XCTest -@testable import CustomAuthDemo +import CustomAuthDemo class CustomAuthDemoTests: XCTestCase { override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. } func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() throws { - // This is an example of a performance test case. self.measure { - // Put the code you want to measure the time of here. } } diff --git a/CustomAuthDemo/CustomAuthDemoUITests/CustomAuthDemoUITests.swift b/CustomAuthDemo/CustomAuthDemoUITests/CustomAuthDemoUITests.swift index cd64582..3d48c4c 100644 --- a/CustomAuthDemo/CustomAuthDemoUITests/CustomAuthDemoUITests.swift +++ b/CustomAuthDemo/CustomAuthDemoUITests/CustomAuthDemoUITests.swift @@ -1,43 +1,22 @@ -// -// CustomAuthDemoUITests.swift -// CustomAuthDemoUITests -// -// Created by Shubham on 24/4/20. -// Copyright © 2020 Shubham. All rights reserved. -// - import XCTest +import CustomAuthDemo class CustomAuthDemoUITests: XCTestCase { - + override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. } override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. } func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. } func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { + measure(metrics: [XCTApplicationLaunchMetric.init()]) { XCUIApplication().launch() } - } } } diff --git a/Development.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Development.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3a07809..9efd57e 100644 --- a/Development.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Development.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,97 +1,15 @@ { - "object": { - "pins": [ - { - "package": "AnyCodable", - "repositoryURL": "https://github.com/Flight-School/AnyCodable", - "state": { - "branch": null, - "revision": "862808b2070cd908cb04f9aafe7de83d35f81b05", - "version": "0.6.7" - } - }, - { - "package": "BigInt", - "repositoryURL": "https://github.com/attaswift/BigInt.git", - "state": { - "branch": null, - "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", - "version": "5.3.0" - } - }, - { - "package": "CryptoSwift", - "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift", - "state": { - "branch": null, - "revision": "7892a123f7e8d0fe62f9f03728b17bbd4f94df5c", - "version": "1.8.1" - } - }, - { - "package": "curvelib.swift", - "repositoryURL": "https://github.com/tkey/curvelib.swift", - "state": { - "branch": null, - "revision": "7dad3bf1793de263f83406c08c18c9316abf082f", - "version": "0.1.2" - } - }, - { - "package": "FetchNodeDetails", - "repositoryURL": "https://github.com/torusresearch/fetch-node-details-swift", - "state": { - "branch": null, - "revision": "d591af500f32ce3c88d04af9bb74d746585acfea", - "version": "5.1.0" - } - }, - { - "package": "jwt-kit", - "repositoryURL": "https://github.com/vapor/jwt-kit.git", - "state": { - "branch": null, - "revision": "e05513b5aec24f88012b6e3034115b6bc915356a", - "version": "4.13.2" - } - }, - { - "package": "JWTDecode", - "repositoryURL": "https://github.com/auth0/JWTDecode.swift.git", - "state": { - "branch": null, - "revision": "58af7278797871e460d79496621b3e5366b865b2", - "version": "3.1.0" - } - }, - { - "package": "PromiseKit", - "repositoryURL": "https://github.com/mxcl/PromiseKit", - "state": { - "branch": null, - "revision": "cb70b070cde06837cd10a1febdf6105c1a3bb348", - "version": "8.1.1" - } - }, - { - "package": "swift-crypto", - "repositoryURL": "https://github.com/apple/swift-crypto.git", - "state": { - "branch": null, - "revision": "f0525da24dc3c6cbb2b6b338b65042bc91cbc4bb", - "version": "3.3.0" - } - }, - { - "package": "TorusUtils", - "repositoryURL": "https://github.com/torusresearch/torus-utils-swift.git", - "state": { - "branch": null, - "revision": "04c62fd5f73f21bd01b7c07e08f6135db26c5940", - "version": "8.0.0" - } + "originHash" : "15db2e52658433a7d473400cc03eb7d9196e5e1ee7c0636fcf9c95b35e7c0bf1", + "pins" : [ + { + "identity" : "promisekit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mxcl/PromiseKit", + "state" : { + "revision" : "cb70b070cde06837cd10a1febdf6105c1a3bb348", + "version" : "8.1.1" } - ] - }, - "version": 1 + } + ], + "version" : 3 } diff --git a/Notice.md b/Notice.md deleted file mode 100644 index ea74098..0000000 --- a/Notice.md +++ /dev/null @@ -1,4 +0,0 @@ -Following code was borrowed/inspired from web3swift library (Apache 2.0 license) -- SFSafariViewControoler implementation -- Dictionary extension for splitting string -- String extension diff --git a/Package.resolved b/Package.resolved index 6675452..b0c2196 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,79 +1,68 @@ { - "object": { - "pins": [ - { - "package": "AnyCodable", - "repositoryURL": "https://github.com/Flight-School/AnyCodable", - "state": { - "branch": null, - "revision": "862808b2070cd908cb04f9aafe7de83d35f81b05", - "version": "0.6.7" - } - }, - { - "package": "BigInt", - "repositoryURL": "https://github.com/attaswift/BigInt", - "state": { - "branch": null, - "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", - "version": "5.3.0" - } - }, - { - "package": "curvelib.swift", - "repositoryURL": "https://github.com/tkey/curvelib.swift", - "state": { - "branch": null, - "revision": "9f88bd5e56d1df443a908f7a7e81ae4f4d9170ea", - "version": "1.0.1" - } - }, - { - "package": "FetchNodeDetails", - "repositoryURL": "https://github.com/torusresearch/fetch-node-details-swift.git", - "state": { - "branch": null, - "revision": "f085d3d85a4f36b57cfef8f0871ac8df1dd4f6f1", - "version": "6.0.1" - } - }, - { - "package": "jwt-kit", - "repositoryURL": "https://github.com/vapor/jwt-kit.git", - "state": { - "branch": null, - "revision": "9e929d925434b91857661bcd455d1bd53f00bf22", - "version": "4.13.0" - } - }, - { - "package": "JWTDecode", - "repositoryURL": "https://github.com/auth0/JWTDecode.swift.git", - "state": { - "branch": null, - "revision": "58af7278797871e460d79496621b3e5366b865b2", - "version": "3.1.0" - } - }, - { - "package": "swift-crypto", - "repositoryURL": "https://github.com/apple/swift-crypto.git", - "state": { - "branch": null, - "revision": "33a20e650c33f6d72d822d558333f2085effa3dc", - "version": "2.5.0" - } - }, - { - "package": "TorusUtils", - "repositoryURL": "https://github.com/torusresearch/torus-utils-swift.git", - "state": { - "branch": null, - "revision": "f20a23bb11b3c144650ff17048f94068f410ceae", - "version": "8.1.0" - } + "pins" : [ + { + "identity" : "bigint", + "kind" : "remoteSourceControl", + "location" : "https://github.com/attaswift/BigInt", + "state" : { + "revision" : "0ed110f7555c34ff468e72e1686e59721f2b0da6", + "version" : "5.3.0" } - ] - }, - "version": 1 + }, + { + "identity" : "curvelib.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tkey/curvelib.swift", + "state" : { + "revision" : "9f88bd5e56d1df443a908f7a7e81ae4f4d9170ea", + "version" : "1.0.1" + } + }, + { + "identity" : "fetch-node-details-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/torusresearch/fetch-node-details-swift.git", + "state" : { + "revision" : "22bfadf7491d77a0bc1953af002cadbd61383e7d", + "version" : "6.0.2" + } + }, + { + "identity" : "jwt-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/jwt-kit.git", + "state" : { + "revision" : "9e929d925434b91857661bcd455d1bd53f00bf22", + "version" : "4.13.0" + } + }, + { + "identity" : "jwtdecode.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/JWTDecode.swift.git", + "state" : { + "revision" : "58af7278797871e460d79496621b3e5366b865b2", + "version" : "3.1.0" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "33a20e650c33f6d72d822d558333f2085effa3dc", + "version" : "2.5.0" + } + }, + { + "identity" : "torus-utils-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/torusresearch/torus-utils-swift.git", + "state" : { + "branch" : "feature_updates", + "revision" : "976da309c9e28670af763a82947276864c27c08e" + } + } + ], + "version" : 2 } diff --git a/Package.swift b/Package.swift index 96c1926..346d342 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version:5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "CustomAuth", platforms: [ - .iOS(.v13) + .iOS(.v13), .macOS(.v11) ], products: [ .library( @@ -14,15 +14,16 @@ let package = Package( targets: ["CustomAuth"]) ], dependencies: [ - .package(url: "https://github.com/torusresearch/torus-utils-swift.git", from: "8.1.0"), - .package(name: "jwt-kit", url: "https://github.com/vapor/jwt-kit.git", from: "4.13.0"), - .package(name: "JWTDecode", url: "https://github.com/auth0/JWTDecode.swift.git", from: "3.1.0"), - .package(url: "https://github.com/tkey/curvelib.swift", from: "1.0.0"), + .package(url: "https://github.com/torusresearch/torus-utils-swift.git", branch: "feature_updates"), // TODO: Change to 9.0.0 + .package(url: "https://github.com/auth0/JWTDecode.swift.git", from: "3.1.0"), + .package(url: "https://github.com/tkey/curvelib.swift", from: "1.0.1"), + // NB: jwt-kit may only be a test dependency or it will break cocoapods support + .package(url: "https://github.com/vapor/jwt-kit.git", from: "4.13.0"), ], targets: [ .target( name: "CustomAuth", - dependencies: ["JWTDecode", .product(name: "curveSecp256k1", package: "curvelib.swift"), .product(name: "TorusUtils", package: "torus-utils-swift")]), + dependencies: [.product(name: "JWTDecode", package: "JWTDecode.swift"), .product(name: "curveSecp256k1", package: "curvelib.swift"), .product(name: "TorusUtils", package: "torus-utils-swift")]), .testTarget( name: "CustomAuthTests", dependencies: ["CustomAuth", .product(name: "JWTKit", package: "jwt-kit")]) diff --git a/README.md b/README.md index 37f6e38..5ce3818 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,6 @@ an example of how to access keys via the SDK via Google. You can read more about interactions with the Torus Network [here](https://medium.com/toruslabs/key-assignments-resolution-and-retrieval-afb984500612). -## Features - -- All API's return Promises (mxcl/PromiseKit). You can import - "yannickl/AwaitKit" to convert APIs to async/await format. - ## 🩹 Examples Checkout the example of `CustomAuth iOS/Swift SDK` in our @@ -32,7 +27,7 @@ import PackageDescription let package = Package( name: "CustomAuth", dependencies: [ - .package(name: "CustomAuth", url: "https://github.com/torusresearch/customauth-swift-sdk", from: "2.4.0")) + .package(name: "CustomAuth", url: "https://github.com/torusresearch/customauth-swift-sdk", from: "11.0.0")) ] ) ``` @@ -40,41 +35,36 @@ let package = Package( #### Cocoapods ```ruby -pod 'CustomAuth', '~> 5.0.0' +pod 'CustomAuth', '~> 11.0.0' ``` -#### Manual import or other packages - -If you require a package manager other than SPM or Cocoapods, do reach out to -hello@tor.us or alternatively clone the repo manually and import as a framework -in your project - -### 2. Initialization +### 2. Initialization and Login -Initalize the SDK depending on the login you require. The example below does so -for a single google login. `redirectURL` refers to a url for the login flow to -redirect into your app, it should have a scheme that is registered by your app, -for example `com.mycompany.myapp://redirect`. `browserRedirectURL` refers to a -page that the browser should use in the login flow, it should have a http or -https scheme. +Initalize the SDK depending and then you can use the login you require. ```swift import CustomAuth -let sub = SubVerifierDetails(loginType: .installed, // default .web - loginProvider: .google, - clientId: "", - verifierName: "", - redirectURL: "", - browserRedirectURL: "") +let config = CustomAuthArgs(urlScheme: ", enableOneKey: true, web3AuthClientId: "your-web3auth-client-id") + + +let customAuth = try CustomAuth(config: config) + +``` + +The example login below does so for a single google login. `redirectURL` refers to url for the login flow to +redirect back to your app, it should use the scheme known to your application. -let tdsdk = CustomAuth(aggregateVerifierType: "", aggregateVerifierName: "", subVerifierDetails: [sub], network: ) -// controller is used to present a SFSafariViewController. -tdsdk.triggerLogin(controller: ?, browserType: , modalPresentationStyle: ).done{ data in - print("private key rebuild", data) -}.catch{ err in - print(err) +``` +let sub = SingleLoginParams(typeOfLogin: .google, verifier: "", clientId: "", redirectURL: ") { - guard let url = URLContexts.first?.url else { - return - } - CustomAuth.handle(url: url) -} -``` - -- For Storyboard, implement the following in your app AppDelegate: - -```swift -func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { - if url.host == "my-wallet-app" { - CustomAuth.handle(url: url) - } - return true -} -``` - -#### Universal Links - -Universal Links allow your users to intelligently follow links to content inside -your app or to your website. Checkout -[Documentation](https://developer.apple.com/ios/universal-links/) for -implementation. - -- For Swift UI, - -```swift -func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { - guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let urlToOpen = userActivity.webpageURL else { - return - } - CustomAuth.handle(url: urlToOpen) -} -``` - -- For Storyboard, - -```swift -func application(_ application: UIApplication, continue userActivity: UIUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool -{ - // Get URL components from the incoming user activity - guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, - let incomingURL = userActivity.webpageURL, - let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else { - return false - } - CustomAuth.handle(url: incomingURL) -} - -``` - -After this you're good to go, reach out to hello@tor.us to get your verifier -spun up on the testnet today! +You can setup the redirectURL using URL Schemes and adding the relevant URLScheme to URL Types for your project. This package makes use of ASWebAuthenticationSession underneath and is done in such a way that it can provide its' own presentation context if necessary. ## Requirements - Swift 5 -## Using CustomAuthFactory - -The `CASDKFactoryProtocol` provides a way to modify the mechanism of discovering -torus nodes in `FetchNodeDetails` and performing key retrieval in `TorusUtils`, -which can be useful in scenarios such as mocking or advanced customization. -Developers who want to use this mechanism should implement -`CASDKFactoryProtocol` in Sources/CustomAuth/CustomAuth.swift, and then pass the -instance into the `init` of `CustomAuth`, for example: - -```swift -let tdsdk = CustomAuth( - aggregateVerifierType: "", - aggregateVerifierName: "", - subVerifierDetails: [sub], - factory: customFactory, - network: myNetworkm - loglevel: myLoglevel -) -``` - ## 💬 Troubleshooting and Discussions - Have a look at our diff --git a/Sources/CustomAuth/Common/AggregateVerifierParams/AggregateLoginParams.swift b/Sources/CustomAuth/Common/AggregateVerifierParams/AggregateLoginParams.swift new file mode 100644 index 0000000..2e64f30 --- /dev/null +++ b/Sources/CustomAuth/Common/AggregateVerifierParams/AggregateLoginParams.swift @@ -0,0 +1,13 @@ +import Foundation + +public class AggregateLoginParams: Codable { + public let aggregateVerifierType: AggregateVerifierType + public let verifierIdentifier: String + public let subVerifierDetailsArray: [SingleLoginParams] + + public init(aggregateVerifierType: AggregateVerifierType, verifierIdentifier: String, subVerifierDetailsArray: [SingleLoginParams]) { + self.aggregateVerifierType = aggregateVerifierType + self.verifierIdentifier = verifierIdentifier + self.subVerifierDetailsArray = subVerifierDetailsArray + } +} diff --git a/Sources/CustomAuth/Common/AggregateVerifierParams/AggregateVerifierType.swift b/Sources/CustomAuth/Common/AggregateVerifierParams/AggregateVerifierType.swift new file mode 100644 index 0000000..cc6fedd --- /dev/null +++ b/Sources/CustomAuth/Common/AggregateVerifierParams/AggregateVerifierType.swift @@ -0,0 +1,5 @@ +import Foundation + +public enum AggregateVerifierType: String, Codable, Equatable, Hashable { + case single_id_verifier +} diff --git a/Sources/CustomAuth/Common/HashParams.swift b/Sources/CustomAuth/Common/HashParams.swift new file mode 100644 index 0000000..41724b6 --- /dev/null +++ b/Sources/CustomAuth/Common/HashParams.swift @@ -0,0 +1,11 @@ +import Foundation + +public class HashParams: Codable { + public let access_token: String + public let id_token: String? + + public init(access_token: String, id_token: String? = nil) { + self.access_token = access_token + self.id_token = id_token + } +} diff --git a/Sources/CustomAuth/Common/LoginParams/Auth0ClientOptions.swift b/Sources/CustomAuth/Common/LoginParams/Auth0ClientOptions.swift new file mode 100644 index 0000000..8b10396 --- /dev/null +++ b/Sources/CustomAuth/Common/LoginParams/Auth0ClientOptions.swift @@ -0,0 +1,31 @@ +import Foundation + +public class Auth0ClientOptions: BaseLoginOptions { + public let domain: String? + public let client_id: String? + public let redirect_url: String? + public let leeway: Int? + public let verifierIdField: String? + public let isVerifierIdCaseSensitive: Bool + public let id_token: String? + public let access_token: String? + public let user_info_route: String? + + public init(display: String? = nil, prompt: String? = nil, max_age: Int? = nil, ui_locales: String? = nil, id_token_hint: String? = nil, arc_values: String? = nil, scope: String? = nil, audience: String? = nil, connection: String? = nil, domain: String? = nil, client_id: String? = nil, redirect_url: String? = nil, leeway: Int? = nil, verifierIdField: String? = nil, isVerifierIdCaseSensitive: Bool = false, id_token: String? = nil, access_token: String? = nil, user_info_route: String? = nil) { + self.domain = domain + self.redirect_url = redirect_url + self.leeway = leeway + self.verifierIdField = verifierIdField + self.isVerifierIdCaseSensitive = isVerifierIdCaseSensitive + self.id_token = id_token + self.access_token = access_token + self.user_info_route = user_info_route + self.client_id = client_id + + super.init(display: display, prompt: prompt, max_age: max_age, ui_locales: ui_locales, id_token_hint: id_token_hint, arc_values: arc_values, scope: scope, audience: audience, connection: connection) + } + + required init(from decoder: any Decoder) throws { + fatalError("init(from:) has not been implemented") + } +} diff --git a/Sources/CustomAuth/Common/LoginParams/Auth0JwtLoginType.swift b/Sources/CustomAuth/Common/LoginParams/Auth0JwtLoginType.swift new file mode 100644 index 0000000..121d4cf --- /dev/null +++ b/Sources/CustomAuth/Common/LoginParams/Auth0JwtLoginType.swift @@ -0,0 +1,12 @@ +import Foundation + +public enum Auth0JwtLoginType: String, Equatable, Hashable, Codable { + case apple + case github + case linkedin + case twitter + case weibo + case line + case email_password + case passwordless +} diff --git a/Sources/CustomAuth/Common/LoginParams/BaseLoginOptions.swift b/Sources/CustomAuth/Common/LoginParams/BaseLoginOptions.swift new file mode 100644 index 0000000..8046b0a --- /dev/null +++ b/Sources/CustomAuth/Common/LoginParams/BaseLoginOptions.swift @@ -0,0 +1,25 @@ +import Foundation + +public class BaseLoginOptions: Codable { + public let display: String? + public let prompt: String? + public let max_age: Int? + public let ui_locales: String? + public let id_token_hint: String? + public let arc_values: String? + public let scope: String? + public let audience: String? + public let connection: String? + + public init(display: String? = nil, prompt: String? = nil, max_age: Int? = nil, ui_locales: String? = nil, id_token_hint: String? = nil, arc_values: String? = nil, scope: String? = nil, audience: String? = nil, connection: String? = nil) { + self.display = display + self.prompt = prompt + self.max_age = max_age + self.ui_locales = ui_locales + self.id_token_hint = id_token_hint + self.arc_values = arc_values + self.scope = scope + self.audience = audience + self.connection = connection + } +} diff --git a/Sources/CustomAuth/Common/LoginParams/HybridAggregateLoginParams.swift b/Sources/CustomAuth/Common/LoginParams/HybridAggregateLoginParams.swift new file mode 100644 index 0000000..5fe21fb --- /dev/null +++ b/Sources/CustomAuth/Common/LoginParams/HybridAggregateLoginParams.swift @@ -0,0 +1,11 @@ +import Foundation + +public class HybridAggregateLoginParams: Codable { + public let singleLogin: SingleLoginParams + public let aggregateLoginParams: AggregateLoginParams + + public init(singleLogin: SingleLoginParams, aggregateLoginParams: AggregateLoginParams) { + self.singleLogin = singleLogin + self.aggregateLoginParams = aggregateLoginParams + } +} diff --git a/Sources/CustomAuth/Common/LoginParams/LoginType.swift b/Sources/CustomAuth/Common/LoginParams/LoginType.swift new file mode 100644 index 0000000..8bb77a4 --- /dev/null +++ b/Sources/CustomAuth/Common/LoginParams/LoginType.swift @@ -0,0 +1,17 @@ +import Foundation + +public enum LoginType: String, Equatable, Hashable, Codable { + case google + case facebook + case discord + case reddit + case twitch + case apple + case github + case linkedin + case twitter + case weibo + case line + case email_password + case jwt +} diff --git a/Sources/CustomAuth/Common/LoginParams/SubVerifierDetails.swift b/Sources/CustomAuth/Common/LoginParams/SubVerifierDetails.swift new file mode 100644 index 0000000..d3c2aae --- /dev/null +++ b/Sources/CustomAuth/Common/LoginParams/SubVerifierDetails.swift @@ -0,0 +1,25 @@ +import Foundation + +public class SubVerifierDetails: Codable { + public let typeOfLogin: LoginType + public let verifier: String + public let clientId: String + public let redirectURL: String + public let jwtParams: Auth0ClientOptions? + public let hash: String? + public let queryParams: TorusGenericContainer? + public let customState: TorusGenericContainer? + + public init(typeOfLogin: LoginType, verifier: String, clientId: String, redirectURL: String, jwtParams: Auth0ClientOptions? = nil, hash: String? = nil, queryParams: TorusGenericContainer? = nil, customState: TorusGenericContainer? = nil) { + self.typeOfLogin = typeOfLogin + self.verifier = verifier + self.clientId = clientId + self.jwtParams = jwtParams + self.hash = hash + self.queryParams = queryParams + self.customState = customState + self.redirectURL = redirectURL + } +} + +public typealias SingleLoginParams = SubVerifierDetails diff --git a/Sources/CustomAuth/Common/LoginParams/TorusSubVerifierInfo.swift b/Sources/CustomAuth/Common/LoginParams/TorusSubVerifierInfo.swift new file mode 100644 index 0000000..74f9873 --- /dev/null +++ b/Sources/CustomAuth/Common/LoginParams/TorusSubVerifierInfo.swift @@ -0,0 +1,13 @@ +import Foundation + +public class TorusSubVerifierInfo: Codable { + public let verifier: String + public let idToken: String + public let extraVerifierParams: PassKeyExtraParams? + + public init(verifier: String, idToken: String, extraVerifierParams: PassKeyExtraParams? = nil) { + self.verifier = verifier + self.idToken = idToken + self.extraVerifierParams = extraVerifierParams + } +} diff --git a/Sources/CustomAuth/Common/LoginResponses/LoginWindowResponse.swift b/Sources/CustomAuth/Common/LoginResponses/LoginWindowResponse.swift new file mode 100644 index 0000000..b7d29ea --- /dev/null +++ b/Sources/CustomAuth/Common/LoginResponses/LoginWindowResponse.swift @@ -0,0 +1,19 @@ +import Foundation + +public class LoginWindowResponse: Codable { + public let accessToken: String? + public let idToken: String? + public let ref: String + public let extraParams: String? + public let extraParamsPassed: String? + public let state: TorusGenericContainer + + public init(accessToken: String? = nil, idToken: String? = nil, ref: String, extraParams: String? = nil, extraParamsPassed: String? = nil, state: TorusGenericContainer) { + self.accessToken = accessToken + self.idToken = idToken + self.ref = ref + self.extraParams = extraParams + self.extraParamsPassed = extraParamsPassed + self.state = state + } +} diff --git a/Sources/CustomAuth/Common/LoginResponses/TorusAggregateLoginResponse.swift b/Sources/CustomAuth/Common/LoginResponses/TorusAggregateLoginResponse.swift new file mode 100644 index 0000000..d051f10 --- /dev/null +++ b/Sources/CustomAuth/Common/LoginResponses/TorusAggregateLoginResponse.swift @@ -0,0 +1,12 @@ +import Foundation +import TorusUtils + +public class TorusAggregateLoginResponse: Codable { + public let torusAggregateVerifierResponse: [TorusAggregateVerifierResponse] + public let torusKey: TorusKey + + public init(torusAggregateVerifierResponse: [TorusAggregateVerifierResponse], torusKey: TorusKey) { + self.torusAggregateVerifierResponse = torusAggregateVerifierResponse + self.torusKey = torusKey + } +} diff --git a/Sources/CustomAuth/Common/LoginResponses/TorusHybridAggregateLoginResponse.swift b/Sources/CustomAuth/Common/LoginResponses/TorusHybridAggregateLoginResponse.swift new file mode 100644 index 0000000..80eb821 --- /dev/null +++ b/Sources/CustomAuth/Common/LoginResponses/TorusHybridAggregateLoginResponse.swift @@ -0,0 +1,12 @@ +import Foundation +import TorusUtils + +public class TorusHybridAggregateLoginResponse: Codable { + public let singleLogin: TorusAggregateLoginResponse + public let aggregateLogins: [TorusKey] + + public init(singleLogin: TorusAggregateLoginResponse, aggregateLogins: [TorusKey]) { + self.singleLogin = singleLogin + self.aggregateLogins = aggregateLogins + } +} diff --git a/Sources/CustomAuth/Common/LoginResponses/TorusLoginResponse.swift b/Sources/CustomAuth/Common/LoginResponses/TorusLoginResponse.swift new file mode 100644 index 0000000..e4b666c --- /dev/null +++ b/Sources/CustomAuth/Common/LoginResponses/TorusLoginResponse.swift @@ -0,0 +1,12 @@ +import Foundation +import TorusUtils + +public class TorusLoginResponse: Codable { + public let singleVerifierResponse: TorusSingleVerifierResponse + public let torusKey: TorusKey + + public init(singleVerifierResponse: TorusSingleVerifierResponse, torusKey: TorusKey) { + self.singleVerifierResponse = singleVerifierResponse + self.torusKey = torusKey + } +} diff --git a/Sources/CustomAuth/Common/Passkeys/AuthenticatorTransports.swift b/Sources/CustomAuth/Common/Passkeys/AuthenticatorTransports.swift new file mode 100644 index 0000000..30a3275 --- /dev/null +++ b/Sources/CustomAuth/Common/Passkeys/AuthenticatorTransports.swift @@ -0,0 +1,9 @@ +import Foundation + +public enum AuthenticatorTransports: String, Equatable, Hashable, Codable { + case ble + case hybrid + case inside = "internal" + case nfc + case usb +} diff --git a/Sources/CustomAuth/Common/Passkeys/PassKeyExtraParams.swift b/Sources/CustomAuth/Common/Passkeys/PassKeyExtraParams.swift new file mode 100644 index 0000000..924fbc0 --- /dev/null +++ b/Sources/CustomAuth/Common/Passkeys/PassKeyExtraParams.swift @@ -0,0 +1,27 @@ +import Foundation + +public class PassKeyExtraParams: Codable { + public let signature: String? + public let clientDataJson: String? + public let authenticatorJsonData: String? + public let publicKey: String? + public let challenge: String? + public let rpOrigin: String? + public let rpId: String? + public let credId: String? + public let transports: [AuthenticatorTransports]? + public let username: String? + + init(signature: String?, clientDataJson: String? = nil, authenticatorJsonData: String? = nil, publicKey: String? = nil, challenge: String? = nil, rpOrigin: String? = nil, rpId: String? = nil, credId: String? = nil, transports: [AuthenticatorTransports]? = nil, username: String? = nil) { + self.signature = signature + self.clientDataJson = clientDataJson + self.authenticatorJsonData = authenticatorJsonData + self.publicKey = publicKey + self.challenge = challenge + self.rpOrigin = rpOrigin + self.rpId = rpId + self.credId = credId + self.transports = transports + self.username = username + } +} diff --git a/Sources/CustomAuth/Common/PopupResponse.swift b/Sources/CustomAuth/Common/PopupResponse.swift new file mode 100644 index 0000000..bf7c7b6 --- /dev/null +++ b/Sources/CustomAuth/Common/PopupResponse.swift @@ -0,0 +1,11 @@ +import Foundation + +public class PopupResponse: Codable { + public let hashParams: HashParams + public let instanceParams: TorusGenericContainer + + public init(hashParams: HashParams, instanceParams: TorusGenericContainer) { + self.hashParams = hashParams + self.instanceParams = instanceParams + } +} diff --git a/Sources/CustomAuth/Common/RedirectResultParams.swift b/Sources/CustomAuth/Common/RedirectResultParams.swift new file mode 100644 index 0000000..6260d3b --- /dev/null +++ b/Sources/CustomAuth/Common/RedirectResultParams.swift @@ -0,0 +1,11 @@ +import Foundation + +public class RedirectResultParams: Codable { + public let relaceUrl: Bool + public let clearLoginDetails: Bool + + public init(relaceUrl: Bool, clearLoginDetails: Bool) { + self.relaceUrl = relaceUrl + self.clearLoginDetails = clearLoginDetails + } +} diff --git a/Sources/CustomAuth/Common/State.swift b/Sources/CustomAuth/Common/State.swift new file mode 100644 index 0000000..36c6d07 --- /dev/null +++ b/Sources/CustomAuth/Common/State.swift @@ -0,0 +1,28 @@ +import Foundation + +public class State: Codable { + public var customState: TorusGenericContainer? + public var instanceId: String + public var verifier: String + public var typeOfLogin: String + public var redirectUri: String + public var redirectToAndroid: String = "true" + + public init(instanceId: String, verifier: String, typeOfLogin: String, redirectUri: String, customState: TorusGenericContainer? = nil) { + self.customState = customState + self.instanceId = instanceId + self.verifier = verifier + self.typeOfLogin = typeOfLogin + self.redirectUri = redirectUri + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(customState, forKey: .customState) + try container.encode(instanceId, forKey: .instanceId) + try container.encode(verifier, forKey: .verifier) + try container.encode(typeOfLogin, forKey: .typeOfLogin) + try container.encode(redirectToAndroid, forKey: .redirectToAndroid) + try container.encode(redirectUri, forKey: .redirectUri) + } +} diff --git a/Sources/CustomAuth/Common/TorusGenericContainer.swift b/Sources/CustomAuth/Common/TorusGenericContainer.swift new file mode 100644 index 0000000..ca1abf0 --- /dev/null +++ b/Sources/CustomAuth/Common/TorusGenericContainer.swift @@ -0,0 +1,9 @@ +import Foundation + +public class TorusGenericContainer: Codable { + public let params: [String: String] + + init(params: [String: String]) { + self.params = params + } +} diff --git a/Sources/CustomAuth/Common/VerifierResponses/TorusAggregateVerifierResponse.swift b/Sources/CustomAuth/Common/VerifierResponses/TorusAggregateVerifierResponse.swift new file mode 100644 index 0000000..86cf42a --- /dev/null +++ b/Sources/CustomAuth/Common/VerifierResponses/TorusAggregateVerifierResponse.swift @@ -0,0 +1,11 @@ +import Foundation + +public class TorusAggregateVerifierResponse: Codable { + public let userInfo: UserInfo + public let loginResponse: LoginWindowResponse + + public init(userInfo: UserInfo, loginResponse: LoginWindowResponse) { + self.userInfo = userInfo + self.loginResponse = loginResponse + } +} diff --git a/Sources/CustomAuth/Common/VerifierResponses/TorusSingleVerifierResponse.swift b/Sources/CustomAuth/Common/VerifierResponses/TorusSingleVerifierResponse.swift new file mode 100644 index 0000000..6f55103 --- /dev/null +++ b/Sources/CustomAuth/Common/VerifierResponses/TorusSingleVerifierResponse.swift @@ -0,0 +1,11 @@ +import Foundation + +public class TorusSingleVerifierResponse: Codable { + public let userInfo: UserInfo + public let loginResponse: LoginWindowResponse + + public init(userInfo: UserInfo, loginResponse: LoginWindowResponse) { + self.userInfo = userInfo + self.loginResponse = loginResponse + } +} diff --git a/Sources/CustomAuth/Common/VerifierResponses/TorusVerifierResponses.swift b/Sources/CustomAuth/Common/VerifierResponses/TorusVerifierResponses.swift new file mode 100644 index 0000000..f66c938 --- /dev/null +++ b/Sources/CustomAuth/Common/VerifierResponses/TorusVerifierResponses.swift @@ -0,0 +1,26 @@ +import Foundation +import TorusUtils + +public class TorusVerifierResponse: Codable { + public let email: String + public let name: String + public let profileImage: String + public let aggregateVerifier: String? + public let verifier: String + public let verifierId: String + public let typeOfLogin: LoginType + public let ref: String? + public let extraVerifierParams: PassKeyExtraParams? + + public init(email: String, name: String, profileImage: String, aggregateVerifier: String? = nil, verifier: String, verifierId: String, typeOfLogin: LoginType, ref: String? = nil, extraVerifierParams: PassKeyExtraParams? = nil) { + self.email = email + self.name = name + self.profileImage = profileImage + self.aggregateVerifier = aggregateVerifier + self.verifier = verifier + self.verifierId = verifierId + self.typeOfLogin = typeOfLogin + self.ref = ref + self.extraVerifierParams = extraVerifierParams + } +} diff --git a/Sources/CustomAuth/Common/VerifierResponses/UserInfo.swift b/Sources/CustomAuth/Common/VerifierResponses/UserInfo.swift new file mode 100644 index 0000000..f9895df --- /dev/null +++ b/Sources/CustomAuth/Common/VerifierResponses/UserInfo.swift @@ -0,0 +1,35 @@ +import Foundation + +public class UserInfo: Codable { + public let email: String + public let name: String + public let profileImage: String + public let aggregateVerifier: String? + public let verifier: String + public let verifierId: String + public let typeOfLogin: LoginType + public let ref: String? + public let extraVerifierParams: PassKeyExtraParams? + public let accessToken: String? + public let idToken: String? + public let extraParams: String? + public let extraParamsPassed: String? + public let state: TorusGenericContainer + + public init(email: String, name: String, profileImage: String, aggregateVerifier: String? = nil, verifier: String, verifierId: String, typeOfLogin: LoginType, ref: String? = nil, extraVerifierParams: PassKeyExtraParams? = nil, accessToken: String? = nil, idToken: String? = nil, extraParams: String? = nil, extraParamsPassed: String? = nil, state: TorusGenericContainer) { + self.email = email + self.name = name + self.profileImage = profileImage + self.aggregateVerifier = aggregateVerifier + self.verifier = verifier + self.verifierId = verifierId + self.typeOfLogin = typeOfLogin + self.ref = ref + self.extraVerifierParams = extraVerifierParams + self.accessToken = accessToken + self.idToken = idToken + self.extraParams = extraParams + self.extraParamsPassed = extraParamsPassed + self.state = state + } +} diff --git a/Sources/CustomAuth/CustomAuth.swift b/Sources/CustomAuth/CustomAuth.swift index f3d9857..1946b1b 100644 --- a/Sources/CustomAuth/CustomAuth.swift +++ b/Sources/CustomAuth/CustomAuth.swift @@ -1,264 +1,207 @@ -// -// CustomAuth class -// CustomAuth -// -// Created by Shubham Rathi on 24/4/2020. -// - import FetchNodeDetails import Foundation import OSLog import TorusUtils -import UIKit -import curveSecp256k1 - -// Global variable -var tsSdkLogType = OSLogType.default - -public struct TorusKeyData { - public let torusKey : TorusKey - public var userInfo : [String: Any] - - public init(torusKey: TorusKey, userInfo: [String : Any]) { - self.torusKey = torusKey - self.userInfo = userInfo - } -} +#if canImport(curveSecp256k1) + import curveSecp256k1 +#endif -/// Provides integration of an iOS app with Torus CustomAuth. -open class CustomAuth { - var nodeDetailManager: NodeDetailManager - var torusUtils: AbstractTorusUtils - var urlSession: URLSession - var enableOneKey: Bool - /// You can pass your own custom url rather than using our default infura url, - /// can be used to get around the Ropsten depreciation from Infura API. - var networkUrl: String? - public let aggregateVerifierType: verifierTypes? - public let aggregateVerifier: String - public let subVerifierDetails: [SubVerifierDetails] - public var authorizeURLHandler: URLOpenerTypes? - var observer: NSObjectProtocol? // useful for Notifications - var network : TorusNetwork - - /// Initiate an CustomAuth instance. - /// - Parameters: - /// - aggregateVerifierType: Type of the verifier. Use `singleLogin` for single providers. Only `singleLogin` and `singleIdVerifier` is supported currently. - /// - aggregateVerifier: Name of the verifier to be used.. - /// - subVerifierDetails: Details of each subverifiers to be used. - /// - factory: Providng mocking by implementing TDSDKFactoryProtocol. - /// - network: Etherum network to be used. - /// - loglevel: Indicates the log level of this instance. All logs lower than this level will be ignored. - public init(web3AuthClientId: String ,aggregateVerifierType: verifierTypes, aggregateVerifier: String, subVerifierDetails: [SubVerifierDetails], network: TorusNetwork, loglevel: OSLogType = .debug, urlSession: URLSession = URLSession.shared, enableOneKey: Bool = false, networkUrl: String? = nil) { - tsSdkLogType = loglevel - self.networkUrl = networkUrl - self.enableOneKey = enableOneKey - // factory method - self.nodeDetailManager = NodeDetailManager(network: network) - self.urlSession = urlSession - - self.torusUtils = TorusUtils( loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, serverTimeOffset: 1000, network: network, clientId: web3AuthClientId) +public class CustomAuth { + public let isInitialized: Bool = false + + public let config: CustomAuthArgs + + public let torus: TorusUtils + + public let nodeDetailManager: NodeDetailManager + + public init(config: CustomAuthArgs) throws { + if URL(string: config.urlScheme)?.scheme == nil { + throw CASDKError.invalidCallbackURLScheme + } - // verifier details - self.aggregateVerifier = aggregateVerifier - self.aggregateVerifierType = aggregateVerifierType - self.subVerifierDetails = subVerifierDetails - self.network = network - } - - /// Initiate an CustomAuth instance. - /// - Parameters: - /// - aggregateVerifierType: Type of the verifier. Use `singleLogin` for single providers. Only `singleLogin` and `singleIdVerifier` is supported currently. - /// - aggregateVerifier: Name of the verifier to be used.. - /// - subVerifierDetails: Details of each subverifiers to be used. - public convenience init(web3AuthClientId: String ,aggregateVerifierType: verifierTypes, aggregateVerifier: String, subVerifierDetails: [SubVerifierDetails], network: TorusNetwork, enableOneKey: Bool = false, networkUrl: String? = nil) { - // let factory = CASDKFactory() - self.init(web3AuthClientId: web3AuthClientId, aggregateVerifierType: aggregateVerifierType, aggregateVerifier: aggregateVerifier, subVerifierDetails: subVerifierDetails, network: network, loglevel: .debug, enableOneKey: enableOneKey, networkUrl: networkUrl) - } - - // public convenience init(web3AuthClientId: String ,aggregateVerifierType: verifierTypes, aggregateVerifier: String, subVerifierDetails: [SubVerifierDetails], network: TorusNetwork, loglevel: OSLogType = .debug, enableOneKey: Bool = false, networkUrl: String? = nil) { - //// let factory = CASDKFactory() - // self.init(web3AuthClientId: web3AuthClientId, aggregateVerifierType: aggregateVerifierType, aggregateVerifier: aggregateVerifier, subVerifierDetails: subVerifierDetails, network: network, loglevel: loglevel, enableOneKey: enableOneKey, networkUrl: networkUrl) - // } - - /// Retrieve information of Torus nodes from a predefined Etherum contract. - /// - Returns: An array of URLs to the nodes. - open func getNodeDetailsFromContract(verifier: String, verfierID: String) async throws -> AllNodeDetailsModel { - let nodeDetails = try await nodeDetailManager.getNodeDetails(verifier: verifier, verifierID: verfierID) - return nodeDetails + self.config = config + + let nodeDetails = NodeDetailManager(network: config.network) + nodeDetailManager = nodeDetails + + let torusOptions = TorusOptions( + clientId: config.web3AuthClientId, + network: config.network, + legacyMetadataHost: config.metadataUrl, + serverTimeOffset: config.serverTimeOffset, + enableOneKey: config.enableOneKey) + let torusUtils = try TorusUtils(params: torusOptions) + torus = torusUtils + + torus.setApiKey(apiKey: config.apiKey ?? "") } - - /// Trigger login flow. - /// - Parameters: - /// - controller: A `UIViewController` used for providing context for the login flow. - /// - browserType: Indicates the way to open the browser for login flow. Use `.external` for opening system safari, or `.asWebAuthSession` for opening an in-app ASwebAuthenticationSession. - /// - modalPresentationStyle: Indicates the UIModalPresentationStyle for the popup. - /// - Returns: A promise that resolve with a Dictionary that contain at least `privateKey` and `publicAddress` field.. - open func triggerLogin(controller: UIViewController? = nil, browserType: URLOpenerTypes = .asWebAuthSession, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> TorusKeyData { - os_log("triggerLogin called with %@ %@", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, browserType.rawValue, modalPresentationStyle.rawValue) - // Set browser - authorizeURLHandler = browserType - - switch aggregateVerifierType { - case .singleLogin: - return try await handleSingleLogins(controller: controller, modalPresentationStyle: modalPresentationStyle) - case .andAggregateVerifier: - return try await handleAndAggregateVerifier(controller: controller) - case .orAggregateVerifier: - return try await handleOrAggregateVerifier(controller: controller) - case .singleIdVerifier: - return try await handleSingleIdVerifier(controller: controller, modalPresentationStyle: modalPresentationStyle) - case .none: - throw CASDKError.methodUnavailable + + public func triggerLogin(args: SingleLoginParams) async throws -> TorusLoginResponse { + let loginHandler = try HandlerFactory.createHandler(params: CreateHandlerParams(typeOfLogin: args.typeOfLogin, verifier: args.verifier, clientId: args.clientId, urlScheme: config.urlScheme, redirectURL: args.redirectURL, jwtParams: args.jwtParams, customState: args.customState)) + + var loginParams: LoginWindowResponse + if args.hash != nil && args.queryParams != nil { + let (error, hashParams, instanceParams) = try handleRedirectParameters(hash: args.hash!, queryParameters: args.queryParams!) + if !error.isEmpty { + throw CASDKError.redirectParamsError(msg: error) + } + loginParams = LoginWindowResponse(accessToken: hashParams.params["access_token"], idToken: hashParams.params["idToken"], ref: hashParams.params["ref"] ?? "", extraParams: hashParams.params["extra_params"], extraParamsPassed: hashParams.params["extra_params_passed"]!, state: instanceParams) + } else { + loginParams = try await loginHandler.handleLoginWindow(popupFeatures: config.popupFeatures) } + + let userInfo = try await loginHandler.getUserInfo(params: loginParams, storageServerUrl: nil) + + let verifyParams: VerifierParams = VerifierParams(verifier_id: userInfo.verifierId) + + let torusKey = try await getTorusKey(verifier: userInfo.verifier, verifierParams: verifyParams, idToken: loginParams.idToken!) + + let returnedInfo = UserInfo(email: userInfo.email, name: userInfo.name, profileImage: userInfo.profileImage, aggregateVerifier: userInfo.aggregateVerifier, verifier: userInfo.verifier, verifierId: userInfo.verifierId, typeOfLogin: userInfo.typeOfLogin, ref: userInfo.ref, extraVerifierParams: userInfo.extraVerifierParams, accessToken: loginParams.accessToken, idToken: loginParams.idToken, extraParams: loginParams.extraParams, extraParamsPassed: loginParams.extraParamsPassed, state: loginParams.state) + + return TorusLoginResponse(singleVerifierResponse: TorusSingleVerifierResponse(userInfo: returnedInfo, loginResponse: loginParams), torusKey: torusKey) } - - open func handleSingleLogins(controller: UIViewController?, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> TorusKeyData { - if let subVerifier = subVerifierDetails.first { - let loginURL = subVerifier.getLoginURL() - await openURL(url: loginURL, view: controller, modalPresentationStyle: modalPresentationStyle) - do { - let url = try await withUnsafeThrowingContinuation { (continuation: UnsafeContinuation) in - observeCallbackWithError { url, err in - guard - err == nil, - let url = url - else { - continuation.resume(throwing: err!) - return - } - - continuation.resume(returning: url) - return - } + + public func triggerAggregateLogin(args: AggregateLoginParams) async throws -> TorusAggregateLoginResponse { + if args.subVerifierDetailsArray.isEmpty { + throw CASDKError.invalidParameters + } + if args.subVerifierDetailsArray.count == 1 && args.aggregateVerifierType == AggregateVerifierType.single_id_verifier { + throw CASDKError.invalidParameters + } + + var loginParamsArray: [LoginWindowResponse] = [] + var userInfoArray: [UserInfo] = [] + for subverifierDetail in args.subVerifierDetailsArray { + let loginHandler = try HandlerFactory.createHandler(params: CreateHandlerParams(typeOfLogin: subverifierDetail.typeOfLogin, verifier: subverifierDetail.verifier, clientId: subverifierDetail.clientId, urlScheme: config.urlScheme, redirectURL: subverifierDetail.redirectURL, jwtParams: subverifierDetail.jwtParams, customState: subverifierDetail.customState)) + var loginParams: LoginWindowResponse + var userInfo: UserInfo + if subverifierDetail.hash != nil && subverifierDetail.queryParams != nil { + let (error, hashParams, instanceParams) = try handleRedirectParameters(hash: subverifierDetail.hash!, queryParameters: subverifierDetail.queryParams!) + if !error.isEmpty { + throw CASDKError.redirectParamsError(msg: error) } - let responseParameters = self.parseURL(url: url) - os_log("ResponseParams after redirect: %@", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, responseParameters) - - let newData = try await subVerifier.getUserInfo(responseParameters: responseParameters) - os_log("getUserInfo newData: %@", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, newData) - var data = newData - let verifierId = data["verifierId"] as! String - let idToken = data["tokenForKeys"] as! String - data.removeValue(forKey: "tokenForKeys") - data.removeValue(forKey: "verifierId") - - - let torusKey = try await getTorusKey(verifier: self.aggregateVerifier, verifierId: verifierId, idToken: idToken, userData: data) - var mergedUserInfo = newData.merging(responseParameters) { (_, new) in new } - mergedUserInfo["verifier"] = self.aggregateVerifier - let result = TorusKeyData(torusKey: torusKey, userInfo: mergedUserInfo) - return result - } catch { - os_log("handleSingleLogin: err: %s", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) - throw error + loginParams = LoginWindowResponse(accessToken: hashParams.params["access_token"], idToken: hashParams.params["idToken"], ref: hashParams.params["ref"] ?? "", extraParams: hashParams.params["extra_params"], extraParamsPassed: hashParams.params["extra_params_passed"]!, state: instanceParams) + } else { + loginParams = try await loginHandler.handleLoginWindow(popupFeatures: config.popupFeatures) } - - // Open in external safari + + let info = try await loginHandler.getUserInfo(params: loginParams, storageServerUrl: nil) + userInfo = UserInfo(email: info.email, name: info.name, profileImage: info.profileImage, aggregateVerifier: args.verifierIdentifier, verifier: info.verifier, verifierId: info.verifierId, typeOfLogin: info.typeOfLogin, ref: info.ref, extraVerifierParams: info.extraVerifierParams, accessToken: loginParams.accessToken, idToken: loginParams.idToken, extraParams: loginParams.extraParams, extraParamsPassed: loginParams.extraParamsPassed, state: loginParams.state) + loginParamsArray.append(loginParams) + userInfoArray.append(userInfo) + } + + var subVerifierIds: [String] = [] + var aggregateVerifierParams: [VerifyParams] = [] + var aggregateIdTokenSeeds: [String] = [] + var aggregateVerifierId: String = "" + + for i in 0 ..< args.subVerifierDetailsArray.count { + let loginParams = loginParamsArray[i] + let userInfo = userInfoArray[i] + + aggregateVerifierParams.append(VerifyParams(verifier_id: userInfo.verifierId, idtoken: loginParams.idToken!)) + aggregateIdTokenSeeds.append(loginParams.idToken ?? loginParams.accessToken!) + subVerifierIds.append(userInfo.verifier) + aggregateVerifierId = userInfo.verifierId } - throw CASDKError.unknownError + aggregateIdTokenSeeds.sort() + let joined = aggregateIdTokenSeeds.joined(separator: "\u{29}").data(using: .utf8)! + let aggregateIdToken = try keccak256(data: joined) + let aggregateParams: VerifierParams = VerifierParams(verifier_id: aggregateVerifierId, extended_verifier_id: nil, sub_verifier_ids: subVerifierIds, verify_params: aggregateVerifierParams) + + let aggregateTorusKey = try await getTorusKey(verifier: aggregateVerifierId, verifierParams: aggregateParams, idToken: String(data: aggregateIdToken, encoding: .utf8)!) + + var aggregateVerifierResponses: [TorusAggregateVerifierResponse] = [] + for i in 0 ..< userInfoArray.count { + let loginParams = loginParamsArray[i] + let userInfo = userInfoArray[i] + aggregateVerifierResponses.append(TorusAggregateVerifierResponse(userInfo: userInfo, loginResponse: loginParams)) + } + + return TorusAggregateLoginResponse(torusAggregateVerifierResponse: aggregateVerifierResponses, torusKey: aggregateTorusKey) } - - open func handleSingleIdVerifier(controller: UIViewController?, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) async throws -> TorusKeyData { - if let subVerifier = subVerifierDetails.first { - let loginURL = subVerifier.getLoginURL() - await MainActor.run(body: { - openURL(url: loginURL, view: controller, modalPresentationStyle: modalPresentationStyle) - }) - - do { - let url = try await withUnsafeThrowingContinuation { (continuation: UnsafeContinuation) in - observeCallbackWithError { url, err in - guard - err == nil, - let url = url - else { - continuation.resume(throwing: err!) - return - } - - continuation.resume(returning: url) - return - } - } - let responseParameters = self.parseURL(url: url) - os_log("ResponseParams after redirect: %@", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, responseParameters) - - let newData = try await subVerifier.getUserInfo(responseParameters: responseParameters) - var data = newData - let verifierId = data["verifierId"] as! String - let idToken = data["tokenForKeys"] as! String - data.removeValue(forKey: "tokenForKeys") - data.removeValue(forKey: "verifierId") - let aggTorusKey = try await getAggregateTorusKey(verifier: self.aggregateVerifier, verifierId: verifierId, idToken: idToken, subVerifierDetails: subVerifier, userData: newData) - var mergedUserInfo = newData.merging(responseParameters) { (_, new) in new } - mergedUserInfo["verifier"] = self.aggregateVerifier - let result = TorusKeyData(torusKey: aggTorusKey, userInfo: mergedUserInfo) - return result - } catch { - os_log("handleSingleIdVerifier err: %s", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) - throw error + + public func triggerHybridAggregateLogin(args: HybridAggregateLoginParams) async throws -> TorusHybridAggregateLoginResponse { + if args.aggregateLoginParams.subVerifierDetailsArray.isEmpty { + throw CASDKError.invalidParameters + } + if args.aggregateLoginParams.subVerifierDetailsArray.count == 1 && args.aggregateLoginParams.aggregateVerifierType == AggregateVerifierType.single_id_verifier { + throw CASDKError.invalidParameters + } + + let loginHandler = try HandlerFactory.createHandler(params: CreateHandlerParams(typeOfLogin: args.singleLogin.typeOfLogin, verifier: args.singleLogin.verifier, clientId: args.singleLogin.clientId, urlScheme: config.urlScheme, redirectURL: args.singleLogin.redirectURL, jwtParams: args.singleLogin.jwtParams, customState: args.singleLogin.customState)) + + var loginParams: LoginWindowResponse + if args.singleLogin.hash != nil && args.singleLogin.queryParams != nil { + let (error, hashParams, instanceParams) = try handleRedirectParameters(hash: args.singleLogin.hash!, queryParameters: args.singleLogin.queryParams!) + if !error.isEmpty { + throw CASDKError.redirectParamsError(msg: error) } - + loginParams = LoginWindowResponse(accessToken: hashParams.params["access_token"], idToken: hashParams.params["idToken"], ref: hashParams.params["ref"] ?? "", extraParams: hashParams.params["extra_params"], extraParamsPassed: hashParams.params["extra_params_passed"]!, state: instanceParams) + } else { + loginParams = try await loginHandler.handleLoginWindow(popupFeatures: config.popupFeatures) } - throw CASDKError.unknownError - - } - - func handleAndAggregateVerifier(controller: UIViewController?) async throws -> TorusKeyData { - // TODO: implement verifier - throw CASDKError.methodUnavailable - } - - func handleOrAggregateVerifier(controller: UIViewController?) async throws -> TorusKeyData { - // TODO: implement verifier - throw CASDKError.methodUnavailable - } - - /// Retrieve the Torus key from the nodes given an already known token. Useful if a custom login flow is required. - /// - Parameters: - /// - verifier: A verifier is a unique identifier for your OAuth registration on the torus network. The public/private keys generated for a user are scoped to a verifier. - /// - verifierId: The unique identifier to publicly represent a user on a verifier. e.g: email, sub etc. other fields can be classified as verifierId, - /// - idToken: Access token received from the OAuth provider. - /// - userData: Custom data that will be returned with `privateKey` and `publicAddress`. - /// - Returns: A promise that resolve with a Dictionary that contain at least `privateKey` and `publicAddress` field.. - open func getTorusKey(verifier: String, verifierId: String, idToken: String, userData: [String: Any] = [:]) async throws -> TorusKey { - let extraParams = ["verifier_id": verifierId] as [String: Codable] - let verifierParams = VerifierParams(verifier_id: verifierId) - do { - let nodeDetails = try await nodeDetailManager.getNodeDetails(verifier: verifier, verifierID: verifierId) - // retrieveShares internall checks if network is legacy and calls getPublicAddress if required. - let responseFromRetrieveShares : TorusKey = try await torusUtils.retrieveShares(endpoints: nodeDetails.torusNodeEndpoints, torusNodePubs: nodeDetails.torusNodePub, indexes: nodeDetails.torusIndexes, verifier: verifier, verifierParams: verifierParams, idToken: idToken, extraParams: extraParams) - - return responseFromRetrieveShares - } catch { - os_log("handleSingleLogin: err: %s", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) - throw error + + let userInfo = try await loginHandler.getUserInfo(params: loginParams, storageServerUrl: nil) + + let verifyParams: VerifierParams = VerifierParams(verifier_id: userInfo.verifierId) + + let torusKey = try await getTorusKey(verifier: userInfo.verifier, verifierParams: verifyParams, idToken: loginParams.idToken!) + + let returnedInfo = UserInfo(email: userInfo.email, name: userInfo.name, profileImage: userInfo.profileImage, aggregateVerifier: userInfo.aggregateVerifier, verifier: userInfo.verifier, verifierId: userInfo.verifierId, typeOfLogin: userInfo.typeOfLogin, ref: userInfo.ref, extraVerifierParams: userInfo.extraVerifierParams, accessToken: loginParams.accessToken, idToken: loginParams.idToken, extraParams: loginParams.extraParams, extraParamsPassed: loginParams.extraParamsPassed, state: loginParams.state) + + var subVerifierIds: [String] = [] + var aggregateVerifierParams: [VerifyParams] = [] + var aggregateIdTokenSeeds: [String] = [] + var aggregateVerifierId: String = "" + for i in 0 ..< args.aggregateLoginParams.subVerifierDetailsArray.count { + let sub = args.aggregateLoginParams.subVerifierDetailsArray[i] + aggregateVerifierParams.append(VerifyParams(verifier_id: userInfo.verifierId, idtoken: loginParams.idToken!)) + aggregateIdTokenSeeds.append(loginParams.idToken ?? loginParams.accessToken!) + subVerifierIds.append(sub.verifier) + aggregateVerifierId = userInfo.verifierId } + aggregateIdTokenSeeds.sort() + let joined = aggregateIdTokenSeeds.joined(separator: "\u{29}").data(using: .utf8)! + let aggregateIdToken = try keccak256(data: joined) + let aggregateParams: VerifierParams = VerifierParams(verifier_id: aggregateVerifierId, extended_verifier_id: nil, sub_verifier_ids: subVerifierIds, verify_params: aggregateVerifierParams) + + let aggregateTorusKey = try await getTorusKey(verifier: args.aggregateLoginParams.verifierIdentifier, verifierParams: aggregateParams, idToken: String(data: aggregateIdToken, encoding: .utf8)!) + + let aggregateResponse = TorusAggregateVerifierResponse(userInfo: returnedInfo, loginResponse: loginParams) + + let aggregateLogin = TorusAggregateLoginResponse(torusAggregateVerifierResponse: [aggregateResponse], torusKey: torusKey) + + return TorusHybridAggregateLoginResponse(singleLogin: aggregateLogin, aggregateLogins: [aggregateTorusKey]) } - - /// Retrieve the Torus key from the nodes given an already known token. Useful if a custom aggregate login flow is required. - /// - Parameters: - /// - verifier: A verifier is a unique identifier for your OAuth registration on the torus network. The public/private keys generated for a user are scoped to a verifier. - /// - verifierId: The unique identifier to publicly represent a user on a verifier. e.g: email, sub etc. other fields can be classified as verifierId, - /// - subVerifierDetails: An array of verifiers to be used for the aggregate login flow, with their respective token and verifier name. - /// - Returns: A promise that resolve with a Dictionary that contain at least `privateKey` and `publicAddress` field.. - open func getAggregateTorusKey(verifier: String, verifierId: String, idToken: String, subVerifierDetails: SubVerifierDetails, userData: [String: Any] = [:]) async throws -> TorusKey { - let extraParams = ["verifieridentifier": verifier, "verifier_id": verifierId, "sub_verifier_ids": [subVerifierDetails.verifier], "verify_params": [["verifier_id": verifierId, "idtoken": idToken]]] as [String: Codable] - let hashedOnce = try keccak256(data: Data(idToken.utf8)) - - let verifierParams = VerifierParams(verifier_id: verifierId) - - do { - let nodeDetails = try await getNodeDetailsFromContract(verifier: verifier, verfierID: verifierId) - - // retrieveShares internall checks if network is legacy and calls getPublicAddress if required. - let responseFromRetrieveShares :TorusKey = try await self.torusUtils.retrieveShares(endpoints: nodeDetails.torusNodeEndpoints, torusNodePubs: nodeDetails.torusNodePub, indexes: nodeDetails.torusIndexes, verifier: verifier, verifierParams: verifierParams, idToken: hashedOnce.toHexString(), extraParams: extraParams) - - return responseFromRetrieveShares - } catch { - os_log("handleSingleIdVerifier err: %@", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error, error.localizedDescription) - throw error + + func getTorusKey(verifier: String, verifierParams: VerifierParams, idToken: String) async throws -> TorusKey { + let nodeDetails = try await nodeDetailManager.getNodeDetails(verifier: verifier, verifierID: verifierParams.verifier_id) + + return try await torus.retrieveShares(endpoints: nodeDetails.getTorusNodeEndpoints(), verifier: verifier, verifierParams: verifierParams, idToken: idToken) + } + + func getAggregateTorusKey(verifier: String, verifierParams: VerifierParams, subVerifierInfoArray: [TorusSubVerifierInfo]) async throws -> TorusKey { + let nodeDetails = try await nodeDetailManager.getNodeDetails(verifier: verifier, verifierID: verifierParams.verifier_id) + + var aggregateIdTokenSeeds: [String] = [] + var subVerifierIds: [String] = [] + + var verifyParams: [VerifyParams] = [] + for i in 0 ..< subVerifierInfoArray.count { + let userInfo = subVerifierInfoArray[i] + verifyParams.append(VerifyParams(verifier_id: verifierParams.verifier_id, idtoken: userInfo.idToken)) + subVerifierIds.append(userInfo.verifier) + aggregateIdTokenSeeds.append(userInfo.idToken) } + + aggregateIdTokenSeeds.sort() + let joined = aggregateIdTokenSeeds.joined(separator: "\u{29}").data(using: .utf8)! + let aggregateIdToken = try keccak256(data: joined) + let params: VerifierParams = VerifierParams(verifier_id: verifierParams.verifier_id, extended_verifier_id: verifierParams.extended_verifier_id, sub_verifier_ids: subVerifierIds, verify_params: verifyParams) + + return try await torus.retrieveShares(endpoints: nodeDetails.getTorusNodeEndpoints(), verifier: verifier, verifierParams: params, idToken: String(data: aggregateIdToken, encoding: .utf8)!) } } diff --git a/Sources/CustomAuth/CustomAuthArgs.swift b/Sources/CustomAuth/CustomAuthArgs.swift new file mode 100644 index 0000000..f7a0879 --- /dev/null +++ b/Sources/CustomAuth/CustomAuthArgs.swift @@ -0,0 +1,28 @@ +import FetchNodeDetails +import Foundation + +public class CustomAuthArgs { + public let urlScheme: String + public let metadataUrl: String? + public let network: TorusNetwork + public let enableLogging: Bool + public let enableOneKey: Bool + public let apiKey: String? + public let popupFeatures: String? + public let storageServerUrl: String? + public let web3AuthClientId: String + public let serverTimeOffset: Int + + public init(urlScheme: String, metadataUrl: String? = nil, network: TorusNetwork, enableLogging: Bool = false, enableOneKey: Bool, apiKey: String? = nil, popupFeatures: String? = nil, storageServerUrl: String? = nil, web3AuthClientId: String, serverTimeOffset: Int = 0, legacyMetadataHost: String? = nil) { + self.urlScheme = urlScheme + self.metadataUrl = metadataUrl + self.network = network + self.enableLogging = enableLogging + self.enableOneKey = enableOneKey + self.apiKey = apiKey + self.popupFeatures = popupFeatures + self.storageServerUrl = storageServerUrl + self.web3AuthClientId = web3AuthClientId + self.serverTimeOffset = serverTimeOffset + } +} diff --git a/Sources/CustomAuth/CustomAuthFactory.swift b/Sources/CustomAuth/CustomAuthFactory.swift deleted file mode 100644 index c77151c..0000000 --- a/Sources/CustomAuth/CustomAuthFactory.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// File.swift -// -// -// Created by Shubham on 30/7/21. -// - -import FetchNodeDetails -import Foundation -import OSLog -import TorusUtils - -/// A protocol should be implmented by users of `CustomAuth`. It provides a way -/// to stub or mock the CustomAuth for testing. -public protocol CASDKFactoryProtocol { - func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils -// func createFetchNodeDetails(network: TorusNetwork, urlSession: URLSession, networkUrl: String?) -> AllNodeDetailsModel -} - -//public class CASDKFactory: CASDKFactoryProtocol { -// public func createFetchNodeDetails(network: TorusNetwork, urlSession: URLSession = URLSession.shared, networkUrl: String? = nil) -> FetchNodeDetails { -// var proxyAddress: String = "" -// switch network { -// case .MAINNET: -// proxyAddress = FetchNodeDetails.proxyAddressMainnet -// case .TESTNET: -// proxyAddress = FetchNodeDetails.proxyAddressTestnet -// case .CYAN: -// proxyAddress = FetchNodeDetails.proxyAddressCyan -// case .AQUA: -// proxyAddress = FetchNodeDetails.proxyAddressAqua -// case .CELESTE: -// proxyAddress = FetchNodeDetails.proxyAddressCeleste -// default: -// proxyAddress = FetchNodeDetails.proxyAddressMainnet -// } -// guard let networkUrl = networkUrl else { return FetchNodeDetails(proxyAddress: proxyAddress, network: network, urlSession: urlSession) } -// return FetchNodeDetails(proxyAddress: proxyAddress, network: .CUSTOM(path: networkUrl), urlSession: urlSession) -// } -// -// public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession = URLSession.shared, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils { -// let allowHost = network.signerMap.appending("/api/allow") -// let signerHost = network.signerMap.appending("/api/sign") -// return TorusUtils(loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, signerHost: signerHost, allowHost: allowHost, network: network) -// } -// -// public init() { -// } -//} diff --git a/Sources/CustomAuth/Extension/CASDK+extension.swift b/Sources/CustomAuth/Extension/CASDK+extension.swift deleted file mode 100644 index e2d7e69..0000000 --- a/Sources/CustomAuth/Extension/CASDK+extension.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// CustomAuth class -// CustomAuth -// -// Created by Shubham Rathi on 18/05/2020. -// - -import Foundation -import OSLog -import SafariServices -import TorusUtils - -typealias torus = CustomAuth - -// MARK: - verifier types - -public enum verifierTypes: String { - case singleLogin = "single_login" - case singleIdVerifier = "single_id_verifier" - case andAggregateVerifier = "and_aggregate_verifier" - case orAggregateVerifier = "or_aggregate_verifier" -} - -// MARK: - torus extension -extension CustomAuth { - - public class var notificationCenter: NotificationCenter { - return NotificationCenter.default - } - - public class var notificationQueue: OperationQueue { - return OperationQueue.main - } - - static let didHandleCallbackURL: Notification.Name = .init("TSDSDKCallbackNotification") - - public func removeCallbackNotificationObserver() { - if let observer = observer { - CustomAuth.notificationCenter.removeObserver(observer) - } - } - - public func observeCallback(_ block: @escaping (_ url: URL) -> Void) { - self.observer = CustomAuth.notificationCenter.addObserver( - forName: CustomAuth.didHandleCallbackURL, - object: nil, - queue: OperationQueue.main) { [weak self] notification in - self?.removeCallbackNotificationObserver() - os_log("notification.userInfo: %s", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, notification.userInfo.debugDescription) - if let urlFromUserInfo = notification.userInfo?["URL"] as? URL { - os_log("executing callback block", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error) - block(urlFromUserInfo) - } else { - assertionFailure() - } - } - } - - - public func observeCallbackWithError(_ block: @escaping (_ url: URL?, _ err: String?) -> Void) { - self.observer = CustomAuth.notificationCenter.addObserver( - forName: CustomAuth.didHandleCallbackURL, - object: nil, - queue: OperationQueue.main) { [weak self] notification in - self?.removeCallbackNotificationObserver() - os_log("notification.userInfo: %s", log: getTorusLogger(log: CASDKLogger.core, type: - .info), type: .info, notification.userInfo.debugDescription) - if let errorFromUserInfo = notification.userInfo?["ERROR"] as? String { - os_log("executing error callback block", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error) - block(nil, errorFromUserInfo) - } - else if let urlFromUserInfo = notification.userInfo?["URL"] as? URL { - os_log("executing callback block", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .info) - block(urlFromUserInfo, nil) - - } else { - assertionFailure() - } - } - } - - @MainActor public func openURL(url: String, view: UIViewController?, modalPresentationStyle: UIModalPresentationStyle) { - os_log("opening URL: %s", log: getTorusLogger(log: CASDKLogger.core, type: .info), type: .info, url) - switch authorizeURLHandler { - case .external: - let handler = ExternalURLHandler() - handler.handle(URL(string: url)!, modalPresentationStyle: modalPresentationStyle) - case .sfsafari: - guard let controller = view else { - os_log("UIViewController not available. Please modify triggerLogin(controller:)", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error) - return - } - let handler = SFURLHandler(viewController: controller) - handler.handle(URL(string: url)!, modalPresentationStyle: modalPresentationStyle) - case .asWebAuthSession: - let handler = ASWebAuthSession(redirectURL: self.subVerifierDetails.first?.redirectURL ?? "") - handler.handle(URL(string: url)!, modalPresentationStyle: modalPresentationStyle) - case .none: - os_log("Cannot access specified browser", log: getTorusLogger(log: CASDKLogger.core, type: .error), type: .error) - } - } - - func makeUrlRequest(url: String, method: String) -> URLRequest { - var rq = URLRequest(url: URL(string: url)!) - rq.httpMethod = method - rq.addValue("application/json", forHTTPHeaderField: "Content-Type") - rq.addValue("application/json", forHTTPHeaderField: "Accept") - return rq - } - - public class func handle(url: URL) { - // CustomAuth.logger.info("Posting notification after Universal link/deep link flow") - let notification = Notification(name: CustomAuth.didHandleCallbackURL, object: nil, userInfo: ["URL": url]) - notificationCenter.post(notification) - } - - public class func handleError(err: String) { - // CustomAuth.logger.info("Posting notification after Universal link/deep link flow") - let notification = Notification(name: CustomAuth.didHandleCallbackURL, object: nil, userInfo: ["ERROR": err]) - notificationCenter.post(notification) - } - - public func parseURL(url: URL) -> [String: String] { - var responseParameters = [String: String]() - if let query = url.query { - responseParameters += query.parametersFromQueryString - } - if let fragment = url.fragment, !fragment.isEmpty { - responseParameters += fragment.parametersFromQueryString - } - return responseParameters - } - - // Run on main block - static func main(block: @escaping () -> Void) { - if Thread.isMainThread { - block() - } else { - DispatchQueue.main.async { - block() - } - } - } -} diff --git a/Sources/CustomAuth/Extension/Data+extension.swift b/Sources/CustomAuth/Extension/Data+extension.swift new file mode 100644 index 0000000..2b62938 --- /dev/null +++ b/Sources/CustomAuth/Extension/Data+extension.swift @@ -0,0 +1,11 @@ +import Foundation + +extension Data { + func toBase64URL() -> String { + var result = base64EncodedString() + result = result.replacingOccurrences(of: "+", with: "-") + result = result.replacingOccurrences(of: "/", with: "_") + result = result.replacingOccurrences(of: "=", with: "") + return result + } +} diff --git a/Sources/CustomAuth/Extension/Dictionary.swift b/Sources/CustomAuth/Extension/Dictionary.swift deleted file mode 100644 index ec7c313..0000000 --- a/Sources/CustomAuth/Extension/Dictionary.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// File.swift -// -// -// Created by Shubham on 29/4/20. -// - -import Foundation - -extension Dictionary { - func join(_ other: Dictionary) -> Dictionary { - var joinedDictionary = Dictionary() - - for (key, value) in self { - joinedDictionary.updateValue(value, forKey: key) - } - - for (key, value) in other { - joinedDictionary.updateValue(value, forKey: key) - } - - return joinedDictionary - } - - var urlEncodedQuery: String { - var parts = [String]() - - for (key, value) in self { - let keyString = "\(key)".urlEncoded - let valueString = "\(value)".urlEncoded - let query = "\(keyString)=\(valueString)" - parts.append(query) - } - - return parts.joined(separator: "&") - } - - mutating func merge(_ dictionaries: [K: V]...) { - for dict in dictionaries { - for (key, value) in dict { - if let v = value as? Value, let k = key as? Key { - updateValue(v, forKey: k) - } - } - } - } - - func map(_ transform: (Key, Value) -> (K, V)) -> [K: V] { - var results: [K: V] = [:] - for k in keys { - if let value = self[k] { - let (u, w) = transform(k, value) - results.updateValue(w, forKey: u) - } - } - return results - } -} - -// -// extension Dictionary { -// @available(swift, introduced: 3.2, obsoleted: 4.0) -// public func filter(_ isIncluded: (Key, Value) throws -> Bool) rethrows -> [Key: Value] { -// var resultDictionary = [Key: Value](minimumCapacity: count) -// for (key, value) in self { -// if try isIncluded(key, value) { -// resultDictionary[key] = value -// } -// } -// return resultDictionary -// } -// } - -func += (left: inout [K: V], right: [K: V]) { left.merge(right) } -func + (left: [K: V], right: [K: V]) -> [K: V] { return left.join(right) } -func += (left: inout [K: V]?, right: [K: V]) { - if left != nil { left?.merge(right) } else { left = right } -} diff --git a/Sources/CustomAuth/Extension/OSLog.swift b/Sources/CustomAuth/Extension/OSLog.swift deleted file mode 100644 index 17f6127..0000000 --- a/Sources/CustomAuth/Extension/OSLog.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// Created by Michael Lee on 13/9/2021. -// - -import Foundation -import os.log - -public let subsystem = Bundle.main.bundleIdentifier ?? "com.torus.CustomAuth" -// -// typealias OriginalOSLogFunction = (_ message: StaticString, _ dso: UnsafeRawPointer? , _ log: OSLog , _ type: OSLogType , _ args: CVarArg...) -> Void -// -// typealias ConvertedOSLogFunction = (_ message: StaticString, _ dso: UnsafeRawPointer? , _ log: OSLog , _ type: OSLogType , _ args: [CVarArg]) -> Void -// -// let os_logv = unsafeBitCast(os_log as OriginalOSLogFunction, to: ConvertedOSLogFunction.self) - -public struct CASDKLogger { - static let inactiveLog = OSLog.disabled - static let core = OSLog(subsystem: subsystem, category: "core") -} - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -func getTorusLogger(log: OSLog = .default, type: OSLogType = .default) -> OSLog { - var logCheck: OSLog { tsSdkLogType.rawValue <= type.rawValue ? log : CASDKLogger.inactiveLog } - return logCheck -} - -// @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -// func log(_ message: StaticString, dso: UnsafeRawPointer? = #dsohandle, log: OSLog = .default, type: OSLogType = .default, _ args: CVarArg...){ -// var logCheck: OSLog { tsSdkLogType.rawValue <= type.rawValue ? log : TDSDKLogger.inactiveLog} -// os_logv(message, dso, logCheck, type, args) -// } diff --git a/Sources/CustomAuth/Extension/String+extension.swift b/Sources/CustomAuth/Extension/String+extension.swift new file mode 100644 index 0000000..afddc9f --- /dev/null +++ b/Sources/CustomAuth/Extension/String+extension.swift @@ -0,0 +1,60 @@ +import Foundation + +extension String { + func fromBase64URL() throws -> String { + var base64 = self + base64 = base64.replacingOccurrences(of: "-", with: "+") + base64 = base64.replacingOccurrences(of: "_", with: "/") + while base64.count % 4 != 0 { + base64 = base64.appending("=") + } + guard let data = Data(base64Encoded: base64) else { + throw CASDKError.decodingFailed + } + return String(data: data, encoding: .utf8)! + } + + var parametersFromQueryString: [String: String] { + return dictionaryBySplitting("&", keyValueSeparator: "=") + } + + func dictionaryBySplitting(_ elementSeparator: String, keyValueSeparator: String) -> [String: String] { + var string = self + + if hasPrefix(elementSeparator) { + string = String(dropFirst(1)) + } + + var parameters = [String: String]() + + let scanner = Scanner(string: string) + + while !scanner.isAtEnd { + let key = scanner.scanUpToString(keyValueSeparator) + _ = scanner.scanString(keyValueSeparator) + + let value = scanner.scanUpToString(elementSeparator) + _ = scanner.scanString(elementSeparator) + + if let key = key { + if let value = value { + if key.contains(elementSeparator) { + var keys = key.components(separatedBy: elementSeparator) + if let key = keys.popLast() { + parameters.updateValue(value, forKey: String(key)) + } + for flag in keys { + parameters.updateValue("", forKey: flag) + } + } else { + parameters.updateValue(value, forKey: key) + } + } else { + parameters.updateValue("", forKey: key) + } + } + } + + return parameters + } +} diff --git a/Sources/CustomAuth/Extension/String.swift b/Sources/CustomAuth/Extension/String.swift deleted file mode 100644 index 78bd290..0000000 --- a/Sources/CustomAuth/Extension/String.swift +++ /dev/null @@ -1,156 +0,0 @@ -import Foundation - -extension String { - var parametersFromQueryString: [String: String] { - return dictionaryBySplitting("&", keyValueSeparator: "=") - } - - /// Encodes url string making it ready to be passed as a query parameter. This encodes pretty much everything apart from - /// alphanumerics and a few other characters compared to standard query encoding. - var urlEncoded: String { - let customAllowedSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~") - return addingPercentEncoding(withAllowedCharacters: customAllowedSet)! - } - - var urlQueryEncoded: String? { - return addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) - } - - /// Returns new url query string by appending query parameter encoding it first, if specified. - func urlQueryByAppending(parameter name: String, value: String, encode: Bool = true, _ encodeError: ((String, String) -> Void)? = nil) -> String? { - if value.isEmpty { - return self - } else if let value = encode ? value.urlQueryEncoded : value { - return "\(self)\(isEmpty ? "" : "&")\(name)=\(value)" - } else { - encodeError?(name, value) - return nil - } - } - - /// Returns new url string by appending query string at the end. - func urlByAppending(query: String) -> String { - return "\(self)\(contains("?") ? "&" : "?")\(query)" - } - - fileprivate func dictionaryBySplitting(_ elementSeparator: String, keyValueSeparator: String) -> [String: String] { - var string = self - - if hasPrefix(elementSeparator) { - string = String(dropFirst(1)) - } - - var parameters = [String: String]() - - let scanner = Scanner(string: string) - - while !scanner.isAtEnd { - if #available(iOS 13.0, tvOS 13.0, OSX 10.15, watchOS 6.0, *) { - let key = scanner.scanUpToString(keyValueSeparator) - _ = scanner.scanString(keyValueSeparator) - - let value = scanner.scanUpToString(elementSeparator) - _ = scanner.scanString(elementSeparator) - - if let key = key { - if let value = value { - if key.contains(elementSeparator) { - var keys = key.components(separatedBy: elementSeparator) - if let key = keys.popLast() { - parameters.updateValue(value, forKey: String(key)) - } - for flag in keys { - parameters.updateValue("", forKey: flag) - } - } else { - parameters.updateValue(value, forKey: key) - } - } else { - parameters.updateValue("", forKey: key) - } - } - } else { - var key: NSString? - scanner.scanUpTo(keyValueSeparator, into: &key) - scanner.scanString(keyValueSeparator, into: nil) - - var value: NSString? - scanner.scanUpTo(elementSeparator, into: &value) - scanner.scanString(elementSeparator, into: nil) - if let key = key as String? { - if let value = value as String? { - if key.contains(elementSeparator) { - var keys = key.components(separatedBy: elementSeparator) - if let key = keys.popLast() { - parameters.updateValue(value, forKey: String(key)) - } - for flag in keys { - parameters.updateValue("", forKey: flag) - } - } else { - parameters.updateValue(value, forKey: key) - } - } else { - parameters.updateValue("", forKey: key) - } - } - } - } - - return parameters - } - - static func randomString(length: Int) -> String { - let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - return String((0 ..< length).map { _ in letters.randomElement()! }) - } - - func fromBase64URL() -> String? { - var base64 = self - base64 = base64.replacingOccurrences(of: "-", with: "+") - base64 = base64.replacingOccurrences(of: "_", with: "/") - while base64.count % 4 != 0 { - base64 = base64.appending("=") - } - guard let data = Data(base64Encoded: base64) else { - return nil - } - return String(data: data, encoding: .utf8) - } - - func toBase64URL() -> String { - var result = Data(utf8).base64EncodedString() - result = result.replacingOccurrences(of: "+", with: "-") - result = result.replacingOccurrences(of: "/", with: "_") - result = result.replacingOccurrences(of: "=", with: "") - return result - } - - var safeStringByRemovingPercentEncoding: String { - return self.removingPercentEncoding ?? self - } - - mutating func dropLast() { - remove(at: index(before: endIndex)) - } - - subscript(bounds: CountableClosedRange) -> String { - let start = index(startIndex, offsetBy: bounds.lowerBound) - let end = index(startIndex, offsetBy: bounds.upperBound) - return String(self[start ... end]) - } - - subscript(bounds: CountableRange) -> String { - let start = index(startIndex, offsetBy: bounds.lowerBound) - let end = index(startIndex, offsetBy: bounds.upperBound) - return String(self[start ..< end]) - } -} - -extension String.Encoding { - var charset: String { - let charset = CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(rawValue)) - // swiftlint:disable:next force_cast superfluous_disable_command - return charset! as String - } -} diff --git a/Sources/CustomAuth/Extension/URLComponents+extension.swift b/Sources/CustomAuth/Extension/URLComponents+extension.swift new file mode 100644 index 0000000..f223d36 --- /dev/null +++ b/Sources/CustomAuth/Extension/URLComponents+extension.swift @@ -0,0 +1,7 @@ +import Foundation + +extension URLComponents { + mutating func setQueryItems(with parameters: [String: String]) { + queryItems = parameters.map { URLQueryItem(name: $0.key, value: $0.value) } + } +} diff --git a/Sources/CustomAuth/Handlers/AuthenticationManager.swift b/Sources/CustomAuth/Handlers/AuthenticationManager.swift new file mode 100644 index 0000000..4fb3709 --- /dev/null +++ b/Sources/CustomAuth/Handlers/AuthenticationManager.swift @@ -0,0 +1,52 @@ +import AuthenticationServices +#if os(macOS) + import AppKit +#else + import UIKit +#endif + +public class AuthenticationManager: NSObject, ASWebAuthenticationPresentationContextProviding { + public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + var window: ASPresentationAnchor? + #if os(macOS) + window = NSApplication.shared.windows.first { $0.isKeyWindow } + #else + window = UIApplication.shared.windows.first { $0.isKeyWindow } + #endif + + return window ?? ASPresentationAnchor() + } + + func webAuth(url: URL, callbackURLScheme: String, prefersEphemeralWebBrowserSession: Bool, + completion: @escaping (Result) -> Void) { + let authSession = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) { url, error in + if let error = error { + completion(.failure(error)) + } else if let url = url { + completion(.success(url)) + } + } + + DispatchQueue.main.async { [weak self] in + guard let self = self else { + return + } + authSession.presentationContextProvider = self + authSession.prefersEphemeralWebBrowserSession = prefersEphemeralWebBrowserSession + authSession.start() + } + } + + public func authenticationManagerWrapper(url: URL, callbackURLScheme: String, prefersEphemeralWebBrowserSession: Bool) async throws -> URL { + return try await withCheckedThrowingContinuation { continuation in + webAuth(url: url, callbackURLScheme: callbackURLScheme, prefersEphemeralWebBrowserSession: prefersEphemeralWebBrowserSession) { result in + switch result { + case let .success(url): + continuation.resume(returning: url) + case let .failure(error): + continuation.resume(throwing: error) + } + } + } + } +} diff --git a/Sources/CustomAuth/Handlers/DiscordLoginHandler.swift b/Sources/CustomAuth/Handlers/DiscordLoginHandler.swift index 7a67eb0..6604daf 100644 --- a/Sources/CustomAuth/Handlers/DiscordLoginHandler.swift +++ b/Sources/CustomAuth/Handlers/DiscordLoginHandler.swift @@ -1,80 +1,60 @@ -// -// DiscordLoginHandler.swift -// -// -// Created by Shubham on 13/11/20. -// - import Foundation -class DiscordLoginHandler: AbstractLoginHandler { - let loginType: SubVerifierType - let clientID: String - let redirectURL: String - let browserRedirectURL: String? - let state: String - var userInfo: [String: Any]? - let nonce = String.randomString(length: 10) - let jwtParams: [String: String] - let defaultParams: [String: String] - var urlSession: URLSession +class DiscordInfo: Codable { + public var id: String + public var name: String + public var avatar: String? + public var discriminator: String + public var email: String +} - public init(loginType: SubVerifierType = .web, clientID: String, redirectURL: String, browserRedirectURL: String?, jwtParams: [String: String] = [:], urlSession: URLSession = URLSession.shared) { - self.loginType = loginType - self.clientID = clientID - self.redirectURL = redirectURL - self.jwtParams = jwtParams - self.browserRedirectURL = browserRedirectURL - defaultParams = ["scope": "email identify", "response_type": "token"] - self.urlSession = urlSession +class DiscordLoginHandler: AbstractLoginHandler { + private var response_type: String = "token" + private var scope: String = "identify email" - let tempState = ["nonce": nonce, "redirectUri": self.redirectURL, "redirectToAndroid": "true"] - let jsonData = try! JSONSerialization.data(withJSONObject: tempState, options: .prettyPrinted) - state = String(data: jsonData, encoding: .utf8)!.toBase64URL() + override public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + try super.init(clientId: clientId, verifier: verifier, urlScheme: urlScheme, redirectURL: redirectURL, typeOfLogin: typeOfLogin, jwtParams: jwtParams, customState: customState) + try setFinalUrl() } - func getUserInfo(responseParameters: [String: String]) async throws -> [String: Any] { - return try await handleLogin(responseParameters: responseParameters) - } + override public func setFinalUrl() throws { + var urlComponents = URLComponents() - func getLoginURL() -> String { - // left join - var tempParams = defaultParams - tempParams.merge(["redirect_uri": browserRedirectURL ?? redirectURL, "client_id": clientID, "state": state]) { _, new in new } - tempParams.merge(jwtParams) { _, new in new } + var params: [String: String] = [:] - // Reconstruct URL - var urlComponents = URLComponents() + if jwtParams != nil { + params = try (JSONSerialization.jsonObject(with: try JSONEncoder().encode(jwtParams), options: []) as! [String: String]) + } + + params.merge([ + "state": try state(), + "response_type": response_type, + "client_id": clientId, + "redirect_uri": redirectURL, + "scope": scope], uniquingKeysWith: { _, new in new }) urlComponents.scheme = "https" urlComponents.host = "discord.com" urlComponents.path = "/api/oauth2/authorize" - urlComponents.setQueryItems(with: tempParams) - - return urlComponents.url!.absoluteString - // return "https://discord.com/api/oauth2/authorize?response_type=token" + "&client_id=\(self.clientId)&scope=email identify&redirect_uri=\(newRedirectURL)".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)! - } + urlComponents.setQueryItems(with: params) - func getVerifierFromUserInfo() -> String { - return userInfo?["id"] as? String ?? "" + finalUrl = urlComponents } - func handleLogin(responseParameters: [String: String]) async throws -> [String: Any] { - if let accessToken = responseParameters["access_token"] { - var request = makeUrlRequest(url: "https://discordapp.com/api/users/@me", method: "GET") - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - do { - let val = try await urlSession.data(for: request) - let data = try JSONSerialization.jsonObject(with: val.0) as? [String: Any] ?? [:] - userInfo = data - var newData: [String: Any] = ["userInfo": userInfo as Any] - newData["tokenForKeys"] = accessToken - newData["verifierId"] = getVerifierFromUserInfo() - return newData - } catch { - throw CASDKError.getUserInfoFailed - } - } else { + override public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + guard let accessToken = params.accessToken else { throw CASDKError.accessTokenNotProvided } + + var urlRequest = makeUrlRequest(url: "https://discord.com/api/users/@me", method: "GET") + urlRequest.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") + + let (data, _) = try await URLSession.shared.data(for: urlRequest) + + let result = try JSONDecoder().decode(DiscordInfo.self, from: data) + + let profileImage = result.avatar == nil ? "https://cdn.discordapp.com/embed/avatars/" + String(Int(result.discriminator)! % 5) + ".png" : + "https://cdn.discordapp.com/avatars/${id}/" + result.avatar! + ".png?size=2048" + + return TorusVerifierResponse(email: result.email, name: result.name + "#" + result.discriminator, profileImage: profileImage, verifier: verifier, verifierId: result.id, typeOfLogin: typeOfLogin) } } diff --git a/Sources/CustomAuth/Handlers/FacebookLoginHandler.swift b/Sources/CustomAuth/Handlers/FacebookLoginHandler.swift index 7d8a51f..503c8ba 100644 --- a/Sources/CustomAuth/Handlers/FacebookLoginHandler.swift +++ b/Sources/CustomAuth/Handlers/FacebookLoginHandler.swift @@ -1,82 +1,56 @@ -// -// GoogleLoginHandler.swift -// -// -// Created by Shubham on 13/11/20. -// - import Foundation -class FacebookLoginHandler: AbstractLoginHandler { - let loginType: SubVerifierType - let clientID: String - let redirectURL: String - let browserRedirectURL: String? - let nonce = String.randomString(length: 10) - let state: String - var userInfo: [String: Any]? - let jwtParams: [String: String] - let defaultParams: [String: String] - var urlSession: URLSession +class FacebookInfo: Codable { + public var id: String + public var username: String + public var profileImage: String + public var email: String +} - public init(loginType: SubVerifierType = .web, clientID: String, redirectURL: String, browserRedirectURL: String?, jwtParams: [String: String] = [:], urlSession: URLSession = URLSession.shared) { - self.loginType = loginType - self.clientID = clientID - self.redirectURL = redirectURL - self.jwtParams = jwtParams - self.browserRedirectURL = browserRedirectURL - self.defaultParams = ["scope": "public_profile email", "response_type": "token"] - self.urlSession = urlSession +class FacebookLoginHandler: AbstractLoginHandler { + private var response_type: String = "token" + private var scope: String = "public_profile email" - let tempState = ["nonce": self.nonce, "redirectUri": self.redirectURL, "redirectToAndroid": "true"] - let jsonData = try! JSONSerialization.data(withJSONObject: tempState, options: .prettyPrinted) - self.state = String(data: jsonData, encoding: .utf8)!.toBase64URL() + override public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + try super.init(clientId: clientId, verifier: verifier, urlScheme: urlScheme, redirectURL: redirectURL, typeOfLogin: typeOfLogin, jwtParams: jwtParams, customState: customState) + try setFinalUrl() } - func getUserInfo(responseParameters: [String: String]) async throws -> [String: Any] { - return try await self.handleLogin(responseParameters: responseParameters) - } + override public func setFinalUrl() throws { + var urlComponents = URLComponents() - func getLoginURL() -> String { - // left join - var tempParams = self.defaultParams - tempParams.merge(["redirect_uri": self.browserRedirectURL ?? self.redirectURL, "client_id": self.clientID, "state": self.state]) {(_, new ) in new} - tempParams.merge(self.jwtParams) {(_, new ) in new} + var params: [String: String] = [:] - // Reconstruct URL - var urlComponents = URLComponents() + if jwtParams != nil { + params = try (JSONSerialization.jsonObject(with: try JSONEncoder().encode(jwtParams), options: []) as! [String: String]) + } + + params.merge([ + "state": try state(), + "response_type": response_type, + "client_id": clientId, + "redirect_uri": redirectURL, + "scope": scope], uniquingKeysWith: { _, new in new }) urlComponents.scheme = "https" urlComponents.host = "www.facebook.com" - urlComponents.path = "/v6.0/dialog/oauth" - urlComponents.setQueryItems(with: tempParams) + urlComponents.path = "/v15.0/dialog/oauth" + urlComponents.setQueryItems(with: params) - return urlComponents.url!.absoluteString - // return "https://www.facebook.com/v6.0/dialog/oauth?response_type=token&client_id=\(self.clientId)" + "&state=random&scope=public_profile email&redirect_uri=\(newRedirectURL)".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)! + finalUrl = urlComponents } - func getVerifierFromUserInfo() -> String { - return self.userInfo?["id"] as? String ?? "" - } - - func handleLogin(responseParameters: [String: String]) async throws -> [String: Any] { - - if let accessToken = responseParameters["access_token"] { - var request = makeUrlRequest(url: "https://graph.facebook.com/me?fields=name,email,picture.type(large)", method: "GET") - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - do { - let val = try await self.urlSession.data(for: request) - let data = try JSONSerialization.jsonObject(with: val.0) as? [String: Any] ?? [:] - self.userInfo = data - var newData: [String: Any] = ["userInfo": self.userInfo as Any] - newData["tokenForKeys"] = accessToken - newData["verifierId"] = self.getVerifierFromUserInfo() - return newData - } catch { - throw CASDKError.getUserInfoFailed - } - } else { + override public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + guard let accessToken = params.accessToken else { throw CASDKError.accessTokenNotProvided } - } + var urlRequest = makeUrlRequest(url: "https://graph.facebook.com/me?fields=name,email,picture.type(large)", method: "GET") + urlRequest.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") + + let (data, _) = try await URLSession.shared.data(for: urlRequest) + + let result = try JSONDecoder().decode(FacebookInfo.self, from: data) + + return TorusVerifierResponse(email: result.email, name: result.username, profileImage: result.profileImage, verifier: verifier, verifierId: result.id, typeOfLogin: typeOfLogin) + } } diff --git a/Sources/CustomAuth/Handlers/GoogleLoginHandler.swift b/Sources/CustomAuth/Handlers/GoogleLoginHandler.swift index 6d10fe7..d3d748c 100644 --- a/Sources/CustomAuth/Handlers/GoogleLoginHandler.swift +++ b/Sources/CustomAuth/Handlers/GoogleLoginHandler.swift @@ -1,123 +1,60 @@ -// -// GoogleLoginHandler.swift -// -// -// Created by Shubham on 13/11/20. -// - import Foundation -class GoogleloginHandler: AbstractLoginHandler { - let loginType: SubVerifierType - let clientID: String - let redirectURL: String - let browserRedirectURL: String? - var userInfo: [String: Any]? - let nonce = String.randomString(length: 10) - let state: String - let jwtParams: [String: String] - let defaultParams: [String: String] - var urlSession: URLSession +class GoogleInfo: Codable { + public var name: String + public var picture: String + public var email: String +} - public init(loginType: SubVerifierType = .web, clientID: String, redirectURL: String, browserRedirectURL: String?, jwtParams: [String: String] = [:], urlSession: URLSession = URLSession.shared) { - self.loginType = loginType - self.clientID = clientID - self.redirectURL = redirectURL - self.jwtParams = jwtParams - self.browserRedirectURL = browserRedirectURL - self.defaultParams = ["nonce": nonce, "scope": "profile+email+openid"] - self.urlSession = urlSession +class GoogleLoginHandler: AbstractLoginHandler { + private var response_type: String = "token id_token" + private var scope: String = "profile email openid" + private var prompt: String = "select_account" - let tempState = ["nonce": self.nonce, "redirectUri": self.redirectURL, "redirectToAndroid": "true"] - let jsonData = try! JSONSerialization.data(withJSONObject: tempState, options: .prettyPrinted) - self.state = String(data: jsonData, encoding: .utf8)!.toBase64URL() + override public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + try super.init(clientId: clientId, verifier: verifier, urlScheme: urlScheme, redirectURL: redirectURL, typeOfLogin: typeOfLogin, jwtParams: jwtParams, customState: customState) + try setFinalUrl() } - func getUserInfo(responseParameters: [String: String]) async throws -> [String: Any] { - return try await self.handleLogin(responseParameters: responseParameters) - } + override public func setFinalUrl() throws { + var urlComponents = URLComponents() - func getLoginURL() -> String { - // handling different OAuth applications - let googleResponseType: String - switch self.loginType { - case .installed: googleResponseType = "code" - case .web: googleResponseType = "id_token+token" - } + var params: [String: String] = [:] - // left join - var tempParams = self.defaultParams - tempParams.merge(["redirect_uri": self.browserRedirectURL ?? self.redirectURL, "client_id": self.clientID, "response_type": googleResponseType, "state": self.state]) {(_, new ) in new} - tempParams.merge(self.jwtParams) {(_, new ) in new} + if jwtParams != nil { + params = try (JSONSerialization.jsonObject(with: try JSONEncoder().encode(jwtParams), options: []) as! [String: String]) + } - // Reconstruct URL - var urlComponents = URLComponents() + params.merge([ + "state": try state(), + "response_type": response_type, + "client_id": clientId, + "prompt": prompt, + "redirect_uri": redirectURL, + "scope": scope, + "nonce": nonce, + ], uniquingKeysWith: { _, new in new }) urlComponents.scheme = "https" urlComponents.host = "accounts.google.com" urlComponents.path = "/o/oauth2/v2/auth" - urlComponents.setQueryItems(with: tempParams) - return urlComponents.url!.absoluteString -// return "https://accounts.google.com/o/oauth2/v2/auth?response_type=\(googleResponseType)&client_id=\(self.clientID)&nonce=123&redirect_uri=\(self.redirectURL)&scope=profile+email+openid" - } + urlComponents.setQueryItems(with: params) - func getVerifierFromUserInfo() -> String { - return self.userInfo?["email"] as? String ?? "" + finalUrl = urlComponents } - func handleLogin(responseParameters: [String: String]) async throws -> [String: Any] { + override public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + guard let accessToken = params.accessToken else { + throw CASDKError.accessTokenNotProvided + } + + var urlRequest = makeUrlRequest(url: "https://www.googleapis.com/userinfo/v2/me", method: "GET") + urlRequest.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - switch self.loginType { - case .installed: - var request: URLRequest = makeUrlRequest(url: "https://oauth2.googleapis.com/token", method: "POST") - var data: Data - if let code = responseParameters["code"] { - request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") - data = "grant_type=authorization_code&redirect_uri=\(self.redirectURL)&client_id=\(self.clientID)&code=\(code)".data(using: .utf8)! + let (data, _) = try await URLSession.shared.data(for: urlRequest) - request.httpBody = data - // Send request to retreive access token and id_token - do { - let val = try await self.urlSession.data(for: request) - let valData = try JSONSerialization.jsonObject(with: val.0) as? [String: Any] ?? [:] - // Retreive user info - if let accessToken = valData["access_token"], let idToken = valData["id_token"] { - var request = makeUrlRequest(url: "https://www.googleapis.com/userinfo/v2/me", method: "GET") - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - let val2 = try await urlSession.data(for: request) - let data = val2.0 - let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] - self.userInfo = dictionary - var newData: [String: Any] = ["userInfo": self.userInfo as Any] - newData["tokenForKeys"] = idToken - newData["verifierId"] = self.getVerifierFromUserInfo() - return newData - } else { - throw CASDKError.accessTokenNotProvided - } - } catch { - throw CASDKError.accessTokenAPIFailed - } - } else { - throw CASDKError.authGrantNotProvided - } - case .web: - if let accessToken = responseParameters["access_token"], let idToken = responseParameters["id_token"] { - var request = makeUrlRequest(url: "https://www.googleapis.com/userinfo/v2/me", method: "GET") - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - do { - let val = try await self.urlSession.data(for: request) - let data = try JSONSerialization.jsonObject(with: val.0) as? [String: Any] ?? [:] - self.userInfo = data - var newData: [String: Any] = ["userInfo": self.userInfo as Any] - newData["tokenForKeys"] = idToken - newData["verifierId"] = self.getVerifierFromUserInfo() - return newData - } catch { - throw CASDKError.accessTokenAPIFailed - } - } else { - throw CASDKError.getUserInfoFailed - } - } + debugPrint(String(data: data, encoding: .utf8)!) + let result = try JSONDecoder().decode(GoogleInfo.self, from: data) + + return TorusVerifierResponse(email: result.email, name: result.name, profileImage: result.picture, verifier: verifier, verifierId: result.email.lowercased(), typeOfLogin: typeOfLogin) } } diff --git a/Sources/CustomAuth/Handlers/HandlerFactory.swift b/Sources/CustomAuth/Handlers/HandlerFactory.swift new file mode 100644 index 0000000..229e86b --- /dev/null +++ b/Sources/CustomAuth/Handlers/HandlerFactory.swift @@ -0,0 +1,47 @@ +import Foundation + +public class HandlerFactory { + static func createHandler( + params: CreateHandlerParams + ) throws -> ILoginHandler { + if params.verifier.isEmpty { + throw CASDKError.invalidVerifier + } + + if params.clientId.isEmpty { + throw CASDKError.invalidClientID + } + + switch params.typeOfLogin { + case .google: + return try GoogleLoginHandler(clientId: params.clientId, verifier: params.verifier, urlScheme: params.urlScheme, redirectURL: params.redirectURL, typeOfLogin: params.typeOfLogin, jwtParams: params.jwtParams, customState: params.customState) + case .facebook: + return try FacebookLoginHandler(clientId: params.clientId, verifier: params.verifier, urlScheme: params.urlScheme, redirectURL: params.redirectURL, typeOfLogin: params.typeOfLogin, jwtParams: params.jwtParams, customState: params.customState) + case .reddit: + return try RedditLoginHandler(clientId: params.clientId, verifier: params.verifier, urlScheme: params.urlScheme, redirectURL: params.redirectURL, typeOfLogin: params.typeOfLogin, jwtParams: params.jwtParams, customState: params.customState) + case .twitch: + return try TwitchLoginHandler(clientId: params.clientId, verifier: params.verifier, urlScheme: params.urlScheme, redirectURL: params.redirectURL, typeOfLogin: params.typeOfLogin, jwtParams: params.jwtParams, customState: params.customState) + case .discord: + return try DiscordLoginHandler(clientId: params.clientId, verifier: params.verifier, urlScheme: params.urlScheme, redirectURL: params.redirectURL, typeOfLogin: params.typeOfLogin, jwtParams: params.jwtParams, customState: params.customState) + case .apple: break + case .github: break + case .linkedin: break + case .twitter: break + case .weibo: break + case .line: break + case .email_password: break + case .jwt: break + } + + if params.jwtParams?.id_token == nil || params.jwtParams?.access_token == nil { + return try MockLoginHandler(clientId: params.clientId, verifier: params.verifier, urlScheme: params.urlScheme, redirectURL: params.redirectURL, typeOfLogin: params.typeOfLogin, jwtParams: params.jwtParams, customState: params.customState) + } + + let domain = params.jwtParams?.domain + if domain == nil { + throw CASDKError.invalidAuth0Options + } + + return try JWTLoginHandler(clientId: params.clientId, verifier: params.verifier, urlScheme: params.urlScheme, redirectURL: params.redirectURL, typeOfLogin: params.typeOfLogin, jwtParams: params.jwtParams, customState: params.customState) + } +} diff --git a/Sources/CustomAuth/Handlers/JWTLoginHandler.swift b/Sources/CustomAuth/Handlers/JWTLoginHandler.swift index 41a8a80..2d6b57d 100644 --- a/Sources/CustomAuth/Handlers/JWTLoginHandler.swift +++ b/Sources/CustomAuth/Handlers/JWTLoginHandler.swift @@ -1,129 +1,80 @@ -// -// File.swift -// -// -// Created by Shubham on 13/11/20. -// - import Foundation -import JWTDecode -import TorusUtils + +public class Auth0UserInfo: Codable { + public let picture: String + public let email: String + public let name: String + public let sub: String + public let nickname: String +} class JWTLoginHandler: AbstractLoginHandler { - let loginType: SubVerifierType - let clientID: String - let redirectURL: String - let browserRedirectURL: String? - var userInfo: [String: Any]? - let nonce = String.randomString(length: 10) - let state: String - let defaultParams: [String: String] - let jwtParams: [String: String] - let connection: LoginProviders - var urlSession: URLSession - - public init(loginType: SubVerifierType = .web, clientID: String, redirectURL: String, browserRedirectURL: String?, jwtParams: [String: String], connection: LoginProviders, urlSession: URLSession = URLSession.shared) { - self.loginType = loginType - self.clientID = clientID - self.redirectURL = redirectURL - self.connection = connection - self.browserRedirectURL = browserRedirectURL - self.jwtParams = jwtParams - defaultParams = ["scope": "openid profile email", "response_type": "token id_token", "nonce": nonce] - self.urlSession = urlSession - - let tempState = ["nonce": nonce, "redirectUri": self.redirectURL, "redirectToAndroid": "true"] - let jsonData = try! JSONSerialization.data(withJSONObject: tempState, options: .prettyPrinted) - state = String(data: jsonData, encoding: .utf8)!.toBase64URL() -// self.state = ["nonce": self.nonce, "redirectUri": self.redirectURL, "redirectToAndroid": "true"].description.toBase64URL() - } + private var response_type: String = "token id_token" + private var scope: String = "openid profile email" + private var prompt: String = "Login" - func getUserInfo(responseParameters: [String: String]) async throws -> [String: Any] { - return try await handleLogin(responseParameters: responseParameters) + override public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + try super.init(clientId: clientId, verifier: verifier, urlScheme: urlScheme, redirectURL: redirectURL, typeOfLogin: typeOfLogin, jwtParams: jwtParams, customState: customState) + try setFinalUrl() } - func getLoginURL() -> String { - // left join - var tempParams = defaultParams - let paramsToJoin: [String: String] = ["redirect_uri": browserRedirectURL ?? redirectURL, "client_id": clientID, "domain": jwtParams["domain"]!, "state": state] - tempParams.merge(paramsToJoin) { _, new in new } - tempParams.merge(jwtParams) { _, new in new } - - // Reconstruct URL + override public func setFinalUrl() throws { var urlComponents = URLComponents() + + if jwtParams == nil { + throw CASDKError.invalidAuth0Options + } + + var params: [String: String] = try (JSONSerialization.jsonObject(with: try JSONEncoder().encode(jwtParams), options: []) as! [String: String]) + params.merge([ + "state": try state(), + "response_type": response_type, + "client_id": clientId, + "prompt": prompt, + "redirect_uri": redirectURL, + "scope": scope, + "connection": loginToConnection(loginType: typeOfLogin), + "nonce": nonce, + ], uniquingKeysWith: { _, new in new }) urlComponents.scheme = "https" - urlComponents.host = jwtParams["domain"] + urlComponents.host = jwtParams?.domain urlComponents.path = "/authorize" - urlComponents.setQueryItems(with: tempParams) + urlComponents.setQueryItems(with: params) - // return string - return urlComponents.url!.absoluteString + finalUrl = urlComponents } - func getVerifierFromUserInfo() -> String { - var res: String - let lowerCased = jwtParams["isVerifierIdCaseSensitive"] ?? "false" + override public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + let accessToken = params.accessToken + let idToken = params.idToken + let verifierIdField = jwtParams?.verifierIdField + let isVerifierCaseSensitive = jwtParams?.isVerifierIdCaseSensitive - if jwtParams["verifierIdField"] != nil { - let field = jwtParams["verifierIdField"]! - res = userInfo![field] as! String - } else { - switch connection { - case .apple, .weibo, .github, .twitter, .linkedin, .line, .jwt: - res = userInfo!["sub"] as! String - case .email_password: - res = userInfo!["name"] as! String - default: - return "verifier not supported" - } - } + if accessToken != nil { + let domain = jwtParams?.domain + let user_route_info = jwtParams?.user_info_route - if lowerCased == "true" { - return res.lowercased() - } else { - return res - } - } + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = domain + urlComponents.path = user_route_info ?? "" - func handleLogin(responseParameters: [String: String]) async throws -> [String: Any] { + var urlRequest = makeUrlRequest(url: urlComponents.string!, method: "GET") + urlRequest.addValue("Bearer \(accessToken!)", forHTTPHeaderField: "Authorization") - // Reconstruct URL - var urlComponents = URLComponents() - urlComponents.scheme = "https" - urlComponents.host = jwtParams["domain"] - urlComponents.path = "/userinfo" - - if let accessToken = responseParameters["access_token"] { - var request = makeUrlRequest(url: urlComponents.url!.absoluteString, method: "GET") - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - do { - let val = try await urlSession.data(for: request) - let data = try JSONSerialization.jsonObject(with: val.0) as? [String: Any] ?? [:] - self.userInfo = data - if responseParameters["error"] != nil { - throw responseParameters["error"]! - } - var newData: [String: Any] = ["userInfo": self.userInfo as Any] - newData["tokenForKeys"] = responseParameters["id_token"] - newData["verifierId"] = self.getVerifierFromUserInfo() - return newData - - } catch { - throw CASDKError.getUserInfoFailed - } - } else if let idToken = responseParameters["id_token"] { - do { - let decodedData = try decode(jwt: idToken) - userInfo = decodedData.body - var newData: [String: Any] = userInfo! - newData["tokenForKeys"] = idToken - newData["verifierId"] = getVerifierFromUserInfo() - return newData - } catch { - throw TorusUtilError.runtime("Invalid ID toke") - } + let (data, _) = try await URLSession.shared.data(for: urlRequest) + + let result = try JSONDecoder().decode(Auth0UserInfo.self, from: data) + + return TorusVerifierResponse(email: result.email, name: result.name, profileImage: result.picture, verifier: verifier, verifierId: try getVerifierId(userInfo: result, typeOfLogin: typeOfLogin, verifierIdField: verifierIdField!, isVerifierIdCaseSensitive: isVerifierCaseSensitive ?? true), typeOfLogin: typeOfLogin) + } + + if idToken == nil { + throw CASDKError.idTokenNotProvided } else { - throw CASDKError.accessTokenNotProvided + let result = try JSONDecoder().decode(Auth0UserInfo.self, from: idToken!.data(using: .utf8)!) + + return TorusVerifierResponse(email: result.email, name: result.name, profileImage: result.picture, verifier: verifier, verifierId: try getVerifierId(userInfo: result, typeOfLogin: typeOfLogin, verifierIdField: verifierIdField, isVerifierIdCaseSensitive: isVerifierCaseSensitive ?? true), typeOfLogin: typeOfLogin) } } } diff --git a/Sources/CustomAuth/Handlers/MockLoginHandler.swift b/Sources/CustomAuth/Handlers/MockLoginHandler.swift new file mode 100644 index 0000000..1c6e50f --- /dev/null +++ b/Sources/CustomAuth/Handlers/MockLoginHandler.swift @@ -0,0 +1,64 @@ +import Foundation + +class MockLoginHandler: AbstractLoginHandler { + override public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + try super.init(clientId: clientId, verifier: verifier, urlScheme: urlScheme, redirectURL: redirectURL, typeOfLogin: typeOfLogin, jwtParams: jwtParams, customState: customState) + try setFinalUrl() + } + + override public func setFinalUrl() throws { + if jwtParams == nil { + throw CASDKError.invalidAuth0Options + } + + var params: [String: String] = try (JSONSerialization.jsonObject(with: try JSONEncoder().encode(jwtParams), options: []) as! [String: String]) + params.merge([ + "state": try state(), + "client_id": clientId, + "nonce": nonce, + ], uniquingKeysWith: { _, new in new }) + + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = jwtParams?.domain + urlComponents.path = "/authorize" + urlComponents.fragment = params.compactMap({ (key, value) -> String in + return "\(key)=\(value)" + }).joined(separator: "&") + finalUrl = urlComponents + } + + override public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + let accessToken = params.accessToken + let idToken = params.idToken + let verifierIdField = jwtParams?.verifierIdField + let isVerifierCaseSensitive = jwtParams?.isVerifierIdCaseSensitive + + if accessToken != nil { + let domain = jwtParams?.domain + let user_route_info = jwtParams?.user_info_route + + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = domain + urlComponents.path = user_route_info ?? "" + + var urlRequest = makeUrlRequest(url: urlComponents.string!, method: "GET") + urlRequest.addValue("Bearer \(accessToken!)", forHTTPHeaderField: "Authorization") + + let (data, _) = try await URLSession.shared.data(for: urlRequest) + + let result = try JSONDecoder().decode(Auth0UserInfo.self, from: data) + + return TorusVerifierResponse(email: result.email, name: result.name, profileImage: result.picture, verifier: verifier, verifierId: try getVerifierId(userInfo: result, typeOfLogin: typeOfLogin, verifierIdField: verifierIdField!, isVerifierIdCaseSensitive: isVerifierCaseSensitive ?? true), typeOfLogin: typeOfLogin) + } + + if idToken == nil { + throw CASDKError.idTokenNotProvided + } else { + let result = try JSONDecoder().decode(Auth0UserInfo.self, from: idToken!.data(using: .utf8)!) + + return TorusVerifierResponse(email: result.email, name: result.name, profileImage: result.picture, verifier: verifier, verifierId: try getVerifierId(userInfo: result, typeOfLogin: typeOfLogin, verifierIdField: verifierIdField, isVerifierIdCaseSensitive: isVerifierCaseSensitive ?? true), typeOfLogin: typeOfLogin) + } + } +} diff --git a/Sources/CustomAuth/Handlers/Protocol/AbstractLoginHandler.swift b/Sources/CustomAuth/Handlers/Protocol/AbstractLoginHandler.swift index 06d9e37..80975ef 100644 --- a/Sources/CustomAuth/Handlers/Protocol/AbstractLoginHandler.swift +++ b/Sources/CustomAuth/Handlers/Protocol/AbstractLoginHandler.swift @@ -1,15 +1,70 @@ -// -// AbstractLoginHandler.swift -// -// -// Created by Shubham on 13/11/20. -// - import Foundation +#if canImport(curveSecp256k1) + import curveSecp256k1 +#endif + +public class AbstractLoginHandler: ILoginHandler { + public var clientId: String + + public var nonce: String + + public var finalUrl: URLComponents + + public var urlScheme: String + + public var redirectURL: String + + public var verifier: String + + public var typeOfLogin: LoginType + + public var jwtParams: Auth0ClientOptions? + + public var customState: TorusGenericContainer? + + public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + self.clientId = clientId + nonce = try SecretKey().serialize().addLeading0sForLength64() + finalUrl = URLComponents() + self.verifier = verifier + self.urlScheme = urlScheme + self.typeOfLogin = typeOfLogin + self.jwtParams = jwtParams + self.customState = customState + self.redirectURL = redirectURL + } + + public func state() throws -> String { + let encoder = JSONEncoder() + encoder.outputFormatting = .sortedKeys + let state = State(instanceId: nonce, verifier: verifier, typeOfLogin: typeOfLogin.rawValue, redirectUri: urlScheme, customState: customState) + return try encoder.encode(state).toBase64URL() + } + + public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + fatalError("getUserInfo must be implemented by concrete classes") + } + + public func handleLoginWindow(popupFeatures: String?) async throws -> LoginWindowResponse { + guard let callbackURLScheme = URL(string: urlScheme)?.scheme else { + throw CASDKError.invalidCallbackURLScheme + } + + let urlWithTokenInfo = try await AuthenticationManager().authenticationManagerWrapper(url: finalUrl.url!, callbackURLScheme: callbackURLScheme, prefersEphemeralWebBrowserSession: false) + + var tokenInfo = parseURL(url: urlWithTokenInfo) + debugPrint(tokenInfo) + let access_token = tokenInfo["access_token"] + let id_token = tokenInfo["id_token"] + let ref = tokenInfo["ref"] + + tokenInfo.removeValue(forKey: "access_token") + tokenInfo.removeValue(forKey: "id_token") + tokenInfo.removeValue(forKey: "ref") + return LoginWindowResponse(accessToken: access_token, idToken: id_token, ref: ref ?? "", state: TorusGenericContainer(params: tokenInfo)) + } -public protocol AbstractLoginHandler { - func getLoginURL() -> String - func getUserInfo(responseParameters: [String: String]) async throws -> [String: Any] - func getVerifierFromUserInfo() -> String - func handleLogin(responseParameters: [String: String]) async throws -> [String: Any] + public func setFinalUrl() throws { + throw CASDKError.invalidMethod(msg: "setFinalUrl cannot be called by abstract class") + } } diff --git a/Sources/CustomAuth/Handlers/Protocol/CreateHandlerParams.swift b/Sources/CustomAuth/Handlers/Protocol/CreateHandlerParams.swift new file mode 100644 index 0000000..9e8da0d --- /dev/null +++ b/Sources/CustomAuth/Handlers/Protocol/CreateHandlerParams.swift @@ -0,0 +1,21 @@ +import Foundation + +public class CreateHandlerParams: Codable { + public let typeOfLogin: LoginType + public let verifier: String + public let clientId: String + public let urlScheme: String + public let redirectURL: String + public let jwtParams: Auth0ClientOptions? + public let customState: TorusGenericContainer? + + public init(typeOfLogin: LoginType, verifier: String, clientId: String, urlScheme: String, redirectURL: String, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) { + self.typeOfLogin = typeOfLogin + self.verifier = verifier + self.clientId = clientId + self.urlScheme = urlScheme + self.jwtParams = jwtParams + self.customState = customState + self.redirectURL = redirectURL + } +} diff --git a/Sources/CustomAuth/Handlers/Protocol/ILoginHandler.swift b/Sources/CustomAuth/Handlers/Protocol/ILoginHandler.swift new file mode 100644 index 0000000..010e721 --- /dev/null +++ b/Sources/CustomAuth/Handlers/Protocol/ILoginHandler.swift @@ -0,0 +1,13 @@ +import Foundation + +public protocol ILoginHandler { + var clientId: String { get set } + var nonce: String { get set } + var finalUrl: URLComponents { get set } + + func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse + + func handleLoginWindow(popupFeatures: String?) async throws -> LoginWindowResponse + + func setFinalUrl() throws +} diff --git a/Sources/CustomAuth/Handlers/RedditLoginHandler.swift b/Sources/CustomAuth/Handlers/RedditLoginHandler.swift index 2f4248f..9df1709 100644 --- a/Sources/CustomAuth/Handlers/RedditLoginHandler.swift +++ b/Sources/CustomAuth/Handlers/RedditLoginHandler.swift @@ -1,82 +1,58 @@ -// -// RedditLoginHandler.swift -// -// -// Created by Shubham on 13/11/20. -// - import Foundation -class RedditLoginHandler: AbstractLoginHandler { - let loginType: SubVerifierType - let clientID: String - let redirectURL: String - let browserRedirectURL: String? - var userInfo: [String: Any]? - let nonce = String.randomString(length: 10) - let state: String - let jwtParams: [String: String] - let defaultParams: [String: String] - var urlSession: URLSession +class RedditInfo: Codable { + public var name: String + public var icon_image: String? +} - public init(loginType: SubVerifierType = .web, clientID: String, redirectURL: String, browserRedirectURL: String?, jwtParams: [String: String] = [:], urlSession: URLSession = URLSession.shared) { - self.loginType = loginType - self.clientID = clientID - self.redirectURL = redirectURL - self.jwtParams = jwtParams - self.browserRedirectURL = browserRedirectURL - self.defaultParams = ["scope": "identity", "response_type": "token", "state": "randomstate"] - self.urlSession = urlSession +class RedditLoginHandler: AbstractLoginHandler { + private var response_type: String = "token" + private var scope: String = "identity" - let tempState = ["nonce": self.nonce, "redirectUri": self.redirectURL, "redirectToAndroid": "true"] - let jsonData = try! JSONSerialization.data(withJSONObject: tempState, options: .prettyPrinted) - self.state = String(data: jsonData, encoding: .utf8)!.toBase64URL() + override public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + try super.init(clientId: clientId, verifier: verifier, urlScheme: urlScheme, redirectURL: redirectURL, typeOfLogin: typeOfLogin, jwtParams: jwtParams, customState: customState) + try setFinalUrl() } - func getUserInfo(responseParameters: [String: String]) async throws -> [String: Any] { - return try await self.handleLogin(responseParameters: responseParameters) - } + override public func setFinalUrl() throws { + var urlComponents = URLComponents() - func getLoginURL() -> String { - // left join - var tempParams = self.defaultParams - tempParams.merge(["redirect_uri": self.browserRedirectURL ?? self.redirectURL, "client_id": self.clientID, "state": self.state]) {(_, new ) in new} - tempParams.merge(self.jwtParams) {(_, new ) in new} + var params: [String: String] = [:] - // Reconstruct URL - var urlComponents = URLComponents() + if jwtParams != nil { + params = try (JSONSerialization.jsonObject(with: try JSONEncoder().encode(jwtParams), options: []) as! [String: String]) + } + + params.merge([ + "state": try state(), + "response_type": response_type, + "client_id": clientId, + "redirect_uri": redirectURL, + "scope": scope, + ], uniquingKeysWith: { _, new in new }) urlComponents.scheme = "https" urlComponents.host = "www.reddit.com" urlComponents.path = "/api/v1/authorize" - urlComponents.setQueryItems(with: tempParams) + urlComponents.setQueryItems(with: params) - return urlComponents.url!.absoluteString - // return "https://www.reddit.com/api/v1/authorize?client_id=\(self.clientId)&redirect_uri=\(newRedirectURL)&response_type=token&scope=identity&state=dfasdfs" + finalUrl = urlComponents } - func getVerifierFromUserInfo() -> String { - return self.userInfo?["name"] as? String ?? "" - } - - func handleLogin(responseParameters: [String: String]) async throws -> [String: Any] { - - if let accessToken = responseParameters["access_token"] { - var request = makeUrlRequest(url: "https://oauth.reddit.com/api/v1/me", method: "GET") - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - do { - let val = try await self.urlSession.data(for: request) - let data = try JSONSerialization.jsonObject(with: val.0) as? [String: Any] ?? [:] - self.userInfo = data - var newData: [String: Any] = ["userInfo": self.userInfo as Any] - newData["tokenForKeys"] = accessToken - newData["verifierId"] = self.getVerifierFromUserInfo() - return newData - } catch { - throw CASDKError.getUserInfoFailed - } - } else { + override public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + guard let accessToken = params.accessToken else { throw CASDKError.accessTokenNotProvided } - } + var urlRequest = makeUrlRequest(url: "https://oauth.reddit.com/api/v1/me", method: "GET") + urlRequest.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") + + let (data, _) = try await URLSession.shared.data(for: urlRequest) + + let result = try JSONDecoder().decode(RedditInfo.self, from: data) + + var profileImage = result.icon_image ?? "" + profileImage = profileImage.split(separator: "?").count > 0 ? String(profileImage.split(separator: "?")[0]) : profileImage + + return TorusVerifierResponse(email: "", name: result.name, profileImage: profileImage, verifier: verifier, verifierId: result.name.lowercased(), typeOfLogin: typeOfLogin) + } } diff --git a/Sources/CustomAuth/Handlers/TwitchLoginHandler.swift b/Sources/CustomAuth/Handlers/TwitchLoginHandler.swift index 958e9e1..a1d61b9 100644 --- a/Sources/CustomAuth/Handlers/TwitchLoginHandler.swift +++ b/Sources/CustomAuth/Handlers/TwitchLoginHandler.swift @@ -1,87 +1,61 @@ -// -// GoogleLoginHandler.swift -// -// -// Created by Shubham on 13/11/20. -// - import Foundation -class TwitchLoginHandler: AbstractLoginHandler { - let loginType: SubVerifierType - let clientID: String - let redirectURL: String - let browserRedirectURL: String? - var userInfo: [String: Any]? - let nonce = String.randomString(length: 10) - let state: String - let jwtParams: [String: String] - let defaultParams: [String: String] = ["scope": "user:read:email", "response_type": "token", "force_verify": "false"] - var urlSession: URLSession +class TwitchInfo: Codable { + public var id: String + public var display_name: String + public var profile_image_url: String +} - public init(loginType: SubVerifierType = .web, clientID: String, redirectURL: String, browserRedirectURL: String?, jwtParams: [String: String] = [:], urlSession: URLSession = URLSession.shared) { - self.loginType = loginType - self.clientID = clientID - self.redirectURL = redirectURL - self.jwtParams = jwtParams - self.browserRedirectURL = browserRedirectURL - self.urlSession = urlSession +class TwitchRootInfo: Codable { + public var data: TwitchInfo +} - let tempState = ["nonce": self.nonce, "redirectUri": self.redirectURL, "redirectToAndroid": "true"] - let jsonData = try! JSONSerialization.data(withJSONObject: tempState, options: .prettyPrinted) - self.state = String(data: jsonData, encoding: .utf8)!.toBase64URL() - } +class TwitchLoginHandler: AbstractLoginHandler { + private var response_type: String = "token" + private var scope: String = "user:read:email" - func getUserInfo(responseParameters: [String: String]) async throws-> [String: Any] { - return try await self.handleLogin(responseParameters: responseParameters) + override public init(clientId: String, verifier: String, urlScheme: String, redirectURL: String, typeOfLogin: LoginType, jwtParams: Auth0ClientOptions? = nil, customState: TorusGenericContainer? = nil) throws { + try super.init(clientId: clientId, verifier: verifier, urlScheme: urlScheme, redirectURL: redirectURL, typeOfLogin: typeOfLogin, jwtParams: jwtParams, customState: customState) + try setFinalUrl() } - func getLoginURL() -> String { - // left join - var tempParams = self.defaultParams - tempParams.merge(["redirect_uri": self.browserRedirectURL ?? self.redirectURL, "client_id": self.clientID, "state": self.state]) {(_, new ) in new} - tempParams.merge(self.jwtParams) {(_, new ) in new} - - // Reconstruct URL + override public func setFinalUrl() throws { var urlComponents = URLComponents() + + var params: [String: String] = [:] + + if jwtParams != nil { + params = try (JSONSerialization.jsonObject(with: try JSONEncoder().encode(jwtParams), options: []) as! [String: String]) + } + + params.merge([ + "state": try state(), + "response_type": response_type, + "client_id": clientId, + "redirect_uri": redirectURL, + "scope": scope, + "force_verify": "true", + ], uniquingKeysWith: { _, new in new }) urlComponents.scheme = "https" urlComponents.host = "id.twitch.tv" urlComponents.path = "/oauth2/authorize" - urlComponents.setQueryItems(with: tempParams) + urlComponents.setQueryItems(with: params) - return urlComponents.url!.absoluteString - // return "https://id.twitch.tv/oauth2/authorize?client_id=p560duf74b2bidzqu6uo0b3ot7qaao&"+"redirect_uri=\(newRedirectURL)&response_type=token&scope=user:read:email&state=554455&force_verify=false".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)! + finalUrl = urlComponents } - func getVerifierFromUserInfo() -> String { - if let newData = self.userInfo?["data"] as? [[String: Any]], let temp = newData.first, let id = temp["id"] as? String { - return id - } else { - return "nil" + override public func getUserInfo(params: LoginWindowResponse, storageServerUrl: String?) async throws -> TorusVerifierResponse { + guard let accessToken = params.accessToken else { + throw CASDKError.accessTokenNotProvided } - } - func handleLogin(responseParameters: [String: String]) async throws -> [String: Any] { + var urlRequest = makeUrlRequest(url: "https://api.twitch.tv/helix/users", method: "GET") + urlRequest.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - if let accessToken = responseParameters["access_token"] { - var request = makeUrlRequest(url: "https://api.twitch.tv/helix/users", method: "GET") - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - request.addValue("p560duf74b2bidzqu6uo0b3ot7qaao", forHTTPHeaderField: "Client-ID") - do { - let val = try await self.urlSession.data(for: request) - let data = try JSONSerialization.jsonObject(with: val.0) as? [String: Any] ?? [:] - self.userInfo = data - var newData: [String: Any] = ["userInfo": self.userInfo as Any] - newData["tokenForKeys"] = accessToken - newData["verifierId"] = self.getVerifierFromUserInfo() - return newData + let (data, _) = try await URLSession.shared.data(for: urlRequest) - } catch { - throw CASDKError.getUserInfoFailed - } - } else { - throw CASDKError.accessTokenNotProvided - } - } + let result = try JSONDecoder().decode(TwitchRootInfo.self, from: data) + return TorusVerifierResponse(email: "", name: result.data.display_name, profileImage: result.data.profile_image_url, verifier: verifier, verifierId: result.data.id, typeOfLogin: typeOfLogin) + } } diff --git a/Sources/CustomAuth/Helpers/ASWebAuthSession.swift b/Sources/CustomAuth/Helpers/ASWebAuthSession.swift deleted file mode 100644 index 3b66d6d..0000000 --- a/Sources/CustomAuth/Helpers/ASWebAuthSession.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// File.swift -// -// -// Created by Dhruv Jaiswal on 05/08/22. -// - -import AuthenticationServices -import Foundation - -open class ASWebAuthSession: NSObject, TorusURLHandlerTypes { - var redirectURL: URL? - // Ensure that there is a strong reference to the ASWebAuthenticationSession instance when the session is in progress. - private var authSession: ASWebAuthenticationSession? - public init(redirectURL: String) { - self.redirectURL = URL(string: redirectURL) - } - - public func handle(_ url: URL, modalPresentationStyle: UIModalPresentationStyle) { - let redirectURLScheme = redirectURL?.scheme ?? CustomAuth.didHandleCallbackURL.rawValue - authSession = ASWebAuthenticationSession( - url: url, callbackURLScheme: redirectURLScheme) { callbackURL, authError in - guard - authError == nil, - let callbackURL = callbackURL - else { - print(authError?.localizedDescription as? String ?? "") - let errStr = authError?.localizedDescription as? String ?? "Something went wrong!!" - CustomAuth.handleError(err: errStr) - return - } - CustomAuth.handle(url: callbackURL) - } - authSession?.presentationContextProvider = self - authSession?.start() - } -} - -extension ASWebAuthSession: ASWebAuthenticationPresentationContextProviding { - public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { - let window = UIApplication.shared.windows.first { $0.isKeyWindow } - return window ?? ASPresentationAnchor() - } -} diff --git a/Sources/CustomAuth/Helpers/Common.swift b/Sources/CustomAuth/Helpers/Common.swift new file mode 100644 index 0000000..4776154 --- /dev/null +++ b/Sources/CustomAuth/Helpers/Common.swift @@ -0,0 +1,108 @@ +import Foundation + +func loginToConnection(loginType: LoginType) -> String { + switch loginType { + case .apple: break + case .google: break + case .facebook: break + case .reddit: break + case .twitch: break + case .discord: break + case .github: break + case .linkedin: break + case .twitter: break + case .weibo: break + case .line: break + case .jwt: break + case .email_password: return "Username-Password-Authentication" + } + + return loginType.rawValue +} + +func getVerifierId( + userInfo: Auth0UserInfo, + typeOfLogin: LoginType, + verifierIdField: String? = nil, + isVerifierIdCaseSensitive: Bool = true +) throws -> String { + let name = userInfo.name + let sub = userInfo.sub + + let encoded = try JSONEncoder().encode(userInfo) + let json = try JSONSerialization.jsonObject(with: encoded, options: []) as! [String: String] + + if verifierIdField != nil { + return json[isVerifierIdCaseSensitive ? verifierIdField!.lowercased() : verifierIdField!]! + } + + switch typeOfLogin { + case .google: return sub + case .facebook: return sub + case .reddit: return sub + case .twitch: return sub + case .apple: return sub + case .github: return sub + case .discord: return sub + case .linkedin: return sub + case .twitter: return sub + case .weibo: return sub + case .line: return sub + case .jwt: return sub + case .email_password: return name + } +} + +func handleRedirectParameters(hash: String, queryParameters: TorusGenericContainer) throws -> (String, TorusGenericContainer, TorusGenericContainer) { + var hashParams: [String: String] = [:] + let hashSplit = hash.split(separator: "&") + hashSplit.forEach({ + let paramSplit = $0.split(separator: "=") + hashParams.updateValue(String(paramSplit[1]), forKey: String(paramSplit[0])) + }) + + var instanceParams: [String: String] = [:] + var error = "" + if hashParams.count > 0 && hashParams["state"] != nil { + let instanceSplit = try hashParams["state"]!.fromBase64URL().split(separator: "&") + instanceSplit.forEach({ + let paramSplit = $0.split(separator: "=") + instanceParams.updateValue(String(paramSplit[1]), forKey: String(paramSplit[0])) + }) + if hashParams["error_description"] != nil { + error = hashParams["error_description"]! + } else if hashParams["error"] != nil { + error = hashParams["error"]! + } + } else if queryParameters.params.count > 0 && queryParameters.params["state"] != nil { + let instanceSplit = try queryParameters.params["state"]!.fromBase64URL().split(separator: "&") + instanceSplit.forEach({ + let paramSplit = $0.split(separator: "=") + instanceParams.updateValue(String(paramSplit[1]), forKey: String(paramSplit[0])) + }) + if queryParameters.params["error"] != nil { + error = queryParameters.params["error"]! + } + } + + return (error, TorusGenericContainer(params: hashParams), TorusGenericContainer(params: instanceParams)) +} + +func parseURL(url: URL) -> [String: String] { + var responseParameters = [String: String]() + if let query = url.query { + responseParameters.merge(query.parametersFromQueryString, uniquingKeysWith: { _, new in new }) + } + if let fragment = url.fragment, !fragment.isEmpty { + responseParameters.merge(fragment.parametersFromQueryString, uniquingKeysWith: { _, new in new }) + } + return responseParameters +} + +func makeUrlRequest(url: String, method: String) -> URLRequest { + var rq = URLRequest(url: URL(string: url)!) + rq.httpMethod = method + rq.addValue("application/json", forHTTPHeaderField: "Content-Type") + rq.addValue("application/json", forHTTPHeaderField: "Accept") + return rq +} diff --git a/Sources/CustomAuth/Helpers/Error.swift b/Sources/CustomAuth/Helpers/Error.swift index e92ef0f..2af425d 100644 --- a/Sources/CustomAuth/Helpers/Error.swift +++ b/Sources/CustomAuth/Helpers/Error.swift @@ -1,31 +1,40 @@ public enum CASDKError: Error { - case getUserInfoFailed case decodingFailed - case accessTokenAPIFailed + case encodingFailed case accessTokenNotProvided - case authGrantNotProvided - case idTokenFailed - case unknownError - case methodUnavailable + case idTokenNotProvided + case invalidParameters + case invalidCallbackURLScheme + case invalidAuth0Options + case invalidVerifier + case invalidClientID + case invalidMethod(msg: String) + case redirectParamsError(msg: String) public var errorDescription: String { switch self { - case .getUserInfoFailed: - return "Unable to get verifier ID" case .decodingFailed: - return "decodingFailed" - case .accessTokenAPIFailed: - return "API failed for retrieving access token" + return "decoding failed" + case .encodingFailed: + return "encoding failed" case .accessTokenNotProvided: - return "access token unavailable in data" - case .authGrantNotProvided: - return "authorization grant not available" - case .idTokenFailed: - return "idTokenFailed" - case .unknownError: - return "unknownError" - case .methodUnavailable: - return "method unavailable/unimplemented" + return "access token not provided" + case .idTokenNotProvided: + return "id token not provided" + case .invalidCallbackURLScheme: + return "callback scheme is invalid" + case .invalidParameters: + return "parameters are missing or invalid" + case .invalidAuth0Options: + return "auth0 options are missing or invalid" + case .invalidMethod(msg: let msg): + return msg + case .redirectParamsError(msg: let msg): + return msg + case .invalidVerifier: + return "invalid verifier" + case .invalidClientID: + return "invalid client ID" } } } diff --git a/Sources/CustomAuth/Helpers/ExternalURLHandler.swift b/Sources/CustomAuth/Helpers/ExternalURLHandler.swift deleted file mode 100644 index caef7b3..0000000 --- a/Sources/CustomAuth/Helpers/ExternalURLHandler.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// ExternalURLHandler.swift -// -// -// Created by Shubham on 18/6/20. -// - -import Foundation -import UIKit - -class ExternalURLHandler: TorusURLHandlerTypes { - @objc open func handle(_ url: URL, modalPresentationStyle: UIModalPresentationStyle) { - #if os(iOS) || os(tvOS) - #if !OAUTH_APP_EXTENSIONS - if #available(iOS 10.0, tvOS 10.0, *) { - UIApplication.shared.open(url) - } else { - UIApplication.shared.openURL(url) - } - #endif - #elseif os(watchOS) - // WATCHOS: not implemented - #elseif os(OSX) - NSWorkspace.shared.open(url) - #endif - } -} diff --git a/Sources/CustomAuth/Helpers/SFURLHandler.swift b/Sources/CustomAuth/Helpers/SFURLHandler.swift deleted file mode 100644 index 5b02f62..0000000 --- a/Sources/CustomAuth/Helpers/SFURLHandler.swift +++ /dev/null @@ -1,92 +0,0 @@ -// MARK: Open SFSafariViewController - -import Foundation -import SafariServices - -import UIKit - -open class SFURLHandler: NSObject, SFSafariViewControllerDelegate, TorusURLHandlerTypes { - public typealias Transition = (_ controller: SFSafariViewController, _ handler: SFURLHandler) -> Void - open var present: Transition - open var dismiss: Transition - var observers = [String: NSObjectProtocol]() - - // configure default presentation and dismissal code - open var animated: Bool = true - open var presentCompletion: (() -> Void)? - open var dismissCompletion: (() -> Void)? - open var delay: UInt32? = 1 - - /// init - public init(viewController: UIViewController) { - present = { [weak viewController] controller, handler in - viewController?.present(controller, animated: handler.animated, completion: handler.presentCompletion) - } - dismiss = { [weak viewController] _, handler in - viewController?.dismiss(animated: handler.animated, completion: handler.dismissCompletion) - } - } - - public init(present: @escaping Transition, dismiss: @escaping Transition) { - // self.oauthSwift = oauthSwift - self.present = present - self.dismiss = dismiss - } - - @objc open func handle(_ url: URL, modalPresentationStyle: UIModalPresentationStyle = .fullScreen) { - let controller = SFSafariViewController(url: url) - controller.modalPresentationStyle = modalPresentationStyle - controller.dismissButtonStyle = .cancel - controller.delegate = self - - // Present on main queue - present(controller, self) - - let key = UUID().uuidString - - observers[key] = CustomAuth.notificationCenter.addObserver( - forName: CustomAuth.didHandleCallbackURL, - object: nil, - queue: OperationQueue.main, - using: { _ in - if let observer = self.observers[key] { - CustomAuth.notificationCenter.removeObserver(observer) - self.observers.removeValue(forKey: key) - } - self.dismiss(controller, self) - // TODO: dismiss on main queue - } - ) - } - - /// Clear internal observers on authentification flow - open func clearObservers() { - clearLocalObservers() - // self.CustomAuth?.removeCallbackNotificationObserver() - } - - open func clearLocalObservers() { - for (_, observer) in observers { - CustomAuth.notificationCenter.removeObserver(observer) - } - observers.removeAll() - } - - /// SFSafari delegates implementation - open weak var delegate: SFSafariViewControllerDelegate? - - public func safariViewController(_ controller: SFSafariViewController, activityItemsFor URL: Foundation.URL, title: String?) -> [UIActivity] { - return delegate?.safariViewController?(controller, activityItemsFor: URL, title: title) ?? [] - } - - public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { - // "Done" pressed - clearObservers() - dismiss(controller, self) - delegate?.safariViewControllerDidFinish?(controller) - } - - public func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) { - delegate?.safariViewController?(controller, didCompleteInitialLoad: didLoadSuccessfully) - } -} diff --git a/Sources/CustomAuth/Helpers/URL.swift b/Sources/CustomAuth/Helpers/URL.swift deleted file mode 100644 index d2fac35..0000000 --- a/Sources/CustomAuth/Helpers/URL.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// File.swift -// -// -// Created by Shubham on 13/11/20. -// - -import Foundation - -func makeUrlRequest(url: String, method: String) -> URLRequest { - var rq = URLRequest(url: URL(string: url)!) - rq.httpMethod = method - rq.addValue("application/json", forHTTPHeaderField: "Content-Type") - rq.addValue("application/json", forHTTPHeaderField: "Accept") - return rq -} - -extension URLComponents { - mutating func setQueryItems(with parameters: [String: String]) { - queryItems = parameters.map { URLQueryItem(name: $0.key, value: $0.value) } - } -} - -extension URL { - var queryDictionary: [String: String]? { - guard let query = query else { return nil } - - var queryStrings = [String: String]() - for pair in query.components(separatedBy: "&") { - let key = pair.components(separatedBy: "=")[0] - - let value = pair - .components(separatedBy: "=")[1] - .replacingOccurrences(of: "+", with: " ") - .removingPercentEncoding ?? "" - - queryStrings[key] = value - } - return queryStrings - } -} - -/* -Fix for the issue - https://www.swiftbysundell.com/articles/making-async-system-apis-backward-compatible/ -*/ -@available(iOS, deprecated: 15.0, message: "Use the built-in API instead") -extension URLSession { - func data(for request: URLRequest, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - try await withCheckedThrowingContinuation { continuation in - let task = self.dataTask(with: request) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) - } - - task.resume() - } - } -} diff --git a/Sources/CustomAuth/Models/Browser.swift b/Sources/CustomAuth/Models/Browser.swift deleted file mode 100644 index 78862e3..0000000 --- a/Sources/CustomAuth/Models/Browser.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Browser.swift -// -// Created by Shubham on 18/6/20. -// - -import Foundation -import UIKit - -public protocol TorusURLHandlerTypes { - func handle(_ url: URL, modalPresentationStyle: UIModalPresentationStyle) -} - -public enum URLOpenerTypes: String { - case external - case sfsafari - case asWebAuthSession -} diff --git a/Sources/CustomAuth/Models/LoginProviders.swift b/Sources/CustomAuth/Models/LoginProviders.swift deleted file mode 100644 index 267ee77..0000000 --- a/Sources/CustomAuth/Models/LoginProviders.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation - -// MARK: - login providers - -public enum LoginProviders: String { - case google - case facebook - case twitch - case reddit - case discord - case apple - case github - case linkedin - case kakao - case twitter - case weibo - case line - case wechat - case email_password = "Username-Password-Authentication" - case jwt - - func getHandler(loginType: SubVerifierType, clientID: String, redirectURL: String, browserRedirectURL: String?, jwtParams: [String: String], urlSession: URLSession = URLSession.shared) -> AbstractLoginHandler { - switch self { - case .google: - return GoogleloginHandler(loginType: loginType, clientID: clientID, redirectURL: redirectURL, browserRedirectURL: browserRedirectURL, jwtParams: jwtParams, urlSession: urlSession) - case .facebook: - return FacebookLoginHandler(loginType: loginType, clientID: clientID, redirectURL: redirectURL, browserRedirectURL: browserRedirectURL, jwtParams: jwtParams, urlSession: urlSession) - case .twitch: - return TwitchLoginHandler(loginType: loginType, clientID: clientID, redirectURL: redirectURL, browserRedirectURL: browserRedirectURL, jwtParams: jwtParams, urlSession: urlSession) - case .reddit: - return RedditLoginHandler(loginType: loginType, clientID: clientID, redirectURL: redirectURL, browserRedirectURL: browserRedirectURL, jwtParams: jwtParams, urlSession: urlSession) - case .discord: - return DiscordLoginHandler(loginType: loginType, clientID: clientID, redirectURL: redirectURL, browserRedirectURL: browserRedirectURL, jwtParams: jwtParams, urlSession: urlSession) - case .apple, .github, .linkedin, .twitter, .weibo, .kakao, .line, .wechat, .email_password, .jwt: - return JWTLoginHandler(loginType: loginType, clientID: clientID, redirectURL: redirectURL, browserRedirectURL: browserRedirectURL, jwtParams: jwtParams, connection: self, urlSession: urlSession) - } - } -} diff --git a/Sources/CustomAuth/Models/SubVerifierDetails.swift b/Sources/CustomAuth/Models/SubVerifierDetails.swift deleted file mode 100644 index 1fac38f..0000000 --- a/Sources/CustomAuth/Models/SubVerifierDetails.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// SubverifierDetails.swift -// -// -// Created by Shubham on 1/6/20. -// -import Foundation - -// Type of OAuth application created. ex. google web app/google iOS app -public enum SubVerifierType: String { - case installed - case web -} - -// MARK: - subverifierdetails - -public struct SubVerifierDetails { - public let loginType: SubVerifierType - public let clientId: String - public let verifier: String - public let loginProvider: LoginProviders - public let redirectURL: String - public let handler: AbstractLoginHandler - public var urlSession: URLSession - - public enum codingKeys: String, CodingKey { - case clientId - case loginProvider - case subVerifierId - } - - public init(loginType: SubVerifierType = .web, loginProvider: LoginProviders, clientId: String, verifier: String, redirectURL: String, browserRedirectURL: String? = nil, jwtParams: [String: String] = [:], urlSession: URLSession = URLSession.shared) { - self.loginType = loginType - self.clientId = clientId - self.loginProvider = loginProvider - self.verifier = verifier - self.redirectURL = redirectURL - self.urlSession = urlSession - handler = self.loginProvider.getHandler(loginType: loginType, clientID: self.clientId, redirectURL: self.redirectURL, browserRedirectURL: browserRedirectURL, jwtParams: jwtParams, urlSession: urlSession) - } - - public func getLoginURL() -> String { - return handler.getLoginURL() - } - - public func getUserInfo(responseParameters: [String: String]) async throws -> [String: Any] { - return try await handler.getUserInfo(responseParameters: responseParameters) - } -} diff --git a/Tests/CustomAuthTests/CustomAuthTests.swift b/Tests/CustomAuthTests/CustomAuthTests.swift index dd9aee3..585dd13 100644 --- a/Tests/CustomAuthTests/CustomAuthTests.swift +++ b/Tests/CustomAuthTests/CustomAuthTests.swift @@ -1,114 +1,10 @@ -@testable import CustomAuth +import Foundation import JWTDecode -import TorusUtils -import UIKit import XCTest -import curveSecp256k1 -final class MockSDKTest: XCTestCase { +final class CustomAuthTests: XCTestCase { func test_jwtDecodeTest() { let idToken = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcnVzLmF1LmF1dGgwLmNvbS8iLCJhdWQiOiJLRzd6azg5WDNRZ3R0U3lYOU5KNGZHRXlGTmhPY0pUdyIsIm5hbWUiOiJkaHJ1dkB0b3IudXMiLCJlbWFpbCI6ImRocnV2QHRvci51cyIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJpYXQiOjE2NTQ2NzcwMTQsImVhdCI6MTY1NDY3NzMxNCwiZXhwIjoxNjU0Njc3MzE0fQ.3nzDGeSiQwfTVmL4T4-e5N19eD280GjtosFzcGjhWv_sUCV2YkM3i7iFIpUq7AxoPXjai5v7GTTPRu1zHPL6bg" - let decodedData = try! decode(jwt: idToken) - print(decodedData) - } - - func testGetTorusKey() async throws { - let expectation = XCTestExpectation(description: "getTorusKey should correctly proxy input and output to/from TorusUtils") - - let expectedPrivateKey = try fakeData.generatePrivateKey() - let expectedPublicAddress = try fakeData.generatePublicKey() - let expectedVerifier = fakeData.generateVerifier() - let expectedVerfierId = fakeData.generateRandomEmail(of: 6) - - let CustomAuth = CustomAuth(web3AuthClientId:"YOUR_CLIENT_ID", aggregateVerifierType: .singleLogin, aggregateVerifier: expectedVerifier, subVerifierDetails: [], network: .legacy(.MAINNET)) - - let mockTorusUtils = MockTorusUtils() - CustomAuth.torusUtils = mockTorusUtils - // Set Mock data - mockTorusUtils.retrieveShares_output["privateKey"] = expectedPrivateKey - mockTorusUtils.retrieveShares_output["publicAddress"] = expectedPublicAddress - do { - let nodeDetails = try await CustomAuth.getNodeDetailsFromContract(verifier: expectedVerifier, verfierID: expectedVerfierId) - let data = try await CustomAuth.getTorusKey(verifier: expectedVerifier, verifierId: expectedVerfierId, idToken: fakeData.generateVerifier()) - let mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils - let FinalKeyData = data.finalKeyData! - print(FinalKeyData) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["endpoints"] as? [String], nodeDetails.getTorusNodeEndpoints()) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierIdentifier"] as? String, expectedVerifier) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierId"] as? String, expectedVerfierId) - XCTAssertEqual(FinalKeyData.privKey, expectedPrivateKey) - XCTAssertEqual(FinalKeyData.evmAddress, expectedPublicAddress) - expectation.fulfill() - } catch { - XCTFail(error.localizedDescription) - expectation.fulfill() - } - } - - func testGetAggregateTorusKey() async throws { - let expectation = XCTestExpectation(description: "getAggregateTorusKey should correctly proxy input and output to/from TorusUtils") - - let expectedPrivateKey = try fakeData.generatePrivateKey() - let expectedPublicAddress = try fakeData.generatePublicKey() - let expectedVerifier = fakeData.generateVerifier() - let expectedVerfierId = fakeData.generateRandomEmail(of: 6) - - let subVerifier = [SubVerifierDetails(loginProvider: .jwt, clientId: fakeData.generateVerifier(), verifier: expectedVerifier, redirectURL: fakeData.generateVerifier())] - - let CustomAuth = CustomAuth(web3AuthClientId:"YOUR_CLIENT_ID", aggregateVerifierType: .singleIdVerifier, aggregateVerifier: expectedVerifier, subVerifierDetails: subVerifier, network: .legacy(.MAINNET)) -// var mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils - let mockTorusUtils = MockTorusUtils() - CustomAuth.torusUtils = mockTorusUtils - - // Set Mock data - mockTorusUtils.retrieveShares_output["privateKey"] = expectedPrivateKey - mockTorusUtils.retrieveShares_output["publicAddress"] = expectedPublicAddress - do { - - let nodeDetails = try await CustomAuth.getNodeDetailsFromContract(verifier: expectedVerifier, verfierID: expectedVerfierId) - let data = try await CustomAuth.getAggregateTorusKey(verifier: expectedVerifier, verifierId: expectedVerfierId, idToken: fakeData.generateVerifier(), subVerifierDetails: subVerifier[0]) - print("Data", data) - let FinalKeyData = data.finalKeyData! - let mockTorusUtils = CustomAuth.torusUtils as! MockAbstractTorusUtils - XCTAssertEqual(mockTorusUtils.retrieveShares_input["endpoints"] as? [String], nodeDetails.getTorusNodeEndpoints()) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierIdentifier"] as? String, expectedVerifier) - XCTAssertEqual(mockTorusUtils.retrieveShares_input["verifierId"] as? String, expectedVerfierId) - XCTAssertEqual(FinalKeyData.privKey, expectedPrivateKey) - XCTAssertEqual(FinalKeyData.evmAddress, expectedPublicAddress) - expectation.fulfill() - } catch { - XCTFail(error.localizedDescription) - expectation.fulfill() - } - } - - static var allTests = [ - ("testGetTorusKey", testGetTorusKey) -// ("testGetAggregateTorusKey", testGetAggregateTorusKey), - ] -} - -class fakeData { - static func generateVerifier() -> String { - return String.randomString(length: 10) - } - - static func generatePrivateKey() throws -> String { - let privateKey = curveSecp256k1.SecretKey(); - return try privateKey.serialize() - } - - static func generatePublicKey() throws -> String { - let privateKey = curveSecp256k1.SecretKey(); - return try privateKey.toPublic().serialize(compressed: false); - } - - static func generateRandomEmail(of length: Int) -> String { - let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - var s = "" - for _ in 0 ..< length { - s.append(letters.randomElement()!) - } - return s + "@gmail.com" + let _ = try! decode(jwt: idToken) } } diff --git a/Tests/CustomAuthTests/IntegrationTests.swift b/Tests/CustomAuthTests/IntegrationTests.swift deleted file mode 100644 index 26dd8a6..0000000 --- a/Tests/CustomAuthTests/IntegrationTests.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// File.swift -// -// -// Created by Shubham on 5/10/21. -// - -@testable import CustomAuth -import Foundation -import JWTKit -import XCTest - -final class IntegrationTests: XCTestCase { - static var sdk: CustomAuth? - var sub: SubVerifierDetails! - - override func setUp() { - super.setUp() - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .google, - clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", - verifier: "google-lrc", - redirectURL: "com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4:/oauthredirect", - browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") - - IntegrationTests.sdk = CustomAuth(web3AuthClientId:"YOUR_CLIENT_ID", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-test-ios-public", subVerifierDetails: [sub], network: .legacy(.CYAN)) - } - - func test_getTorusKey() async throws { - let TORUS_TEST_VERIFIER = "torus-test-health" - let email = "hello@tor.us" - let jwt = try! generateIdToken(email: email) - let data = try await IntegrationTests.sdk?.getTorusKey(verifier: TORUS_TEST_VERIFIER, verifierId: email, idToken: jwt) - let finalKeyDataDict = data!.finalKeyData! - let evmAddress = finalKeyDataDict.evmAddress - XCTAssertEqual(evmAddress, "0xC615aA03Dd8C9b2dc6F7c43cBDfF2c34bBa47Ec9") - } - - let TORUS_TEST_EMAIL = "saasas@tr.us" - let TORUS_IMPORT_EMAIL = "importeduser2@tor.us" - - let TORUS_EXTENDED_VERIFIER_EMAIL = "testextenderverifierid@example.com" - - let TORUS_TEST_VERIFIER = "torus-test-health" - - let TORUS_TEST_AGGREGATE_VERIFIER = "torus-test-health-aggregate" - let HashEnabledVerifier = "torus-test-verifierid-hash" - - func test_Sapphire_getTorusKey() async throws { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .google, - clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", - verifier: "google-lrc", - redirectURL: "com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4:/oauthredirect", - browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") - let sdk = CustomAuth(web3AuthClientId:"221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-test-ios-public", subVerifierDetails: [sub], network: .sapphire(.SAPPHIRE_DEVNET)) - - let jwt = try! generateIdToken(email: TORUS_TEST_EMAIL) - let data = try await sdk.getTorusKey(verifier: TORUS_TEST_VERIFIER, verifierId: TORUS_TEST_EMAIL, idToken: jwt) - let finalKeyDataDict = data.finalKeyData! - let evmAddress = finalKeyDataDict.evmAddress - XCTAssertEqual(evmAddress, "0x81001206C06AD09b3611b593aEEd3A607d79871E") - } - - func test_Sapphire_getTorusKey_gated() async throws { - let sub = SubVerifierDetails(loginType: .web, - loginProvider: .jwt, - clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", - verifier: "torus-test-ios-public", - redirectURL: "com.googleusercontent.apps.238941746713-vfap8uumijal4ump28p9jd3lbe6onqt4:/oauthredirect", - browserRedirectURL: "https://scripts.toruswallet.io/redirect.html") - let sdk = CustomAuth(web3AuthClientId:"221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com", aggregateVerifierType: .singleLogin, aggregateVerifier: "torus-test-ios-public", subVerifierDetails: [sub], network: .sapphire(.SAPPHIRE_DEVNET)) - - let jwt = try! generateIdToken(email: TORUS_TEST_EMAIL) - let data = try await sdk.getTorusKey(verifier: TORUS_TEST_VERIFIER, verifierId: TORUS_TEST_EMAIL, idToken: jwt) - let finalKeyDataDict = data.finalKeyData! - let evmAddress = finalKeyDataDict.evmAddress - XCTAssertEqual(evmAddress, "0x81001206C06AD09b3611b593aEEd3A607d79871E") - } -} - -// JWT payload structure. -struct TestPayload: JWTPayload, Equatable { - enum CodingKeys: String, CodingKey { - case subject = "sub" - case expiration = "exp" - case isAdmin = "admin" - case emailVerified = "email_verified" - case issuer = "iss" - case iat - case email - case audience = "aud" - } - - var subject: SubjectClaim - var expiration: ExpirationClaim - var audience: AudienceClaim - var isAdmin: Bool - let emailVerified: Bool - var issuer: IssuerClaim - var iat: IssuedAtClaim - var email: String - - // call its verify method. - func verify(using signer: JWTSigner) throws { - try expiration.verifyNotExpired() - } -} - -func generateRandomEmail(of length: Int) -> String { - let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - var s = "" - for _ in 0 ..< length { - s.append(letters.randomElement()!) - } - return s + "@gmail.com" -} - -func generateIdToken(email: String) throws -> String { - let verifierPrivateKeyForSigning = - """ - -----BEGIN PRIVATE KEY----- - MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCD7oLrcKae+jVZPGx52Cb/lKhdKxpXjl9eGNa1MlY57A== - -----END PRIVATE KEY----- - """ - - do { - let signers = JWTSigners() - let keys = try ECDSAKey.private(pem: verifierPrivateKeyForSigning) - signers.use(.es256(key: keys)) - - // Parses the JWT and verifies its signature. - let today = Date() - let modifiedDate = Calendar.current.date(byAdding: .hour, value: 1, to: today)! - - let emailComponent = email.components(separatedBy: "@")[0] - let subject = "email|" + emailComponent - - let payload = TestPayload(subject: SubjectClaim(stringLiteral: subject), expiration: ExpirationClaim(value: modifiedDate), audience: "torus-key-test", isAdmin: false, emailVerified: true, issuer: "torus-key-test", iat: IssuedAtClaim(value: Date()), email: email) - let jwt = try signers.sign(payload) - return jwt - } catch { - print(error) - throw error - } -} diff --git a/Tests/CustomAuthTests/MockCASDKFactory.swift b/Tests/CustomAuthTests/MockCASDKFactory.swift deleted file mode 100644 index 8be08dd..0000000 --- a/Tests/CustomAuthTests/MockCASDKFactory.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// File.swift -// -// -// Created by Shubham on 31/7/21. -// - -import CustomAuth -import FetchNodeDetails -import Foundation -import OSLog -import TorusUtils - -public class MockFactory: CASDKFactoryProtocol { - init() {} - - public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils { - MockTorusUtils() - } - -// public func createFetchNodeDetails(network: TorusNetwork, urlSession: URLSession, networkUrl: String? = nil) -> AllNodeDetailsModel { -// let net = network == .MAINNET ? "0xf20336e16B5182637f09821c27BDe29b0AFcfe80" : "0x6258c9d6c12ed3edda59a1a6527e469517744aa7" -// return AllNodeDetailsModel(proxyAddress: net, network: network) -// } -} diff --git a/Tests/CustomAuthTests/MockTorusUtils.swift b/Tests/CustomAuthTests/MockTorusUtils.swift deleted file mode 100644 index 659a0fc..0000000 --- a/Tests/CustomAuthTests/MockTorusUtils.swift +++ /dev/null @@ -1,73 +0,0 @@ -import CustomAuth -import FetchNodeDetails -import Foundation -import BigInt -@testable import TorusUtils - -// Added so the that we can assign values later. -public protocol MockAbstractTorusUtils { - var retrieveShares_input: [String: Any] { get set } - var retrieveShares_output: [String: String] { get set } -} - -class MockTorusUtils: AbstractTorusUtils, MockAbstractTorusUtils { - - func retrieveShares(endpoints: [String], torusNodePubs: [TorusNodePubModel], indexes: [BigUInt], verifier: String, verifierParams: VerifierParams, idToken: String, extraParams: [String : Codable]) async throws -> TorusKey { - retrieveShares_input = [ - "endpoints": endpoints, - "verifierIdentifier": verifier, - "verifierId": verifierParams.verifier_id, - "idToken": idToken, - "extraParams": extraParams - ] - let finalKeyData : TorusKey.FinalKeyData = .init(evmAddress: retrieveShares_output["publicAddress"] ?? "", X: "", Y: "", privKey: retrieveShares_output["privateKey"] ?? "") - return TorusKey(finalKeyData: finalKeyData, oAuthKeyData: nil, sessionData: nil, metadata: nil, nodesData: nil) - } - - func getPublicAddress(endpoints: [String], torusNodePubs: [TorusNodePubModel], verifier: String, verifierId: String, extendedVerifierId: String?) async throws -> TorusPublicKey { - return .init(finalKeyData: nil, oAuthKeyData: nil, metadata: nil, nodesData: nil) - } - - func getUserTypeAndAddress(endpoints: [String], torusNodePubs: [TorusNodePubModel]?, verifier: String, verifierID: String, doesKeyAssign: Bool)async throws -> GetUserAndAddress{ - return .init(typeOfUser: .v1, address: "", x: "", y: "") - } - - func getOrSetNonce(x: String, y: String, privateKey: String?, getOnly: Bool) async throws -> GetOrSetNonceResult { - return GetOrSetNonceResult(typeOfUser: "v1") - } - - var retrieveShares_input: [String: Any] = [:] - var retrieveShares_output: [String: String] = [ - "privateKey": "", - "publicAddress": "" - ] - - var label: String? - var nodePubKeys: [TorusNodePubModel]? - - init() { - } - - func setTorusNodePubKeys(nodePubKeys: [TorusNodePubModel]) { - self.nodePubKeys = nodePubKeys - } - - func getPublicAddress(endpoints: [String], torusNodePubs: [TorusNodePubModel], verifier: String, verifierId: String, isExtended: Bool) async throws -> [String: String] { - return (["publicAddress": retrieveShares_output["publicAddress"] ?? ""]) - } - - func retrieveShares(torusNodePubs: [TorusNodePubModel], endpoints: [String], verifier verifierIdentifier: String, verifierId: String, idToken: String, extraParams: Data) async throws -> [String: String] { - retrieveShares_input = [ - "endpoints": endpoints, - "verifierIdentifier": verifierIdentifier, - "verifierId": verifierId, - "idToken": idToken, - "extraParams": extraParams - ] - return retrieveShares_output - } - - func getPostBoxKey(torusKey: RetrieveSharesResponseModel) -> String { - return "" - } -} diff --git a/Tests/CustomAuthTests/StubURLProtocol.swift b/Tests/CustomAuthTests/StubURLProtocol.swift deleted file mode 100644 index 5086acd..0000000 --- a/Tests/CustomAuthTests/StubURLProtocol.swift +++ /dev/null @@ -1,514 +0,0 @@ -import Foundation - -private func mustDecodeJSON(_ s: String) -> [String: Any] { - return try! JSONSerialization.jsonObject(with: Data(s.utf8), options: []) as! [String: Any] -} - -private func httpBodyStreamToData(stream: InputStream?) -> Data? { - guard let bodyStream = stream else { return nil } - bodyStream.open() - - // Will read 16 chars per iteration. Can use bigger buffer if needed - let bufferSize: Int = 16 - - let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) - - var dat = Data() - - while bodyStream.hasBytesAvailable { - let readDat = bodyStream.read(buffer, maxLength: bufferSize) - dat.append(buffer, count: readDat) - } - - buffer.deallocate() - - bodyStream.close() - - return dat -} - -private func stubMatcher(host: String, scheme: String, path: String, method: String, requestHeaders: [String: String]) -> (URLRequest) -> Bool { - return { (req: URLRequest) -> Bool in - if req.url?.host != host || req.url?.scheme != scheme || req.url?.path != path || req.httpMethod != method { - return false - } - for (name, value) in requestHeaders { - if req.value(forHTTPHeaderField: name) != value { - return false - } - } - return true - } -} - -private func stubMatcherWithBody(host: String, scheme: String, path: String, method: String, requestHeaders: [String: String], body: [String: Any]) -> (URLRequest) -> Bool { - return { (req: URLRequest) -> Bool in - if !stubMatcher(host: host, scheme: scheme, path: path, method: method, requestHeaders: requestHeaders)(req) { - return false - } - guard - let bodyData = StubURLProtocol.property(forKey: httpBodyKey, in: req) as? Data, - let jsonBody = (try? JSONSerialization.jsonObject(with: bodyData, options: [])) as? [String: Any] - else { - return false - } - return NSDictionary(dictionary: jsonBody).isEqual(to: body) - } -} - -private let injectedURLs: Set = [ - URL(string: "https://www.googleapis.com/userinfo/v2/me"), - URL(string: "https://ropsten.infura.io/v3/7f287687b3d049e2bea7b64869ee30a3"), - URL(string: "https://teal-15-4.torusnode.com/jrpc"), - URL(string: "https://teal-15-2.torusnode.com/jrpc"), - URL(string: "https://teal-15-1.torusnode.com/jrpc"), - URL(string: "https://teal-15-3.torusnode.com/jrpc"), - URL(string: "https://teal-15-5.torusnode.com/jrpc"), - URL(string: "https://signer.tor.us/api/allow"), - URL(string: "https://metadata.tor.us/get") -] - -private let injectedStubs: [Stub] = [ - Stub( - requestMatcher: stubMatcher( - host: "www.googleapis.com", - scheme: "https", - path: "/userinfo/v2/me", - method: "GET", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Authorization":"Bearer ya29.a0ARrdaM96u3PfVhg9xbkCPuecmF6YaylxRcJwKJTlHY8kwwuSyKbqme2qBbTdLoVORMZy4n8Al5Wr1HCnfjCesU38W1xkSgFNoPhRgTen6Zqxyr_tOddJw6-TUUbe45z6Zvkbx8DzBHShQkm-KbbNzh_M00kh","Accept":"application/json"}"#) as! [String: String] - ), - responseBody: Data(#"{"id":"109111953856031799639","email":"michael@tor.us","verified_email":true,"name":"Michael Lee","given_name":"Michael","family_name":"Lee","picture":"https://lh3.googleusercontent.com/a/AATXAJwsBb98gSYjVNlBBAhXJjvqNOw2GDSeTf0I6SJh=s96-c","locale":"en","hd":"tor.us"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Date":"Sun, 17 Oct 2021 10:57:29 GMT","x-frame-options":"SAMEORIGIN","Pragma":"no-cache","x-xss-protection":"0","Content-Encoding":"gzip","Server":"ESF","Cache-Control":"no-cache, no-store, max-age=0, must-revalidate","Vary":"Origin, X-Origin, Referer","Alt-Svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","x-content-type-options":"nosniff","Content-Length":"234","Content-Type":"application/json; charset=UTF-8","Expires":"Mon, 01 Jan 1990 00:00:00 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "ropsten.infura.io", - scheme: "https", - path: "/v3/b8cdb0e4cff24599a286bf8e87ff1c96", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"eth_call","id":1,"params":[{"to":"0x4023d2a0d330bf11426b12c6144cfb96b7fa6183","data":"0x76671808"},"latest"]}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","id":1,"result":"0x000000000000000000000000000000000000000000000000000000000000000f"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Accept-Encoding, Origin","Date":"Sun, 17 Oct 2021 10:57:30 GMT","Content-Length":"102","Content-Type":"application/json"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "ropsten.infura.io", - scheme: "https", - path: "/v3/b8cdb0e4cff24599a286bf8e87ff1c96", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"eth_call","id":1,"params":[{"to":"0x4023d2a0d330bf11426b12c6144cfb96b7fa6183","data":"0x135022c2000000000000000000000000000000000000000000000000000000000000000f"},"latest"]}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","id":1,"result":"0x000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000005000000000000000000000000455d2ba3f20fa83b9f824e665dd201d908c79dce000000000000000000000000b452bbd6f4d52d87f33336aad921538bf8dfdf67000000000000000000000000e3c0493536f20d090c8f9427d8fdfe548af3266200000000000000000000000054ac312ed9ba51cdd65f182487f29a3999dbf4e200000000000000000000000057f7a525608dc540fefc3e851700a4189d19142d"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Content-Length":"870","Vary":"Accept-Encoding, Origin","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:31 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "ropsten.infura.io", - scheme: "https", - path: "/v3/b8cdb0e4cff24599a286bf8e87ff1c96", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"eth_call","id":1,"params":[{"to":"0x4023d2a0d330bf11426b12c6144cfb96b7fa6183","data":"0xbafb358100000000000000000000000054ac312ed9ba51cdd65f182487f29a3999dbf4e2"},"latest"]}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000425a98d9ae006aed1d77e81d58be8f67193d13d01a9888e2923841894f4b0bf9cf63d40df480dacf68922004ed36dbab9e2969181b047730a5ce0797fb695824900000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001b7465616c2d31352d352e746f7275736e6f64652e636f6d3a343433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Date":"Sun, 17 Oct 2021 10:57:31 GMT","Content-Type":"application/json","Content-Length":"678","Vary":"Accept-Encoding, Origin"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "ropsten.infura.io", - scheme: "https", - path: "/v3/b8cdb0e4cff24599a286bf8e87ff1c96", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"eth_call","id":1,"params":[{"to":"0x4023d2a0d330bf11426b12c6144cfb96b7fa6183","data":"0xbafb3581000000000000000000000000455d2ba3f20fa83b9f824e665dd201d908c79dce"},"latest"]}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000011363aad8868cacd7f8946c590325cd463106fb3731f08811ab4302d2deae35c3d77eebe5cdf466b475ec892d5b4cffbe0c1670525debbd97eee6dae2f87a7cbe00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001b7465616c2d31352d312e746f7275736e6f64652e636f6d3a343433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Accept-Encoding, Origin","Date":"Sun, 17 Oct 2021 10:57:31 GMT","Content-Length":"678","Content-Type":"application/json"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "ropsten.infura.io", - scheme: "https", - path: "/v3/b8cdb0e4cff24599a286bf8e87ff1c96", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"eth_call","id":1,"params":[{"to":"0x4023d2a0d330bf11426b12c6144cfb96b7fa6183","data":"0xbafb3581000000000000000000000000b452bbd6f4d52d87f33336aad921538bf8dfdf67"},"latest"]}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000027c8cc521c48690f016bea593f67f88ad24f447dd6c31bbab541e59e207bf029db359f0a82608db2e06b953b36d0c9a473a00458117ca32a5b0f4563a7d53963600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001b7465616c2d31352d332e746f7275736e6f64652e636f6d3a343433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Date":"Sun, 17 Oct 2021 10:57:31 GMT","Vary":"Accept-Encoding, Origin","Content-Type":"application/json","Content-Length":"678"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "ropsten.infura.io", - scheme: "https", - path: "/v3/b8cdb0e4cff24599a286bf8e87ff1c96", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"eth_call","id":1,"params":[{"to":"0x4023d2a0d330bf11426b12c6144cfb96b7fa6183","data":"0xbafb358100000000000000000000000057f7a525608dc540fefc3e851700a4189d19142d"},"latest"]}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000005d908f41f8e06324a8a7abcf702adb6a273ce3ae63d86a3d22723e1bbf1438c9af977530b3ec0e525438c72d1e768380cbc5fb3b38a760ee925053b2e169428ce00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001b7465616c2d31352d322e746f7275736e6f64652e636f6d3a343433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Accept-Encoding, Origin","Content-Length":"678","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:31 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "ropsten.infura.io", - scheme: "https", - path: "/v3/b8cdb0e4cff24599a286bf8e87ff1c96", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"eth_call","id":1,"params":[{"to":"0x4023d2a0d330bf11426b12c6144cfb96b7fa6183","data":"0xbafb3581000000000000000000000000e3c0493536f20d090c8f9427d8fdfe548af32662"},"latest"]}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000038a86543ca17df5687719e2549caa024cf17fe0361e119e741eaee668f8dd0a6f9cdb254ff915a76950d6d13d78ef054d5d0dc34e2908c00bb009a6e4da70189100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001b7465616c2d31352d342e746f7275736e6f64652e636f6d3a343433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Date":"Sun, 17 Oct 2021 10:57:31 GMT","Content-Length":"678","Content-Type":"application/json","Vary":"Accept-Encoding, Origin"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-4.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"VerifierLookupRequest","id":10,"params":{"verifier":"torus-direct-mock-ios","verifier_id":"michael@tor.us"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"key_index":"1c724","pub_key_X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","pub_key_Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64","address":"0x22f2Ce611cE0d0ff4DA661d3a4C4B7A60B2b13F8"}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Origin","Server":"nginx/1.19.9","Content-Length":"281","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-2.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"VerifierLookupRequest","id":10,"params":{"verifier":"torus-direct-mock-ios","verifier_id":"michael@tor.us"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"key_index":"1c724","pub_key_X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","pub_key_Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64","address":"0x22f2Ce611cE0d0ff4DA661d3a4C4B7A60B2b13F8"}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Origin","Server":"nginx/1.19.9","Content-Length":"281","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-1.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"VerifierLookupRequest","id":10,"params":{"verifier":"torus-direct-mock-ios","verifier_id":"michael@tor.us"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"key_index":"1c724","pub_key_X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","pub_key_Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64","address":"0x22f2Ce611cE0d0ff4DA661d3a4C4B7A60B2b13F8"}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Content-Length":"281","Vary":"Origin","Content-Type":"application/json","Server":"nginx/1.19.9","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-3.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"VerifierLookupRequest","id":10,"params":{"verifier":"torus-direct-mock-ios","verifier_id":"michael@tor.us"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"key_index":"1c724","pub_key_X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","pub_key_Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64","address":"0x22f2Ce611cE0d0ff4DA661d3a4C4B7A60B2b13F8"}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Date":"Sun, 17 Oct 2021 10:57:32 GMT","Content-Length":"281","Content-Type":"application/json","Server":"nginx/1.19.9","Vary":"Origin"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcher( - host: "signer.tor.us", - scheme: "https", - path: "/api/allow", - method: "GET", - requestHeaders: mustDecodeJSON(#"{"Origin":"torus-direct-mock-ios","Accept":"application/json","Content-Type":"application/json","x-api-key":"torus-default"}"#) as! [String: String] - ), - responseBody: Data(#"{"success":true}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Content-Length":"16","access-control-allow-headers":"pubkeyx,pubkeyy,x-api-key,x-embed-host,content-type,authorization,verifier,verifier_id","access-control-max-age":"86400","access-control-allow-methods":"GET,OPTIONS","Access-Control-Allow-Origin":"*","Date":"Sun, 17 Oct 2021 10:57:32 GMT","Content-Type":"application/json"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-5.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"VerifierLookupRequest","id":10,"params":{"verifier":"torus-direct-mock-ios","verifier_id":"michael@tor.us"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"key_index":"1c724","pub_key_X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","pub_key_Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64","address":"0x22f2Ce611cE0d0ff4DA661d3a4C4B7A60B2b13F8"}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Origin","Content-Length":"281","Server":"nginx/1.19.9","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "metadata.tor.us", - scheme: "https", - path: "/get", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"pub_key_X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","pub_key_Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64"}"#) - ), - responseBody: Data(#"{"message":""}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"x-download-options":"noopen","x-permitted-cross-domain-policies":"none","x-content-type-options":"nosniff","Strict-Transport-Security":"max-age=15552000; includeSubDomains","x-dns-prefetch-control":"off","x-xss-protection":"0","content-security-policy":"default-src 'self';base-uri 'self';block-all-mixed-content;font-src 'self' https: data:;frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests","x-frame-options":"SAMEORIGIN","referrer-policy":"no-referrer","Date":"Sun, 17 Oct 2021 10:57:32 GMT","Content-Type":"application/json; charset=utf-8","expect-ct":"max-age=0","Etag":"W/\"e-JWOqSwGs6lhRJiUZe/mVb6Mua74\"","Content-Length":"14","Vary":"Origin, Accept-Encoding"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-1.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"CommitmentRequest","id":10,"params":{"messageprefix":"mug00","temppuby":"03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e","temppubx":"3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025","tokencommitment":"f9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b","timestamp":"0","verifieridentifier":"torus-direct-mock-ios"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"signature":"f94f88b5a2fff06463fe0cb4569a652a11f351061dcd5b15e466274e374eb2992632153bda0c017d9c83916b82f1daa3ee5ac9990201d73a18915224a828b6a41b","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepubx":"1363aad8868cacd7f8946c590325cd463106fb3731f08811ab4302d2deae35c3","nodepuby":"d77eebe5cdf466b475ec892d5b4cffbe0c1670525debbd97eee6dae2f87a7cbe"},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Server":"nginx/1.19.9","Content-Type":"application/json","Vary":"Origin","Date":"Sun, 17 Oct 2021 10:57:32 GMT","Content-Length":"606"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-3.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"CommitmentRequest","id":10,"params":{"messageprefix":"mug00","temppuby":"03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e","temppubx":"3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025","tokencommitment":"f9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b","timestamp":"0","verifieridentifier":"torus-direct-mock-ios"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"signature":"ee5b3560bc5b394326ddb784970eb27c995b77ceac9ce04ddffe72a52542dffd7b90b30c50b69481b43f04a0373b632798bac8fcdf8d695ead606200e0a24fc41c","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepubx":"7c8cc521c48690f016bea593f67f88ad24f447dd6c31bbab541e59e207bf029d","nodepuby":"b359f0a82608db2e06b953b36d0c9a473a00458117ca32a5b0f4563a7d539636"},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Server":"nginx/1.19.9","Content-Type":"application/json","Vary":"Origin","Date":"Sun, 17 Oct 2021 10:57:32 GMT","Content-Length":"606"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-4.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"CommitmentRequest","id":10,"params":{"messageprefix":"mug00","temppuby":"03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e","temppubx":"3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025","tokencommitment":"f9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b","timestamp":"0","verifieridentifier":"torus-direct-mock-ios"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"signature":"739487dab15bc238d32db83faf7b0aeb57f6863ac079aa331605eee9e076567c5cfa588978128af9d2c160d92a30197ccec8ab8c24ea68a3ac540a2534f65e261c","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepubx":"8a86543ca17df5687719e2549caa024cf17fe0361e119e741eaee668f8dd0a6f","nodepuby":"9cdb254ff915a76950d6d13d78ef054d5d0dc34e2908c00bb009a6e4da701891"},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Origin","Server":"nginx/1.19.9","Content-Length":"606","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-5.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"CommitmentRequest","id":10,"params":{"messageprefix":"mug00","temppuby":"03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e","temppubx":"3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025","tokencommitment":"f9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b","timestamp":"0","verifieridentifier":"torus-direct-mock-ios"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"signature":"d24ccf58546df41bc8506b467e017ec64d941feb442a02001bea1c014dbe4d6b01317473884284d038ea116e6040ab25dad9901ee94d41dd33674cd105bc32151b","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepubx":"25a98d9ae006aed1d77e81d58be8f67193d13d01a9888e2923841894f4b0bf9c","nodepuby":"f63d40df480dacf68922004ed36dbab9e2969181b047730a5ce0797fb6958249"},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Origin","Server":"nginx/1.19.9","Content-Length":"606","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-2.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"jsonrpc":"2.0","method":"CommitmentRequest","id":10,"params":{"messageprefix":"mug00","temppuby":"03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e","temppubx":"3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025","tokencommitment":"f9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b","timestamp":"0","verifieridentifier":"torus-direct-mock-ios"}}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"signature":"ed5d0191d91c02b427d6482cec5d5380026218a1adecfefd6f892903577abb5d43f303302b1eac90b7d225beeb66c8c9ed9ebde8ccfe5994b3fb5f028cf571411c","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepubx":"d908f41f8e06324a8a7abcf702adb6a273ce3ae63d86a3d22723e1bbf1438c9a","nodepuby":"f977530b3ec0e525438c72d1e768380cbc5fb3b38a760ee925053b2e169428ce"},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Server":"nginx/1.19.9","Content-Length":"606","Content-Type":"application/json","Vary":"Origin","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-3.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"params":{"encrypted":"yes","item":[{"verifieridentifier":"torus-direct-mock-ios","nodesignatures":[{"signature":"f94f88b5a2fff06463fe0cb4569a652a11f351061dcd5b15e466274e374eb2992632153bda0c017d9c83916b82f1daa3ee5ac9990201d73a18915224a828b6a41b","nodepubx":"1363aad8868cacd7f8946c590325cd463106fb3731f08811ab4302d2deae35c3","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"d77eebe5cdf466b475ec892d5b4cffbe0c1670525debbd97eee6dae2f87a7cbe"},{"nodepubx":"7c8cc521c48690f016bea593f67f88ad24f447dd6c31bbab541e59e207bf029d","nodepuby":"b359f0a82608db2e06b953b36d0c9a473a00458117ca32a5b0f4563a7d539636","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"ee5b3560bc5b394326ddb784970eb27c995b77ceac9ce04ddffe72a52542dffd7b90b30c50b69481b43f04a0373b632798bac8fcdf8d695ead606200e0a24fc41c"},{"signature":"739487dab15bc238d32db83faf7b0aeb57f6863ac079aa331605eee9e076567c5cfa588978128af9d2c160d92a30197ccec8ab8c24ea68a3ac540a2534f65e261c","nodepubx":"8a86543ca17df5687719e2549caa024cf17fe0361e119e741eaee668f8dd0a6f","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"9cdb254ff915a76950d6d13d78ef054d5d0dc34e2908c00bb009a6e4da701891"},{"data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"d24ccf58546df41bc8506b467e017ec64d941feb442a02001bea1c014dbe4d6b01317473884284d038ea116e6040ab25dad9901ee94d41dd33674cd105bc32151b","nodepuby":"f63d40df480dacf68922004ed36dbab9e2969181b047730a5ce0797fb6958249","nodepubx":"25a98d9ae006aed1d77e81d58be8f67193d13d01a9888e2923841894f4b0bf9c"}],"verifier_id":"michael@tor.us","idtoken":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImFkZDhjMGVlNjIzOTU0NGFmNTNmOTM3MTJhNTdiMmUyNmY5NDMzNTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDkxMTE5NTM4NTYwMzE3OTk2MzkiLCJoZCI6InRvci51cyIsImVtYWlsIjoibWljaGFlbEB0b3IudXMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6InRUNDhSck1vdGFFbi1UN3dzc2U3QnciLCJub25jZSI6InZSU2tPZWwyQTkiLCJuYW1lIjoiTWljaGFlbCBMZWUiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUFUWEFKd3NCYjk4Z1NZalZObEJCQWhYSmp2cU5PdzJHRFNlVGYwSTZTSmg9czk2LWMiLCJnaXZlbl9uYW1lIjoiTWljaGFlbCIsImZhbWlseV9uYW1lIjoiTGVlIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE2MzQ0NjgyNDksImV4cCI6MTYzNDQ3MTg0OX0.XGu1tm_OqlSrc5BMDMzOrlhxLZo1YnpCUT0_j2U1mQt86nJzf_Hp85JfapZj2QeeUz91H6-Ei8FR1i4ICEfjMcoZOW1Azc89qUNfUgWeyjqZ7wCHSsbHAwabE74RFAS9YAja8_ynUvCARfDEtoqcreNgmbw3ZntzAqpuuNBXYfbr87kMvu_wZ7fWjLKM91CvuXytQBwtieTyjAFnTXmEL60Pdu-JSQfHCbS5H39ZHlnYxEO6qztIjvbnQokhjHDGc4PMCx0wfzrEet1ojNOCnbfmaYE5NQudquzQNZtqZfn8f4B-sQhECElnOXagHlafWO5RayS0dCb1mTfr8orcCA"}]},"id":10,"method":"ShareRequest","jsonrpc":"2.0"}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"Index":"1c724","PublicKey":{"X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64"},"Threshold":1,"Verifiers":{"torus-direct-mock-ios":["michael@tor.us"]},"Share":"NGNmMDY4M2M0ZjVlMzAzZTE1YWE0YWU3NDQwZjJiNWQ2ZWVkN2U2MjcxZGQ3MjVjMTA2OGY5Njk3MTM0ODRmNmFmYjQwNjhhYjkyMGM3MTY0MWFjNWZjYTBiMGVhMTQw","Metadata":{"iv":"95d1859aa5f86d87f13ed672d12e2d10","ephemPublicKey":"0403aa155f2605555d7399378e71146420e8d4eac9fd911ee57134da846f0e1e60702397386f0ec1226c2e7616283739922d9b654570bce4fd775021ee7bfb6451","mac":"aabadefa3f0d1d7530425595e2b54faaafb9ee3e3ff7c8ad14f8f8572095ba4e","mode":"AES256"}}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Origin","Content-Length":"722","Date":"Sun, 17 Oct 2021 10:57:32 GMT","Content-Type":"application/json","Server":"nginx/1.19.9"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-1.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Content-Type":"application/json","Accept":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"params":{"encrypted":"yes","item":[{"verifieridentifier":"torus-direct-mock-ios","nodesignatures":[{"signature":"f94f88b5a2fff06463fe0cb4569a652a11f351061dcd5b15e466274e374eb2992632153bda0c017d9c83916b82f1daa3ee5ac9990201d73a18915224a828b6a41b","nodepubx":"1363aad8868cacd7f8946c590325cd463106fb3731f08811ab4302d2deae35c3","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"d77eebe5cdf466b475ec892d5b4cffbe0c1670525debbd97eee6dae2f87a7cbe"},{"nodepubx":"7c8cc521c48690f016bea593f67f88ad24f447dd6c31bbab541e59e207bf029d","nodepuby":"b359f0a82608db2e06b953b36d0c9a473a00458117ca32a5b0f4563a7d539636","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"ee5b3560bc5b394326ddb784970eb27c995b77ceac9ce04ddffe72a52542dffd7b90b30c50b69481b43f04a0373b632798bac8fcdf8d695ead606200e0a24fc41c"},{"signature":"739487dab15bc238d32db83faf7b0aeb57f6863ac079aa331605eee9e076567c5cfa588978128af9d2c160d92a30197ccec8ab8c24ea68a3ac540a2534f65e261c","nodepubx":"8a86543ca17df5687719e2549caa024cf17fe0361e119e741eaee668f8dd0a6f","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"9cdb254ff915a76950d6d13d78ef054d5d0dc34e2908c00bb009a6e4da701891"},{"data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"d24ccf58546df41bc8506b467e017ec64d941feb442a02001bea1c014dbe4d6b01317473884284d038ea116e6040ab25dad9901ee94d41dd33674cd105bc32151b","nodepuby":"f63d40df480dacf68922004ed36dbab9e2969181b047730a5ce0797fb6958249","nodepubx":"25a98d9ae006aed1d77e81d58be8f67193d13d01a9888e2923841894f4b0bf9c"}],"verifier_id":"michael@tor.us","idtoken":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImFkZDhjMGVlNjIzOTU0NGFmNTNmOTM3MTJhNTdiMmUyNmY5NDMzNTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDkxMTE5NTM4NTYwMzE3OTk2MzkiLCJoZCI6InRvci51cyIsImVtYWlsIjoibWljaGFlbEB0b3IudXMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6InRUNDhSck1vdGFFbi1UN3dzc2U3QnciLCJub25jZSI6InZSU2tPZWwyQTkiLCJuYW1lIjoiTWljaGFlbCBMZWUiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUFUWEFKd3NCYjk4Z1NZalZObEJCQWhYSmp2cU5PdzJHRFNlVGYwSTZTSmg9czk2LWMiLCJnaXZlbl9uYW1lIjoiTWljaGFlbCIsImZhbWlseV9uYW1lIjoiTGVlIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE2MzQ0NjgyNDksImV4cCI6MTYzNDQ3MTg0OX0.XGu1tm_OqlSrc5BMDMzOrlhxLZo1YnpCUT0_j2U1mQt86nJzf_Hp85JfapZj2QeeUz91H6-Ei8FR1i4ICEfjMcoZOW1Azc89qUNfUgWeyjqZ7wCHSsbHAwabE74RFAS9YAja8_ynUvCARfDEtoqcreNgmbw3ZntzAqpuuNBXYfbr87kMvu_wZ7fWjLKM91CvuXytQBwtieTyjAFnTXmEL60Pdu-JSQfHCbS5H39ZHlnYxEO6qztIjvbnQokhjHDGc4PMCx0wfzrEet1ojNOCnbfmaYE5NQudquzQNZtqZfn8f4B-sQhECElnOXagHlafWO5RayS0dCb1mTfr8orcCA"}]},"id":10,"method":"ShareRequest","jsonrpc":"2.0"}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"Index":"1c724","PublicKey":{"X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64"},"Threshold":1,"Verifiers":{"torus-direct-mock-ios":["michael@tor.us"]},"Share":"ZjBjNTEyNDI1MTBmOThiMGJjNDhhZjdhOTgwZjNkYTM0YjhmYmVkYzRjZTA2NzI1ZmI4MDExYWQ1MTc3YTUwNzFkYjNmNDNhYzA2NGNjYjkzYWIxYjY0YWZkY2I2NzMy","Metadata":{"iv":"e72d1cbaef1868cfbe241c3a84bb0a26","ephemPublicKey":"048b20e455385773ea58f59b0da8bde5cbe07f46155f6793fb120cd0fac8113ecf31adfcf5c07a8457d0973b93902c59fd156496ccf3746b9d44ce3de671360109","mac":"d9e7d9b565dc815a4969928296630bbc8102f080d3bcabb8f91df08f6cdb3f74","mode":"AES256"}}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Server":"nginx/1.19.9","Content-Length":"722","Vary":"Origin","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-5.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"params":{"encrypted":"yes","item":[{"verifieridentifier":"torus-direct-mock-ios","nodesignatures":[{"signature":"f94f88b5a2fff06463fe0cb4569a652a11f351061dcd5b15e466274e374eb2992632153bda0c017d9c83916b82f1daa3ee5ac9990201d73a18915224a828b6a41b","nodepubx":"1363aad8868cacd7f8946c590325cd463106fb3731f08811ab4302d2deae35c3","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"d77eebe5cdf466b475ec892d5b4cffbe0c1670525debbd97eee6dae2f87a7cbe"},{"nodepubx":"7c8cc521c48690f016bea593f67f88ad24f447dd6c31bbab541e59e207bf029d","nodepuby":"b359f0a82608db2e06b953b36d0c9a473a00458117ca32a5b0f4563a7d539636","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"ee5b3560bc5b394326ddb784970eb27c995b77ceac9ce04ddffe72a52542dffd7b90b30c50b69481b43f04a0373b632798bac8fcdf8d695ead606200e0a24fc41c"},{"signature":"739487dab15bc238d32db83faf7b0aeb57f6863ac079aa331605eee9e076567c5cfa588978128af9d2c160d92a30197ccec8ab8c24ea68a3ac540a2534f65e261c","nodepubx":"8a86543ca17df5687719e2549caa024cf17fe0361e119e741eaee668f8dd0a6f","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"9cdb254ff915a76950d6d13d78ef054d5d0dc34e2908c00bb009a6e4da701891"},{"data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"d24ccf58546df41bc8506b467e017ec64d941feb442a02001bea1c014dbe4d6b01317473884284d038ea116e6040ab25dad9901ee94d41dd33674cd105bc32151b","nodepuby":"f63d40df480dacf68922004ed36dbab9e2969181b047730a5ce0797fb6958249","nodepubx":"25a98d9ae006aed1d77e81d58be8f67193d13d01a9888e2923841894f4b0bf9c"}],"verifier_id":"michael@tor.us","idtoken":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImFkZDhjMGVlNjIzOTU0NGFmNTNmOTM3MTJhNTdiMmUyNmY5NDMzNTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDkxMTE5NTM4NTYwMzE3OTk2MzkiLCJoZCI6InRvci51cyIsImVtYWlsIjoibWljaGFlbEB0b3IudXMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6InRUNDhSck1vdGFFbi1UN3dzc2U3QnciLCJub25jZSI6InZSU2tPZWwyQTkiLCJuYW1lIjoiTWljaGFlbCBMZWUiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUFUWEFKd3NCYjk4Z1NZalZObEJCQWhYSmp2cU5PdzJHRFNlVGYwSTZTSmg9czk2LWMiLCJnaXZlbl9uYW1lIjoiTWljaGFlbCIsImZhbWlseV9uYW1lIjoiTGVlIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE2MzQ0NjgyNDksImV4cCI6MTYzNDQ3MTg0OX0.XGu1tm_OqlSrc5BMDMzOrlhxLZo1YnpCUT0_j2U1mQt86nJzf_Hp85JfapZj2QeeUz91H6-Ei8FR1i4ICEfjMcoZOW1Azc89qUNfUgWeyjqZ7wCHSsbHAwabE74RFAS9YAja8_ynUvCARfDEtoqcreNgmbw3ZntzAqpuuNBXYfbr87kMvu_wZ7fWjLKM91CvuXytQBwtieTyjAFnTXmEL60Pdu-JSQfHCbS5H39ZHlnYxEO6qztIjvbnQokhjHDGc4PMCx0wfzrEet1ojNOCnbfmaYE5NQudquzQNZtqZfn8f4B-sQhECElnOXagHlafWO5RayS0dCb1mTfr8orcCA"}]},"id":10,"method":"ShareRequest","jsonrpc":"2.0"}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"Index":"1c724","PublicKey":{"X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64"},"Threshold":1,"Verifiers":{"torus-direct-mock-ios":["michael@tor.us"]},"Share":"MzUyMTg4ZjEyMzc1NDAwZjk0MDIxOTgyNGJjNjZkM2U1MmZmM2Y0YjJjZWFkOTQzN2M0N2ZjMjMxMDFkYzQ5YzY5NjZiZTUzM2MwMDg2NTE1OGRlNThiNDc5N2M5Yjgy","Metadata":{"iv":"c73e422b8c1ca9bbe10caa04d8d6e79d","ephemPublicKey":"046ac88f638dc83e4eef85b9bea2de984449ad7587cc5c652451632d2ecc1509b1fa43180768d9c6e5e513d48f2bd8c69d450a4e279a0dbbdb5e7d917e54405e84","mac":"eb941f9a9317d7b27f7bda11988b6478a87bed80d644ccf6b09131e4a488bcd4","mode":"AES256"}}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Content-Length":"722","Vary":"Origin","Date":"Sun, 17 Oct 2021 10:57:32 GMT","Server":"nginx/1.19.9","Content-Type":"application/json"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-2.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"params":{"encrypted":"yes","item":[{"verifieridentifier":"torus-direct-mock-ios","nodesignatures":[{"signature":"f94f88b5a2fff06463fe0cb4569a652a11f351061dcd5b15e466274e374eb2992632153bda0c017d9c83916b82f1daa3ee5ac9990201d73a18915224a828b6a41b","nodepubx":"1363aad8868cacd7f8946c590325cd463106fb3731f08811ab4302d2deae35c3","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"d77eebe5cdf466b475ec892d5b4cffbe0c1670525debbd97eee6dae2f87a7cbe"},{"nodepubx":"7c8cc521c48690f016bea593f67f88ad24f447dd6c31bbab541e59e207bf029d","nodepuby":"b359f0a82608db2e06b953b36d0c9a473a00458117ca32a5b0f4563a7d539636","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"ee5b3560bc5b394326ddb784970eb27c995b77ceac9ce04ddffe72a52542dffd7b90b30c50b69481b43f04a0373b632798bac8fcdf8d695ead606200e0a24fc41c"},{"signature":"739487dab15bc238d32db83faf7b0aeb57f6863ac079aa331605eee9e076567c5cfa588978128af9d2c160d92a30197ccec8ab8c24ea68a3ac540a2534f65e261c","nodepubx":"8a86543ca17df5687719e2549caa024cf17fe0361e119e741eaee668f8dd0a6f","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"9cdb254ff915a76950d6d13d78ef054d5d0dc34e2908c00bb009a6e4da701891"},{"data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"d24ccf58546df41bc8506b467e017ec64d941feb442a02001bea1c014dbe4d6b01317473884284d038ea116e6040ab25dad9901ee94d41dd33674cd105bc32151b","nodepuby":"f63d40df480dacf68922004ed36dbab9e2969181b047730a5ce0797fb6958249","nodepubx":"25a98d9ae006aed1d77e81d58be8f67193d13d01a9888e2923841894f4b0bf9c"}],"verifier_id":"michael@tor.us","idtoken":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImFkZDhjMGVlNjIzOTU0NGFmNTNmOTM3MTJhNTdiMmUyNmY5NDMzNTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDkxMTE5NTM4NTYwMzE3OTk2MzkiLCJoZCI6InRvci51cyIsImVtYWlsIjoibWljaGFlbEB0b3IudXMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6InRUNDhSck1vdGFFbi1UN3dzc2U3QnciLCJub25jZSI6InZSU2tPZWwyQTkiLCJuYW1lIjoiTWljaGFlbCBMZWUiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUFUWEFKd3NCYjk4Z1NZalZObEJCQWhYSmp2cU5PdzJHRFNlVGYwSTZTSmg9czk2LWMiLCJnaXZlbl9uYW1lIjoiTWljaGFlbCIsImZhbWlseV9uYW1lIjoiTGVlIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE2MzQ0NjgyNDksImV4cCI6MTYzNDQ3MTg0OX0.XGu1tm_OqlSrc5BMDMzOrlhxLZo1YnpCUT0_j2U1mQt86nJzf_Hp85JfapZj2QeeUz91H6-Ei8FR1i4ICEfjMcoZOW1Azc89qUNfUgWeyjqZ7wCHSsbHAwabE74RFAS9YAja8_ynUvCARfDEtoqcreNgmbw3ZntzAqpuuNBXYfbr87kMvu_wZ7fWjLKM91CvuXytQBwtieTyjAFnTXmEL60Pdu-JSQfHCbS5H39ZHlnYxEO6qztIjvbnQokhjHDGc4PMCx0wfzrEet1ojNOCnbfmaYE5NQudquzQNZtqZfn8f4B-sQhECElnOXagHlafWO5RayS0dCb1mTfr8orcCA"}]},"id":10,"method":"ShareRequest","jsonrpc":"2.0"}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"Index":"1c724","PublicKey":{"X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64"},"Threshold":1,"Verifiers":{"torus-direct-mock-ios":["michael@tor.us"]},"Share":"OTNhMzBjODY1YjM4OTNiNWQxOWQ2MmNmZmY1YjUzNTE1NzViZjZiMmM3ZmM0YWFmZTRiYzY0ZjA3YjkzNjU0MzczYzhjNmIyYjQ0ZjIzNTIyZWUwOGRmZWVjNzFlMjVk","Metadata":{"iv":"6e8150c48e9eaae7f03d71fe339e8ddf","ephemPublicKey":"048a363e0572bb294e979e5588488d3f702ea99df104b1b9a82e52505d85983d6ea11061a70a9bd99b2e77a0dc5e816eb1080618f96865ef318129711cd9f6634c","mac":"94d7c5a01d2a01379abb3a3a7c604910f8d58ac0f75c427392ea7c8c8085509c","mode":"AES256"}}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Vary":"Origin","Server":"nginx/1.19.9","Content-Length":"722","Content-Type":"application/json","Date":"Sun, 17 Oct 2021 10:57:32 GMT"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "teal-15-4.torusnode.com", - scheme: "https", - path: "/jrpc", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"params":{"encrypted":"yes","item":[{"verifieridentifier":"torus-direct-mock-ios","nodesignatures":[{"signature":"f94f88b5a2fff06463fe0cb4569a652a11f351061dcd5b15e466274e374eb2992632153bda0c017d9c83916b82f1daa3ee5ac9990201d73a18915224a828b6a41b","nodepubx":"1363aad8868cacd7f8946c590325cd463106fb3731f08811ab4302d2deae35c3","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"d77eebe5cdf466b475ec892d5b4cffbe0c1670525debbd97eee6dae2f87a7cbe"},{"nodepubx":"7c8cc521c48690f016bea593f67f88ad24f447dd6c31bbab541e59e207bf029d","nodepuby":"b359f0a82608db2e06b953b36d0c9a473a00458117ca32a5b0f4563a7d539636","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"ee5b3560bc5b394326ddb784970eb27c995b77ceac9ce04ddffe72a52542dffd7b90b30c50b69481b43f04a0373b632798bac8fcdf8d695ead606200e0a24fc41c"},{"signature":"739487dab15bc238d32db83faf7b0aeb57f6863ac079aa331605eee9e076567c5cfa588978128af9d2c160d92a30197ccec8ab8c24ea68a3ac540a2534f65e261c","nodepubx":"8a86543ca17df5687719e2549caa024cf17fe0361e119e741eaee668f8dd0a6f","data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","nodepuby":"9cdb254ff915a76950d6d13d78ef054d5d0dc34e2908c00bb009a6e4da701891"},{"data":"mug00\u001cf9a9e61d68072c950e5dc8baf824b810d52073e9e3748e35f9a534502bac8a5b\u001c3b695585f9c5ac4a4f036757c8873d01f51b68d5e8f0274e2dc4ebbc7daa7025\u001c03b18e36aa6c864091cd7d6536d30a3808c772f18479d753e12847266144c10e\u001ctorus-direct-mock-ios\u001c1634468252","signature":"d24ccf58546df41bc8506b467e017ec64d941feb442a02001bea1c014dbe4d6b01317473884284d038ea116e6040ab25dad9901ee94d41dd33674cd105bc32151b","nodepuby":"f63d40df480dacf68922004ed36dbab9e2969181b047730a5ce0797fb6958249","nodepubx":"25a98d9ae006aed1d77e81d58be8f67193d13d01a9888e2923841894f4b0bf9c"}],"verifier_id":"michael@tor.us","idtoken":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImFkZDhjMGVlNjIzOTU0NGFmNTNmOTM3MTJhNTdiMmUyNmY5NDMzNTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI2MzYxOTk0NjUyNDItZmQ3dWp0b3JwdnZ1ZHRzbDN1M2V2OTBuaWplY3RmcW0uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDkxMTE5NTM4NTYwMzE3OTk2MzkiLCJoZCI6InRvci51cyIsImVtYWlsIjoibWljaGFlbEB0b3IudXMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6InRUNDhSck1vdGFFbi1UN3dzc2U3QnciLCJub25jZSI6InZSU2tPZWwyQTkiLCJuYW1lIjoiTWljaGFlbCBMZWUiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUFUWEFKd3NCYjk4Z1NZalZObEJCQWhYSmp2cU5PdzJHRFNlVGYwSTZTSmg9czk2LWMiLCJnaXZlbl9uYW1lIjoiTWljaGFlbCIsImZhbWlseV9uYW1lIjoiTGVlIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE2MzQ0NjgyNDksImV4cCI6MTYzNDQ3MTg0OX0.XGu1tm_OqlSrc5BMDMzOrlhxLZo1YnpCUT0_j2U1mQt86nJzf_Hp85JfapZj2QeeUz91H6-Ei8FR1i4ICEfjMcoZOW1Azc89qUNfUgWeyjqZ7wCHSsbHAwabE74RFAS9YAja8_ynUvCARfDEtoqcreNgmbw3ZntzAqpuuNBXYfbr87kMvu_wZ7fWjLKM91CvuXytQBwtieTyjAFnTXmEL60Pdu-JSQfHCbS5H39ZHlnYxEO6qztIjvbnQokhjHDGc4PMCx0wfzrEet1ojNOCnbfmaYE5NQudquzQNZtqZfn8f4B-sQhECElnOXagHlafWO5RayS0dCb1mTfr8orcCA"}]},"id":10,"method":"ShareRequest","jsonrpc":"2.0"}"#) - ), - responseBody: Data(#"{"jsonrpc":"2.0","result":{"keys":[{"Index":"1c724","PublicKey":{"X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64"},"Threshold":1,"Verifiers":{"torus-direct-mock-ios":["michael@tor.us"]},"Share":"M2U2OGMxYzg0ODFhMDAxNTFkOWE1MTMyMmZjNjlkOWQ0MWUzZjgzZDQ0NGJlNmQ1YzdlMDEwNzliZTRhYjg4OTdmM2Y3YWRiNjcwZDZhMTA5MDk4NjE2OGI2OTBlZWM2","Metadata":{"iv":"29a6a7bb27cd3a9a13cfb47818e894a0","ephemPublicKey":"0489299b0ccc867e2596e2069dce3c129e163f9e8c47c51c2dd2ea5aa56af88b4cfa4cfab34ece86512dc0995fcbab1fe9206609cafa648a66bc35d95c1795dd41","mac":"541759eb560a77517057c452b11113630e2ac32de7ba2addab8643ff52a19f59","mode":"AES256"}}]},"id":10}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Date":"Sun, 17 Oct 2021 10:57:33 GMT","Content-Type":"application/json","Content-Length":"722","Vary":"Origin","Server":"nginx/1.19.9"}"#) as! [String: String] - ), - - Stub( - requestMatcher: stubMatcherWithBody( - host: "metadata.tor.us", - scheme: "https", - path: "/get", - method: "POST", - requestHeaders: mustDecodeJSON(#"{"Accept":"application/json","Content-Type":"application/json"}"#) as! [String: String], - body: mustDecodeJSON(#"{"pub_key_X":"22d225892d5d149c0486bfb358b143568d1a951c39d5ada061a48c06c48afe39","pub_key_Y":"fcd9074bff4b5097489b79f951146d66bbcd05dc6acf68b8d0afc271fb73cf64"}"#) - ), - responseBody: Data(#"{"message":""}"#.utf8), - statusCode: 200, - responseHeaders: mustDecodeJSON(#"{"Content-Type":"application/json; charset=utf-8","Etag":"W/\"e-JWOqSwGs6lhRJiUZe/mVb6Mua74\"","x-xss-protection":"0","x-content-type-options":"nosniff","Vary":"Origin, Accept-Encoding","x-frame-options":"SAMEORIGIN","referrer-policy":"no-referrer","content-security-policy":"default-src 'self';base-uri 'self';block-all-mixed-content;font-src 'self' https: data:;frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests","Date":"Sun, 17 Oct 2021 10:57:33 GMT","x-dns-prefetch-control":"off","x-permitted-cross-domain-policies":"none","Strict-Transport-Security":"max-age=15552000; includeSubDomains","x-download-options":"noopen","Content-Length":"14","expect-ct":"max-age=0"}"#) as! [String: String] - ) -] - -private let httpBodyKey = "StubURLProtocolHTTPBody" - -private struct Stub { - let requestMatcher: (URLRequest) -> Bool - let responseBody: Data? - let statusCode: Int - let responseHeaders: [String: String] -} - -public class StubURLProtocol: URLProtocol { - private static let terminateUnknownRequest = true - - private static let stubs = injectedStubs - - private static let urls = injectedURLs - - private class func matchStub(req: URLRequest) -> Stub? { - var inputReq: URLRequest - if let httpBodyData = httpBodyStreamToData(stream: req.httpBodyStream) { - let mutableReq = (req as NSURLRequest).mutableCopy() as! NSMutableURLRequest - setProperty(httpBodyData, forKey: httpBodyKey, in: mutableReq) - inputReq = mutableReq as URLRequest - } else { - inputReq = req - } - for stub in stubs { - if stub.requestMatcher(inputReq) { - return stub - } - } - return nil - } - - override public class func canInit(with request: URLRequest) -> Bool { - var cleanURL: URL? { - var comp = URLComponents() - comp.scheme = request.url?.scheme - comp.host = request.url?.host - comp.path = request.url?.path ?? "/" - return comp.url - } - if urls.contains(cleanURL) { - return true - } - return terminateUnknownRequest - } - - override public class func canonicalRequest(for request: URLRequest) -> URLRequest { - return request - } - - override public func startLoading() { - guard let url = request.url else { - fatalError("Request has no URL") - } - var cleanURL: URL? { - var comp = URLComponents() - comp.scheme = url.scheme - comp.host = url.host - comp.path = url.path - return comp.url - } - if !StubURLProtocol.urls.contains(cleanURL) { - fatalError("URL not mocked, inconsistent injectedURLs: \(url.absoluteString)") - } - if let stub = StubURLProtocol.matchStub(req: request) { - let res = HTTPURLResponse(url: url, statusCode: stub.statusCode, httpVersion: nil, headerFields: stub.responseHeaders)! - client?.urlProtocol(self, didReceive: res, cacheStoragePolicy: .notAllowed) - if let d = stub.responseBody { - client?.urlProtocol(self, didLoad: d) - } - } else { - fatalError("URL not mocked: \(url.absoluteString)") - } - client?.urlProtocolDidFinishLoading(self) - } - - override public func stopLoading() { - } -} diff --git a/Tests/CustomAuthTests/StubURLProtocolTests.swift b/Tests/CustomAuthTests/StubURLProtocolTests.swift deleted file mode 100644 index a5ef2ca..0000000 --- a/Tests/CustomAuthTests/StubURLProtocolTests.swift +++ /dev/null @@ -1,30 +0,0 @@ -import CustomAuth -import FetchNodeDetails -import Foundation -import OSLog -import TorusUtils -import XCTest - -final class StubURLProtocolTests: XCTestCase {} - -public class StubMockTorusUtils: TorusUtils { - override open func getTimestamp() -> TimeInterval { - let ret = 0.0 - print("[StubMockTorusUtils] getTimeStamp(): ", ret) - return ret - } - - open func generatePrivateKeyData() -> Data? { - let ret = Data(base64Encoded: "FBz7bssmbsV6jBWoOJpkVOu14+6/Xgyt1pxTycODG08=") - return ret - } -} - -public class StubMockCASDKFactory: CASDKFactoryProtocol { - public func createTorusUtils(loglevel: OSLogType, urlSession: URLSession, enableOneKey: Bool, network: TorusNetwork) -> AbstractTorusUtils { - return StubMockTorusUtils(loglevel: loglevel, urlSession: urlSession, enableOneKey: enableOneKey, network: .sapphire(.SAPPHIRE_DEVNET), clientId: "Your Client ID") - } - - public init() { - } -}