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>

Before processing is required connect VK ID Captcha SDK Android.

Error handling with code 14

  1. 1.

    Handle it captcha Error from the API VKontakte and get the URL from the field redirect_uri .

    Kotlin
    val 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. 2.

    Set up captcha tracking using the interface VKCaptchaResultListener :

    Kotlin
    val listener = object : VKCaptchaResultListener { override fun onResult(result: VKCaptchaResult) { // результат прохождения капчи } }
  3. 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 .
    Kotlin
    VKCaptcha.openCaptcha(domain, redirectUri, listener)
  4. 4.

    Get the result of passing the captcha (token success_token ) in the method onResult() inside the transmitted listener interface.

  5. 5.

    Pass the received token success_token in the query parameter of the query to the API VKontakte for which the captcha had to be passed.

    Kotlin
    val url = HttpUrl.Builder().addQueryParameter("success_token", token).build() val request = Request.Builder().url(url).build()

Handling Headers X-Challenge ?1? X-Challenge-URL

  1. 1.

    Handle Headers X-Challenge ?1? X-Challenge-Url from API VKontakte remember the URL from the header X-Challenge-Url You will need it in the next step.

    Kotlin
    val 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. 2.

    Set up captcha tracking using the interface VKCaptchaResultListener :

    Kotlin
    val listener = object : VKCaptchaResultListener { override fun onResult(result: VKCaptchaResult) { // результат прохождения when (result) { is VKCaptchaResult.Success -> // успешное прохождение капчи is VKCaptchaResult.Error -> // ошибка при прохождении капчи } } }
  3. 3.

    To display the captcha, call the method VKCaptcha.openCaptcha() .
    Send it:

    • •
      challengeUrl which you received when you joined domain ?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 .
    Kotlin
    VKCaptcha.openCaptcha(domain, challengeUrl, listener)
  4. 4.

    Get the result of passing the captcha (token) in the method onResult() inside the transmitted listener interface.

  5. 5.

    Pass the token in the header header в параметре X-Challenge-Solution .

    Kotlin
    val 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:

Error with code 14

An example implementation of an interseptor for OkHttp Error handling with code 14 :

Kotlin
// Интерсептор, который обрабатывает ошибку с кодом `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 :

Kotlin
// Интерсептор, который обрабатывает ошибки в заголовках 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())