com.github.stormbit.vksdk.vkapi.Auth.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vk-bot-sdk-kotlin Show documentation
Show all versions of vk-bot-sdk-kotlin Show documentation
The Kotlin library for working with VK api
The newest version!
package com.github.stormbit.vksdk.vkapi
import com.github.stormbit.vksdk.clients.VkUserClient
import com.github.stormbit.vksdk.exceptions.AuthException
import com.github.stormbit.vksdk.exceptions.BanException
import com.github.stormbit.vksdk.exceptions.TwoFactorException
import com.github.stormbit.vksdk.objects.TwoFactor
import com.github.stormbit.vksdk.utils.*
import com.github.stormbit.vksdk.utils.append
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.features.cookies.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import org.slf4j.LoggerFactory
import com.github.stormbit.vksdk.objects.Captcha as CaptchaObject
private val RE_LOGIN_HASH = Regex("name=\"lg_h\" value=\"([a-z0-9]+)\"")
private val RE_CAPTCHA_ID = Regex("onLoginCaptcha\\('(\\d+)'")
private val RE_AUTH_HASH = Regex("\\{.*?act: 'a_authcheck_code'.+?hash: '([a-z_0-9]+)'.*\\}")
private val RE_TOKEN_URL = Regex("location\\.href = \"(.*?)\"\\+addr;")
private val DEFAULT_USER_SCOPES = VkUserPermissions().apply {
all = true
}
class Auth(
private val login: String,
private val password: String,
private val httpClient: HttpClient,
private var appId: Int = 6222115,
private val scope: VkUserPermissions = DEFAULT_USER_SCOPES,
private val redirectUrl: String = ""
) {
companion object {
private const val STRING_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
}
private val log = LoggerFactory.getLogger(Auth::class.java)
suspend fun authByVk(
captcha: CaptchaObject? = null,
twoFactor: TwoFactor? = null
): Response {
val parameters = Parameters.build {
append("captcha_sid", captcha?.captchaSid)
append("captcha_key", captcha?.captchaCode)
append("code", twoFactor?.code)
append("remember", twoFactor?.remember?.toInt())
}
return oauth(parameters)
}
private suspend fun oauth(
parameters: Parameters = parametersOf()
): Response {
log.info("Logging in...")
val prms = Parameters.build {
append("grant_type", "password")
append("client_id", VkUserClient.CLIENT_ID)
append("client_secret", VkUserClient.CLIENT_SECRET)
append("username", login)
append("password", password)
append("v", VK_API_VERSION)
append("2fa_supported", 1)
appendAll(parameters)
}
val response = httpClient.get(VkUserClient.BASE_PROXY_OAUTH_URL + "token") {
url.parameters.appendAll(prms)
headers.appendAll(VkUserClient.HEADER)
}
val responseParsed = json.parseToJsonElement(response).jsonObject
if ("error" in responseParsed.toString()) {
if ("need_captcha" == responseParsed.getString("error")) {
log.info("Captcha code is required")
val captchaSid = responseParsed.getString("captcha_sid")!!
val captchaUrl = "https://api.vk.com/captcha.php?sid=$captchaSid"
return Response.Captcha(captchaSid, captchaUrl)
} else if ("need_validation" == responseParsed.getString("error")) {
if ("2fa_sms" in responseParsed.toString()) {
return Response.TwoFactor("")
} else if ("ban_info" in responseParsed.toString()) {
throw BanException(responseParsed.getString("error_description"))
}
} else if ("invalid_request" == responseParsed.getString("error")) {
if ("wrong_otp" in responseParsed.toString()) {
return Response.TwoFactor("")
}
} else if ("invalid_client" == responseParsed.getString("error")) {
return Response.Error(ErrorType.INVALID_CLIENT)
} else {
throw AuthException("Error: ${responseParsed["error_description"]}")
}
}
log.info("Authentication succeeded!")
val accessToken = responseParsed.getString("access_token")!!
val refreshToken = refreshToken(accessToken)["response"]!!.jsonObject
return Response.Success(refreshToken.getString("token")!!)
}
private suspend fun refreshToken(accessToken: String): JsonObject {
val params = Parameters.build {
append("access_token", accessToken)
append("receipt", VkUserClient.RECEIPT)
append("v", VK_API_VERSION)
}
val responseString = httpClient.get(BASE_API_URL + "auth.refreshToken") {
url.parameters.appendAll(params)
headers.appendAll(VkUserClient.HEADER)
}
val responseParsed = json.parseToJsonElement(responseString).jsonObject
if ("error" in responseParsed.toString()) {
throw AuthException("Error: ${responseParsed["error_description"]}")
}
return responseParsed
}
private suspend fun passTwoFactor(twoFactor: TwoFactor) {
val parameters = Parameters.build {
append("act", "a_authcheck_code")
append("al", "1")
append("code", twoFactor.code)
append("remember", twoFactor.remember.toInt())
append("hash", twoFactor.authHash)
}
val resp = httpClient.post("https://vk.com/al_login.php") {
headers.appendAll(VkUserClient.HEADER)
body = FormDataContent(parameters)
}
val data = resp.replace("[-]".toRegex(), "").toJsonObject()
val status = data.getJsonArray("payload")!!.getInt(0)
when (status) {
4 -> {
val path = data.getJsonArray("payload")!!.getJsonArray(1).getString(0).replace("[\\\\\"]".toRegex(), "")
httpClient.get("https://vk.com/$path")
}
in listOf(0, 8) -> return passTwoFactor(twoFactor)
2 -> throw TwoFactorException("ReCaptcha required")
}
throw TwoFactorException("Two factor authentication failed")
}
/**
* VK Authentication with getting cookies remixsid
* @param captcha Captcha object
* @return token
*/
suspend fun vkLogin(
captcha: CaptchaObject? = null,
twoFactor: TwoFactor? = null
): Response {
log.info("Logging in...")
var response = httpClient.get("https://vk.com/") {
userAgent(STRING_USER_AGENT)
}
val params = Parameters.build {
append("act", "login")
append("role", "al_frame")
append("_origin", "https://vk.com")
append("utf8", 1)
append("email", login)
append("pass", password)
append("lg_h", regexSearch(RE_LOGIN_HASH, response, 1)!!)
if (captcha != null) {
log.info("Using captcha code: ${captcha.captchaSid}: ${captcha.captchaCode}")
append("captcha_sid", captcha.captchaSid)
append("captcha_key", captcha.captchaCode)
}
}
response = httpClient.post("https://login.vk.com/") {
body = FormDataContent(params)
}
when {
"act=authcheck" in response -> {
log.info("Two Factor is required")
response = httpClient.get("https://vk.com/login?act=authcheck")
val authHash = regexSearch(RE_AUTH_HASH, response, 1)!!
if (twoFactor != null) {
passTwoFactor(twoFactor)
} else {
return Response.TwoFactor(authHash)
}
}
"onLoginCaptcha(" in response -> {
log.info("Captcha code is required")
val sid = regexSearch(RE_CAPTCHA_ID, response)!!
val url = "https://api.vk.com/captcha.php?sid=$sid"
return Response.Captcha(sid, url)
}
"onLoginReCaptcha(" in response -> {
log.info("Captcha code is required (recaptcha)")
val sid = System.currentTimeMillis().toString()
val url = "https://api.vk.com/captcha.php?sid=$sid"
return Response.Captcha(sid, url)
}
"onLoginFailed(4" in response -> return Response.Error(ErrorType.INVALID_CLIENT)
else -> {
if (checkSid()) log.info("Got remixsid")
else return Response.Error(ErrorType.UNKNOWN)
}
}
return apiLogin()
}
/**
* Получение токена через Desktop приложение
* @return token
*/
private suspend fun apiLogin(): Response {
for (cookie in listOf("p", "l")) {
if (httpClient.cookies("login.vk.com").none { it.name == cookie }) {
throw AuthException("API auth error (no login cookies)")
}
}
val params = Parameters.build {
append("client_id", appId)
append("scope", scope.mask)
append("response_type", "token")
append("redirect_url", redirectUrl)
}
var response = httpClient.get("https://oauth.vk.com/authorize") {
userAgent(STRING_USER_AGENT)
url.parameters.appendAll(params)
}
var responseString = response.receive()
if ("act=blocked" in response.request.url.toString()) {
return Response.Error(ErrorType.ACCOUNT_BLOCKED)
}
if ("access_token" !in response.request.url.toString()) {
val url = regexSearch(RE_TOKEN_URL, responseString, 1)
if (url != null) {
response = httpClient.get(url) {
userAgent(STRING_USER_AGENT)
}
responseString = response.receive()
}
}
when {
"access_token" in response.request.url.toString() -> {
val parts = response.request.url.toString()
.split("#", limit = 2)[1]
.split("&").subList(0, 1)
val token = parts[0].split("=")[1]
log.info("Authentication succeeded!")
return Response.Success(token)
}
"oauth.vk.com/error" in response.request.url.toString() -> {
val errorData = responseString.toJsonObject()
var errorText = errorData.getString("error_description")!!
if ("@vk.com" in errorText) {
errorText = errorData.getString("error")!!
}
throw AuthException("VK auth error: $errorText")
}
else -> return Response.Error(ErrorType.UNKNOWN)
}
}
private suspend fun checkSid(): Boolean {
return httpClient.cookies("").any { it.name == "remixsid" || it.name == "remixsid6" }
}
sealed class Response {
data class Error(val errorType: ErrorType) : Response()
data class Captcha(val sid: String, val url: String) : Response()
data class TwoFactor(val hash: String) : Response()
data class Success(val token: String) : Response()
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy