The translation was generated automatically and may contain mistakes
Hand-crafted captcha
If you are using OkHttp We recommend applying solution with a ready-made Interceptor .
This instruction describes how to manually adjust the display and processing of the captcha if you are not using OkHttp , as well as examples independent implementation of Interceptors .
For manual processing of captcha in VK ID Captcha SDK Android, listener interfaces are used. The processing depends on the type of error that came from the API VKontakte>
- •Error with code
14. More details in section Error withredirect_uri. - •
Before processing is required connect VK ID Captcha SDK Android.
Error handling with code 14
- 1.
Handle it captcha Error from the API VKontakte and get the URL from the field
redirect_uri.Kotlinval url = "https://api.vk.ru/method/some_method" val request = Request.Builder().url(url).build() val response = OkHttpClient.Builder().build().newCall(request).execute() val responseBody = JSONObject(response.peekBody(Long.MAX_VALUE).string()) val error14ChallengeUrl = if (responseBody.has("error")) { val error = responseBody.getJSONObject("error") val redirectUri = if (error.getInt("error_code") == 14) { error.getString("redirect_uri") } else { null } } - 2.
Set up captcha tracking using the interface
VKCaptchaResultListener:Kotlinval listener = object : VKCaptchaResultListener { override fun onResult(result: VKCaptchaResult) { // результат прохождения капчи } } - 3.
To display the captcha, call the method
VKCaptcha.openCaptcha()and pass it on:- •URL RECEIVED from the field
redirect_uriна шаге 1. - •The domain where the captcha came from.
- •Captcha tracking interface
VKCaptchaResultListener.
KotlinVKCaptcha.openCaptcha(domain, redirectUri, listener) - •
- 4.
Get the result of passing the captcha (token
success_token) in the methodonResult()inside the transmitted listener interface. - 5.
Pass the received token
success_tokenin the query parameter of the query to the API VKontakte for which the captcha had to be passed.Kotlinval url = HttpUrl.Builder().addQueryParameter("success_token", token).build() val request = Request.Builder().url(url).build()
Handling Headers X-Challenge ?1? X-Challenge-URL
- 1.
Handle Headers
X-Challenge?1?X-Challenge-Urlfrom API VKontakte remember the URL from the headerX-Challenge-UrlYou will need it in the next step.Kotlinval url = "https://api.vk.ru/method/some_method" val request = Request.Builder().url(url).build() val response = OkHttpClient.Builder().build().newCall(request).execute() val challengeUrl = domain + response.headers("X-Challenge-Url").firstOrNull() - 2.
Set up captcha tracking using the interface
VKCaptchaResultListener:Kotlinval listener = object : VKCaptchaResultListener { override fun onResult(result: VKCaptchaResult) { // результат прохождения when (result) { is VKCaptchaResult.Success -> // успешное прохождение капчи is VKCaptchaResult.Error -> // ошибка при прохождении капчи } } } - 3.
To display the captcha, call the method
VKCaptcha.openCaptcha().
Send it:- •
challengeUrlwhich you received when you joineddomain?1?X-Challenge-Urlна шаге 1. - •The domain where the captcha came from.
Note that the domain must be specified with a protocol, for example:https://api.vk.ru. - •Captcha tracking interface
VKCaptchaResultListener.
KotlinVKCaptcha.openCaptcha(domain, challengeUrl, listener) - •
- 4.
Get the result of passing the captcha (token) in the method
onResult()inside the transmitted listener interface. - 5.
Pass the token in the header
headerв параметреX-Challenge-Solution.Kotlinval request = Request.Builder().header("X-Challenge-Solution", token).build()
Independent Integration with Interceptors
You can manually configure captcha display and processing as described above to implement its interceptor.
The following are examples of self-implementation of an interseptor for OkHttp which can be used as a basis for their implementation:
- •When handling an error with code
14. More details in section Error withredirect_uri. - •
Error with code 14
An example implementation of an interseptor for OkHttp Error handling with code 14 :
// Интерсептор, который обрабатывает ошибку с кодом `14`
public class Error14HandlingInterceptor(
private val domains: Set<String> = emptySet(),
) : Interceptor {
private val cookie = AtomicReference<String?>(null)
private companion object {
private const val CAPTCHA_ERROR_CODE = 14
private val executor = Executors.newSingleThreadExecutor()
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().withCookie()
val response = chain.proceed(request)
response.parseCookie()
if (request.shouldSkipCaptcha()) return response
val redirectUri = response.getRedirectUri() ?: return response
val token = passCaptchaAndGetToken(redirectUri)
return chain.proceed(chain.request().withCookie().withSuccessToken(token))
}
// Запускает процесс проверки капчи
private fun passCaptchaAndGetToken(redirectUri: String): String = synchronized(this) {
val tokenResult = AtomicReference<Result<String>>(Result.failure(Exception("No result")))
executor.submit {
VKCaptcha.openCaptcha(
domain = redirectUri.toHttpUrl().run { "$scheme://$host" },
redirectUri = redirectUri,
listener = object : VKCaptchaResultListener {
override fun onResult(result: VKCaptchaResult) {
synchronized(tokenResult) {
tokenResult.set(wrapResult(result))
tokenResult.notifyAll()
}
}
}
)
}
synchronized(tokenResult) {
if (tokenResult.get().getOrNull() == null) {
tokenResult.wait()
}
tokenResult.get().getOrThrow()
}
}
// Преобразует результат проверки капчи в результат, который содержит полученный токен
private fun wrapResult(result: VKCaptchaResult): Result<String> {
return when (result) {
is VKCaptchaResult.Error -> Result.failure(Exception(result.error.message, result.error.error))
is VKCaptchaResult.Success -> Result.success(result.token)
}
}
// Добавляет полученный токен в запрос
private fun Request.withSuccessToken(token: String): Request {
return newBuilder()
.url(url.newBuilder().addQueryParameter("success_token", token).build())
.build()
}
// Получает redirect_uri из ответа
private fun Response.getRedirectUri(): String? {
val responseBody = JSONObject(peekBody(Long.MAX_VALUE).string())
return if (responseBody.has("error")) {
val error = responseBody.getJSONObject("error")
if (error.getInt("error_code") == CAPTCHA_ERROR_CODE) {
error.getString("redirect_uri")
} else {
null
}
} else {
null
}
}
// Проверяет, нужно ли запускать капчу для домена, переданного в запросе
private fun Request.shouldSkipCaptcha(): Boolean {
return !domains.contains(url.toUrl().host) && domains.isNotEmpty()
}
// Получает cookie из ответа
private fun Response.parseCookie() {
headers("Set-Cookie").firstOrNull { it.contains("remixstlid") }?.let(cookie::set)
}
// Добавляет cookie в запрос
private fun Request.withCookie(): Request {
return newBuilder().apply { cookie.get()?.let { addHeader("Cookie", it) } }.build()
}
}
// Добавляет интерсептор в HTTP-клиент
OkHttpClient.Builder().addInterceptor(Error14HandlingInterceptor())For headings X-Challenge ?1? X-Challenge-URL
An example implementation of an interseptor for OkHttp When processing headers X-Challenge-Solution ?1? X-Challenge-Url :
// Интерсептор, который обрабатывает ошибки в заголовках
public class CaptchaHeadersInterceptor(
private val domains: Set<String> = emptySet(),
) : Interceptor {
private companion object {
private const val HEADER_CHALLENGE_SOLUTION = "X-Challenge-Solution"
private const val HEADER_CHALLENGE_URL = "X-Challenge-Url"
private const val HEADER_CHALLENGE = "X-Challenge"
private val executor = Executors.newSingleThreadExecutor()
}
override fun intercept(chain: Interceptor.Chain): Response {
val domain = chain.request().getDomain()
val request = chain.request().withSavedChallengeSolution()
val response = chain.proceed(request)
if (shouldSkipCaptcha(response, domain)) return response
val challengeUrl = response.headers(HEADER_CHALLENGE_URL).firstOrNull() ?: return response
val token = passCaptchaAndGetToken(domain, challengeUrl)
response.close()
return chain.proceed(chain.request().withChallengeSolution(token))
}
// Запускает процесс проверки капчи
private fun passCaptchaAndGetToken(
domain: String,
challengeUrl: String,
): String = synchronized(this) {
val tokenResult = AtomicReference<Result<String>>(Result.failure(Exception("No result")))
executor.submit {
VKCaptcha.openCaptcha(
domain = domain,
redirectUri = domain + challengeUrl,
listener = object : VKCaptchaResultListener {
override fun onResult(result: VKCaptchaResult) {
synchronized(tokenResult) {
tokenResult.set(wrapResult(result))
tokenResult.notifyAll()
}
}
}
)
}
synchronized(tokenResult) {
if (tokenResult.get().getOrNull() == null) {
tokenResult.wait()
}
tokenResult.get().getOrThrow()
}
}
// Преобразует результат проверки капчи в kotlin Result
private fun wrapResult(result: VKCaptchaResult): Result<String> {
return when (result) {
is VKCaptchaResult.Error -> Result.failure(Exception(result.error.message, result.error.error))
is VKCaptchaResult.Success -> Result.success(result.token)
}
}
// Добавляет токен в заголовок запроса X-Challenge-Solution
private fun Request.withChallengeSolution(token: String): Request {
return newBuilder()
.header(HEADER_CHALLENGE_SOLUTION, token)
.build()
}
// Добавляет сохранённый токен в заголовок запроса X-Challenge-Solution
private fun Request.withSavedChallengeSolution(): Request {
val headersToken = VKCaptcha.getToken(getDomain())
return if (header(HEADER_CHALLENGE_SOLUTION) == null && headersToken != null) {
newBuilder().addHeader(HEADER_CHALLENGE_SOLUTION, headerToken).build()
} else {
this
}
}
// Проверяет, нужно ли передавать капчу в домен ответа
private fun shouldSkipCaptcha(response: Response, domain: String): Boolean {
return !domains.contains(domain) && domains.isNotEmpty() || response.headers(HEADER_CHALLENGE).all { it != "required" }
}
// Получает домен из запроса
private fun Request.getDomain(): String {
val url = url.toUrl()
return URL(url.protocol, url.host, url.port, "").toString()
}
}
// Добавляет интерсептор для обработки ошибок капчи в заголовках
OkHttpClient.Builder().addInterceptor(CaptchaHeadersInterceptor())