Compare commits

...

13 Commits

Author SHA1 Message Date
Artur b40f0ac185 Update to master branch 2019-10-03 16:09:07 +03:00
Artur Azarau dbcad92ff0 cartfile resolved updated 2019-10-02 09:51:39 +03:00
Artur Azarau 605e1aeeef branch changed 2019-10-02 09:49:50 +03:00
Ivan Babkin 6e712bf664 Carthage update 2019-06-17 19:43:13 +03:00
Ivan Babkin 0b860372e9
Merge pull request #3 from ZharaOo/fix/cartfile
Using RxSwift binary
2019-06-17 19:39:04 +03:00
Ivan Babkin c8f4fb213e Using RxSwift binary 2019-06-17 19:37:56 +03:00
Ivan Babkin 7ba0aff437
Merge pull request #2 from ZharaOo/feature/loading_observable
Added loadingObservable property
2019-06-17 19:32:57 +03:00
Ivan Babkin b3994f932e Added possibility to observe loading state changing 2019-06-17 19:32:23 +03:00
Ivan Babkin 1d178f7154
Merge pull request #1 from ZharaOo/fix/execute
Fixed unexpected error on old devices
2019-06-11 14:16:28 +03:00
Ivan Babkin 784187e911 Fixed unexpected error on old devices 2019-06-03 22:44:24 +03:00
Flávio Caetano 31a64e5967 Update issue template to include ReCaptcha v2 docs 2019-04-04 17:21:28 -03:00
Flávio Caetano ce8acad6a2 Add incompatibility with ReCaptcha v3 to README 2019-04-04 17:17:42 -03:00
Rachit Mishra 0e47c7264f Add warning to uncheck verify origin settings in recaptcha (#68)
* Add warning to uncheck verify origin 

If verify origin is checked it leads to JS error code 4

* Add screenshot for verify domain settings

* Update file name for verify origin settings

* Add example image for verify origin

* Remove redundant space
2019-03-20 10:59:44 -03:00
8 changed files with 112 additions and 11 deletions

View File

@ -1,14 +1,13 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Create a report to help us improve
--- ---
<!-- <!--
## Is it really a bug? ## Is it really a bug?
Before opening an issue, check the following: Before opening an issue, check the following:
1. You are using the **Client side integration** key 1. You are using the **SITE** key
2. The correct domain, with protocol, is setup. 2. The correct domain, with protocol, is setup.
3. You are using an **Invisible** reCAPTCHA key. 3. You are using an **Invisible** reCAPTCHA v2 key.
4. If the widget doesn't appear, that is expected since the library will try to resolve the challenge _invisibly_. 4. If the widget doesn't appear, that is expected since the library will try to resolve the challenge _invisibly_.
https://www.google.com/recaptcha/admin#site https://www.google.com/recaptcha/admin#site
@ -20,7 +19,7 @@ A clear and concise description of what the bug is.
## To Reproduce ## To Reproduce
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '...'
3. ... 3. ...
4. Profit (jk See error) 4. Profit (jk See error)

View File

@ -1 +1 @@
github "ReactiveX/RxSwift" ~> 4.3 binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxSwift/RxSwift.json"

View File

@ -1 +1 @@
github "ReactiveX/RxSwift" "4.4.0" binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxSwift/RxSwift.json" "4.5.0"

View File

@ -9,7 +9,7 @@
----- -----
Add Google's [Invisible ReCaptcha](https://developers.google.com/recaptcha/docs/invisible) to your project. This library Add Google's [Invisible ReCaptcha v2](https://developers.google.com/recaptcha/docs/invisible) to your project. This library
automatically handles ReCaptcha's events and retrieves the validation token or notifies you to present the challenge if automatically handles ReCaptcha's events and retrieves the validation token or notifies you to present the challenge if
invisibility is not possible. invisibility is not possible.
@ -17,8 +17,15 @@ invisibility is not possible.
#### _Warning_ ⚠️ #### _Warning_ ⚠️
Beware that this library only works for Invisible ReCaptcha keys! Make sure to check the Invisible reCAPTCHA option Beware that this library only works for ReCaptcha v2 Invisible keys! Make sure to check the reCAPTCHA
when creating your [API Key](https://www.google.com/recaptcha/admin). v2 Invisible badge option when creating your [API Key](https://www.google.com/recaptcha/admin/create).
![ReCaptcha v2 invisible key example](https://raw.githubusercontent.com/fjcaetano/ReCaptcha/master/example-v2-key.png)
You won't be able to use a ReCaptcha v3 key because it requires server-side validation. On v3, all
challenges succeed into a token which is then validated in the server for a score. For this reason,
a frontend app can't know on its own wether or not a user is valid since the challenge will always
result in a valid token.
## Installation ## Installation
@ -42,7 +49,7 @@ extension for the ReCaptcha framework.
## Usage ## Usage
Simply add `ReCaptchaKey` and `ReCaptchaDomain` (with a protocol) to your Info.plist and run: Simply add `ReCaptchaKey` and `ReCaptchaDomain` (with a protocol ex. http:// or https://) to your Info.plist and run:
``` swift ``` swift
let recaptcha = try? ReCaptcha() let recaptcha = try? ReCaptcha()

View File

@ -13,6 +13,8 @@ import WebKit
/** /**
*/ */
public class ReCaptcha { public class ReCaptcha {
public typealias BoolParameterClosure = (Bool) -> ()
fileprivate struct Constants { fileprivate struct Constants {
struct InfoDictKeys { struct InfoDictKeys {
static let APIKey = "ReCaptchaKey" static let APIKey = "ReCaptchaKey"
@ -101,6 +103,12 @@ public class ReCaptcha {
} }
} }
/// Callback for WebView loading state changing
public var onLoadingChanged: BoolParameterClosure? {
get { return manager.onLoadingChanged }
set { manager.onLoadingChanged = newValue }
}
/// The worker that handles webview events and communication /// The worker that handles webview events and communication
let manager: ReCaptchaWebViewManager let manager: ReCaptchaWebViewManager

View File

@ -18,6 +18,13 @@ internal class ReCaptchaWebViewManager {
static let ExecuteJSCommand = "execute();" static let ExecuteJSCommand = "execute();"
static let ResetCommand = "reset();" static let ResetCommand = "reset();"
static let BotUserAgent = "Googlebot/2.1" static let BotUserAgent = "Googlebot/2.1"
static let NumberOfDivsCommand = "document.getElementsByTagName(\"div\").length"
static let MinNumberOfDivs = 5
static let NumberOfDivsFinishedLoadingAttempts = 10
// A page doesn't have enough time to load on old devices
static let NumberOfDivsLoadingDelay = 0.5
} }
#if DEBUG #if DEBUG
@ -37,6 +44,9 @@ internal class ReCaptchaWebViewManager {
public var shouldSkipForTests = false public var shouldSkipForTests = false
#endif #endif
/// Callback for loading state changing
var onLoadingChanged: ReCaptcha.BoolParameterClosure?
/// Sends the result message /// Sends the result message
var completion: ((ReCaptchaResult) -> Void)? var completion: ((ReCaptchaResult) -> Void)?
@ -99,6 +109,7 @@ internal class ReCaptchaWebViewManager {
*/ */
init(html: String, apiKey: String, baseURL: URL, endpoint: String) { init(html: String, apiKey: String, baseURL: URL, endpoint: String) {
self.endpoint = endpoint self.endpoint = endpoint
self.decoder = ReCaptchaDecoder { [weak self] result in self.decoder = ReCaptchaDecoder { [weak self] result in
self?.handle(result: result) self?.handle(result: result)
} }
@ -126,6 +137,7 @@ internal class ReCaptchaWebViewManager {
Starts the challenge validation Starts the challenge validation
*/ */
func validate(on view: UIView) { func validate(on view: UIView) {
onLoadingChanged?(true)
#if DEBUG #if DEBUG
guard !shouldSkipForTests else { guard !shouldSkipForTests else {
completion?(.token("")) completion?(.token(""))
@ -166,7 +178,7 @@ internal class ReCaptchaWebViewManager {
/** Private methods for ReCaptchaWebViewManager /** Private methods for ReCaptchaWebViewManager
*/ */
fileprivate extension ReCaptchaWebViewManager { fileprivate extension ReCaptchaWebViewManager {
/** Executes the JS command that loads the ReCaptcha challenge. /** Executes the JS command that loads the ReCaptcha challenge after a page finished loading.
This method has no effect if the webview hasn't finished loading. This method has no effect if the webview hasn't finished loading.
*/ */
func execute() { func execute() {
@ -175,10 +187,65 @@ fileprivate extension ReCaptchaWebViewManager {
return return
} }
evaluateExecuteWhenLoadingFinished(count: 0)
}
/**
- parameter count: Number of checks of number of divs
Executes the JS command that returns number of divs.
*/
func evaluateExecuteWhenLoadingFinished(count: Int) {
webView.evaluateJavaScript(Constants.NumberOfDivsCommand) { [weak self] (result, error) -> Void in
if let error = error {
self?.decoder.send(error: .unexpected(error))
self?.onLoadingChanged?(false)
} else {
self?.handleNumberOfDivs(result: result, count: count)
}
}
}
/**
- parameters:
- result: Result of number of divs command evaluation
- count: Number of checks of divs count
Handles number of divs command result.
*/
func handleNumberOfDivs(result: Any?, count: Int) {
if let result = result as? Int, result >= Constants.MinNumberOfDivs {
evaluateExecute()
} else {
handleInvalidNumberOfDivsResult(count: count)
}
}
/**
- parameter count: Number of checks of number of divs
Handles invalid number of divs.
*/
func handleInvalidNumberOfDivsResult(count: Int) {
if count < Constants.NumberOfDivsFinishedLoadingAttempts {
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.NumberOfDivsLoadingDelay) { [weak self] in
self?.evaluateExecuteWhenLoadingFinished(count: count + 1)
}
} else {
decoder.send(error: .htmlLoadError)
onLoadingChanged?(false)
}
}
/**
Executes the JS command that loads the ReCaptcha challenge.
*/
func evaluateExecute() {
webView.evaluateJavaScript(Constants.ExecuteJSCommand) { [weak self] _, error in webView.evaluateJavaScript(Constants.ExecuteJSCommand) { [weak self] _, error in
if let error = error { if let error = error {
self?.decoder.send(error: .unexpected(error)) self?.decoder.send(error: .unexpected(error))
} }
self?.onLoadingChanged?(false)
} }
} }

View File

@ -9,6 +9,10 @@
import RxSwift import RxSwift
import UIKit import UIKit
public enum ReCaptchaRxError: Error {
case baseWasReleased
}
/// Makes ReCaptcha compatible with RxSwift extensions /// Makes ReCaptcha compatible with RxSwift extensions
extension ReCaptcha: ReactiveCompatible {} extension ReCaptcha: ReactiveCompatible {}
@ -64,4 +68,20 @@ public extension Reactive where Base: ReCaptcha {
base?.reset() base?.reset()
} }
} }
/**
Observable of loading state
(will not work if someone changes onLoadingChanged variable; current onLodinglChanged will not work after subscription)
*/
var loadingObservable: Observable<Bool> {
return .create { [weak base] observer -> Disposable in
guard let base = base else {
observer.onError(ReCaptchaRxError.baseWasReleased)
return Disposables.create()
}
base.onLoadingChanged = { observer.onNext($0) }
return Disposables.create { [weak base] in base?.onLoadingChanged = nil }
}
}
} }

BIN
example-v2-key.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB