Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
github "ReactiveX/RxSwift" ~> 6.2.0
github "wultra/WultraSSLPinning"
12 changes: 12 additions & 0 deletions Example/HCaptcha.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -486,16 +486,22 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-HCaptcha_Example/Pods-HCaptcha_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/HCaptcha-Core-RxSwift/HCaptcha.framework",
"${BUILT_PRODUCTS_DIR}/PowerAuth2/PowerAuth2.framework",
"${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework",
"${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework",
"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
"${BUILT_PRODUCTS_DIR}/WultraSSLPinning/WultraSSLPinning.framework",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/PowerAuthCore/PowerAuthCore.framework/PowerAuthCore",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HCaptcha.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PowerAuth2.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WultraSSLPinning.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PowerAuthCore.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -598,10 +604,16 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-HCaptcha_Objc_Example/Pods-HCaptcha_Objc_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/HCaptcha-Core/HCaptcha.framework",
"${BUILT_PRODUCTS_DIR}/PowerAuth2/PowerAuth2.framework",
"${BUILT_PRODUCTS_DIR}/WultraSSLPinning/WultraSSLPinning.framework",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/PowerAuthCore/PowerAuthCore.framework/PowerAuthCore",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HCaptcha.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PowerAuth2.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WultraSSLPinning.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PowerAuthCore.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down
18 changes: 16 additions & 2 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
PODS:
- AppSwizzle (1.3.1)
- HCaptcha/Core (2.0.0)
- HCaptcha/Core (2.0.0):
- WultraSSLPinning/PowerAuthIntegration
- HCaptcha/RxSwift (2.0.0):
- HCaptcha/Core
- RxSwift (~> 6.2.0)
- PowerAuth2 (1.6.2):
- PowerAuthCore (~> 1.6.2)
- PowerAuthCore (1.6.2)
- RxBlocking (6.2.0):
- RxSwift (= 6.2.0)
- RxCocoa (6.2.0):
Expand All @@ -13,6 +17,10 @@ PODS:
- RxSwift (= 6.2.0)
- RxSwift (6.2.0)
- SwiftLint (0.43.1)
- WultraSSLPinning/Lib (1.4.0)
- WultraSSLPinning/PowerAuthIntegration (1.4.0):
- PowerAuth2 (>= 1.6.0)
- WultraSSLPinning/Lib

DEPENDENCIES:
- AppSwizzle (~> 1.3)
Expand All @@ -25,24 +33,30 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- AppSwizzle
- PowerAuth2
- PowerAuthCore
- RxBlocking
- RxCocoa
- RxRelay
- RxSwift
- SwiftLint
- WultraSSLPinning

EXTERNAL SOURCES:
HCaptcha:
:path: "../"

SPEC CHECKSUMS:
AppSwizzle: db36e436f56110d93e5ae0147683435df593cabc
HCaptcha: a4c1efafe5a049c2db09d847736e369645d1079a
HCaptcha: 4ce547fc4e00a50fcad0d1a9811e67703dcdc0a1
PowerAuth2: 18365eda20a3aaec6e1d0d92679c7e90d646cc05
PowerAuthCore: 733c68b8f923d49304353ac5d7871ef49f948b38
RxBlocking: 0b29f7d2079109a8de49c411381bed7c33ef1eeb
RxCocoa: 4baf94bb35f2c0ab31bc0cb9f1900155f646ba42
RxRelay: e72dbfd157807478401ef1982e1c61c945c94b2f
RxSwift: d356ab7bee873611322f134c5f9ef379fa183d8f
SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52
WultraSSLPinning: 73a73de4f89b0ac98154c3ef761ed6eee7760749

PODFILE CHECKSUM: 44657674683c7639f916ef6eb6fae5387395d652

Expand Down
2 changes: 2 additions & 0 deletions HCaptcha.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ invisibility is not possible.
core.source_files = 'HCaptcha/Classes/*'
core.frameworks = ['WebKit', 'JavaScriptCore']

core.dependency 'WultraSSLPinning/PowerAuthIntegration'

core.resource_bundles = {
'HCaptcha' => ['HCaptcha/Assets/**/*']
}
Expand Down
9 changes: 6 additions & 3 deletions HCaptcha/Classes/HCaptcha.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public class HCaptcha: NSObject {
imghost: URL? = nil,
host: String? = nil,
theme: String = "light",
customTheme: String? = nil
customTheme: String? = nil,
sslPinning: Bool = false
) throws {
let infoDict = Bundle.main.infoDictionary

Expand All @@ -80,7 +81,8 @@ public class HCaptcha: NSObject {
imghost: imghost,
host: host,
theme: theme,
customTheme: customTheme)
customTheme: customTheme,
sslPinning: sslPinning)

self.init(manager: HCaptchaWebViewManager(
html: config.html,
Expand All @@ -89,7 +91,8 @@ public class HCaptcha: NSObject {
endpoint: config.getEndpointURL(locale: locale),
size: config.size,
rqdata: config.rqdata,
theme: config.actualTheme
theme: config.actualTheme,
sslPinning: config.sslPinning
))
}

Expand Down
7 changes: 6 additions & 1 deletion HCaptcha/Classes/HCaptchaConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ struct HCaptchaConfig {
self.customTheme ?? "\"\(theme)\""
}

/// Enable SSL pinning
let sslPinning: Bool

/// The Bundle that holds HCaptcha's assets
private static let bundle: Bundle = {
#if SWIFT_PACKAGE
Expand Down Expand Up @@ -144,7 +147,8 @@ struct HCaptchaConfig {
imghost: URL?,
host: String?,
theme: String,
customTheme: String?) throws {
customTheme: String?,
sslPinning: Bool) throws {
guard let filePath = HCaptchaConfig.bundle.path(forResource: "hcaptcha", ofType: "html") else {
throw HCaptchaError.htmlLoadError
}
Expand Down Expand Up @@ -187,6 +191,7 @@ struct HCaptchaConfig {
self.host = host
self.theme = theme
self.customTheme = customTheme
self.sslPinning = sslPinning
}

/**
Expand Down
14 changes: 13 additions & 1 deletion HCaptcha/Classes/HCaptchaWebViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Foundation
import WebKit
import WultraSSLPinning


/** Handles comunications with the webview containing the HCaptcha challenge.
Expand Down Expand Up @@ -89,6 +90,12 @@ internal class HCaptchaWebViewManager {
/// Keep error If it happens before validate call
fileprivate var lastError: HCaptchaError?

/// Enable SSL pinning
fileprivate var sslPinning: Bool

/// SSL pinning aware navigation delegate
fileprivate let webViewNavDelegate = HCaptchaWebViewNavDelegate()

/// The webview that executes JS code
lazy var webView: WKWebView = {
let webview = WKWebView(
Expand All @@ -99,6 +106,10 @@ internal class HCaptchaWebViewManager {
webview.accessibilityTraits = UIAccessibilityTraits.link
webview.isHidden = true

if sslPinning {
webview.navigationDelegate = webViewNavDelegate
}

return webview
}()

Expand All @@ -113,8 +124,9 @@ internal class HCaptchaWebViewManager {
- theme: Widget theme, value must be valid JS Object or String with brackets
*/
init(html: String, apiKey: String, baseURL: URL, endpoint: URL,
size: HCaptchaSize, rqdata: String?, theme: String) {
size: HCaptchaSize, rqdata: String?, theme: String, sslPinning: Bool) {
self.baseURL = baseURL
self.sslPinning = sslPinning
self.decoder = HCaptchaDecoder { [weak self] result in
self?.handle(result: result)
}
Expand Down
30 changes: 30 additions & 0 deletions HCaptcha/Classes/HCaptchaWebViewNavDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// HCaptchaWebViewNavDelegate.swift
// HCaptcha
//
// Copyright © 2022 HCaptcha. All rights reserved.
//

import Foundation
import WebKit
import WultraSSLPinning

class HCaptchaWebViewNavDelegate: UIResponder, WKNavigationDelegate {
fileprivate static var certStore: CertStore {
let publicKey = "BO47Jqs1wXV9dt1q1hMHuZzUPVzGNaPn9vUqRzleMiHAIYg+zZmktobi3nKVrtExIctL9f8aTdpfwhex21jbI9g="
let config = CertStoreConfiguration(serviceUrl: URL(string: "https://hcaptcha.com")!,
publicKey: publicKey)
return .powerAuthCertStore(configuration: config)
}

func webView(_ webView: WKWebView,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
switch HCaptchaWebViewNavDelegate.certStore.validate(challenge: challenge) {
case .trusted:
completionHandler(.performDefaultHandling, nil)
case .untrusted, .empty:
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/ReactiveX/RxSwift", .upToNextMajor(from: "6.2.0"))
.package(url: "https://github.com/wultra/ssl-pinning-ios")
],
targets: [
.target(
Expand Down