From ddee53699b5d6b3f4938035a403188158104ad82 Mon Sep 17 00:00:00 2001 From: itsokto <36706257+itsokto@users.noreply.github.com> Date: Tue, 16 Apr 2019 19:45:13 +0300 Subject: [PATCH] [WIP] Fix 809 (#823) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Добавить недостающие параметры авторизации #809 * Добавить JsonConverter атрибут над свойствами типа SafetyEnum * Сделать метод CreateAuthorizeUrl без параметров #809 * Рефакторинг: переместить асинхронные методы в соответствующий класс * Добавить свойство Code в параметры авторизации * Изменить данные для теста Тест не проходил. В одном из предыдущих коммитов зачем-то заменили данные ¯\_(ツ)_/¯ --- .../Infrastructure/ImplicitFlowTests.cs | 28 +- .../TestData/Models/money_transfer.json | 19 +- .../Authorization/IImplicitFlow.cs | 10 +- VkNet/Infrastructure/Constants.cs | 5 + VkNet/Model/ApiAuthParams.cs | 13 +- VkNet/Model/IApiAuthParams.cs | 25 +- VkNet/Utils/Async/Browser.Async.cs | 235 ++++++---- VkNet/Utils/Browser.cs | 406 ++---------------- VkNet/Utils/ImplicitFlow.cs | 83 ++-- 9 files changed, 305 insertions(+), 519 deletions(-) diff --git a/VkNet.Tests/Infrastructure/ImplicitFlowTests.cs b/VkNet.Tests/Infrastructure/ImplicitFlowTests.cs index 42e149d40..b3ac49013 100644 --- a/VkNet.Tests/Infrastructure/ImplicitFlowTests.cs +++ b/VkNet.Tests/Infrastructure/ImplicitFlowTests.cs @@ -11,6 +11,7 @@ using VkNet.Enums.Filters; using VkNet.Enums.SafetyEnums; using VkNet.Exception; +using VkNet.Infrastructure; using VkNet.Infrastructure.Authorization; using VkNet.Infrastructure.Authorization.ImplicitFlow; using VkNet.Model; @@ -28,26 +29,34 @@ public void CreateAuthorizeUrl() var scope = Settings.All|Settings.Offline; const string state = "123"; var display = Display.Mobile; - var builder = new StringBuilder("https://oauth.vk.com/authorize?"); + var builder = new StringBuilder("https://oauth.vk.com/authorize?"); builder.Append($"client_id={clientId}&"); - builder.Append("redirect_uri=https://oauth.vk.com/blank.html&"); - + builder.Append($"redirect_uri={Constants.DefaultRedirectUri}&"); builder.Append($"display={display}&"); - builder.Append($"scope={scope.ToUInt64()}&"); - builder.Append("response_type=token&"); + builder.Append($"scope={scope}&"); + builder.Append($"response_type={ResponseType.Token}&"); builder.Append("v=5.92&"); - builder.Append($"state={state}&"); builder.Append("revoke=1"); - var expected = builder.ToString(); + var mocker = new AutoMocker(); mocker.Setup(x => x.Version).Returns("5.92"); var implicitFlow = mocker.CreateInstance(); - var authorizeUrl = implicitFlow.CreateAuthorizeUrl(clientId, scope.ToUInt64(), display, state); + implicitFlow.SetAuthorizationParams(new ApiAuthParams + { + ApplicationId = clientId, + Settings = scope, + Display = display, + State = state, + RedirectUri = new Uri(Constants.DefaultRedirectUri), + Revoke = true + }); + + var authorizeUrl = implicitFlow.CreateAuthorizeUrl(); Assert.AreEqual(new Url(expected), authorizeUrl); } @@ -94,7 +103,8 @@ public async Task Authorize() Login = "login", Password = "pass", ApplicationId = 4268118, - Settings = Settings.All + Settings = Settings.All, + RedirectUri = new Uri(Constants.DefaultRedirectUri) }); var result = await implicitFlow.AuthorizeAsync().ConfigureAwait(false); diff --git a/VkNet.Tests/TestData/Models/money_transfer.json b/VkNet.Tests/TestData/Models/money_transfer.json index 0290ccff1..1fe782f70 100644 --- a/VkNet.Tests/TestData/Models/money_transfer.json +++ b/VkNet.Tests/TestData/Models/money_transfer.json @@ -1,18 +1,19 @@ { - "type": "money_request", - "money_request": { - "id": 139, - "from_id": 123, - "to_id": 62103461, - "processed": 0, + "type": "money_transfer", + "money_transfer": { + "id": 10263042, + "from_id": 62103461, + "to_id": 1, + "status": 1, + "date": 1526637398, "amount": { - "amount": "10000", + "amount": "30000", "currency": { "id": 643, "name": "RUB" }, - "text": "100 руб." + "text": "300 руб." }, - "init_url": "https://m.vk.com/payments?act=init_money_transfer&to_id=123&request_id=139&amount=100¤cy=RUB&hash=HASH&hash_2=HASH2&user_id=62103461&from=api" + "comment": "bug" } } \ No newline at end of file diff --git a/VkNet/Abstractions/Authorization/IImplicitFlow.cs b/VkNet/Abstractions/Authorization/IImplicitFlow.cs index a56100138..b4100d2d6 100644 --- a/VkNet/Abstractions/Authorization/IImplicitFlow.cs +++ b/VkNet/Abstractions/Authorization/IImplicitFlow.cs @@ -1,4 +1,5 @@ -using Flurl; +using System; +using Flurl; using JetBrains.Annotations; using VkNet.Enums.SafetyEnums; @@ -35,6 +36,13 @@ public interface IImplicitFlow : IAuthorizationFlow /// результатом авторизации. /// /// Возвращает Uri для авторизации + [Obsolete("Используйте перегрузку Url CreateAuthorizeUrl();\nПараметры авторизации должны быть уставленны вызовом void SetAuthorizationParams(IApiAuthParams authorizationParams);")] Url CreateAuthorizeUrl(ulong clientId, ulong scope, Display display, string state); + + /// + /// Построить URL для авторизации. + /// + /// Возвращает Uri для авторизации + Url CreateAuthorizeUrl(); } } \ No newline at end of file diff --git a/VkNet/Infrastructure/Constants.cs b/VkNet/Infrastructure/Constants.cs index 59af51973..bcb57fed2 100644 --- a/VkNet/Infrastructure/Constants.cs +++ b/VkNet/Infrastructure/Constants.cs @@ -9,5 +9,10 @@ public static class Constants /// Токен /// public const string AccessToken = "access_token"; + + /// + /// redirect_uri по умолчанию. + /// + public const string DefaultRedirectUri = "https://oauth.vk.com/blank.html"; } } \ No newline at end of file diff --git a/VkNet/Model/ApiAuthParams.cs b/VkNet/Model/ApiAuthParams.cs index d25edf3bb..f457d5008 100644 --- a/VkNet/Model/ApiAuthParams.cs +++ b/VkNet/Model/ApiAuthParams.cs @@ -87,14 +87,17 @@ public class ApiAuthParams : IApiAuthParams /// public bool? Revoke { get; set; } + /// + public string Code { get; set; } + /// /// Формирует параметры авторизации по минимальному набору необходимых полей /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// public static ApiAuthParams Format(ulong appId, string login, string password, Settings settings) { return new ApiAuthParams diff --git a/VkNet/Model/IApiAuthParams.cs b/VkNet/Model/IApiAuthParams.cs index 58678d700..5291a9136 100644 --- a/VkNet/Model/IApiAuthParams.cs +++ b/VkNet/Model/IApiAuthParams.cs @@ -31,7 +31,8 @@ public interface IApiAuthParams /// См. описание /// /// - /// ВНИМАНИЕ!!! Settings.All по умолчанию не содержит Settings.Offline и Settings.Messages + /// ВНИМАНИЕ!!! Settings.All по умолчанию не содержит Settings.Offline и + /// Settings.Messages /// Settings Settings { get; set; } @@ -99,8 +100,10 @@ public interface IApiAuthParams string ClientSecret { get; set; } /// - /// Если возникли проблемы с получением кода из приложения или пользователь не хочет использовать приложение для генерации кодов, - /// Вы можете форсировать получение кода по SMS, повторно передав запрос авторизации с этим параметром равным true + /// Если возникли проблемы с получением кода из приложения или пользователь не + /// хочет использовать приложение для генерации кодов, + /// Вы можете форсировать получение кода по SMS, повторно передав запрос + /// авторизации с этим параметром равным true /// bool? ForceSms { get; set; } @@ -111,7 +114,8 @@ public interface IApiAuthParams Display Display { get; set; } /// - /// Адрес, на который будет переадресован пользователь после прохождения авторизации / передан code. + /// Адрес, на который будет переадресован пользователь после прохождения + /// авторизации / передан code. /// Uri RedirectUri { get; set; } @@ -121,7 +125,7 @@ public interface IApiAuthParams string State { get; set; } /// - /// Передайте true, чтобы включить поддержку двухфакторной аутентификации. + /// Передайте true , чтобы включить поддержку двухфакторной аутентификации. /// bool? TwoFactorSupported { get; set; } @@ -138,9 +142,16 @@ public interface IApiAuthParams ResponseType ResponseType { get; set; } /// - /// Параметр, указывающий, что необходимо не пропускать этап подтверждения прав, даже если пользователь уже авторизован. - /// (При true) + /// Параметр, указывающий, что необходимо не пропускать этап подтверждения прав, + /// даже если пользователь уже авторизован. + /// (При true ) /// bool? Revoke { get; set; } + + /// + /// Код подтверждения двухфакторной авторизации / временный код, полученный после + /// прохождения авторизации. + /// + string Code { get; set; } } } \ No newline at end of file diff --git a/VkNet/Utils/Async/Browser.Async.cs b/VkNet/Utils/Async/Browser.Async.cs index 7baeb5e7d..4a831b37e 100644 --- a/VkNet/Utils/Async/Browser.Async.cs +++ b/VkNet/Utils/Async/Browser.Async.cs @@ -4,9 +4,9 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Logging; -using VkNet.Enums.Filters; -using VkNet.Enums.SafetyEnums; +using VkNet.Enums; using VkNet.Exception; +using VkNet.Infrastructure.Authorization.ImplicitFlow; using VkNet.Model; namespace VkNet.Utils @@ -14,6 +14,28 @@ namespace VkNet.Utils /// public partial class Browser { + /// + public async Task AuthorizeAsync() + { + var authorizeUrlResult = await OpenAuthDialogAsync().ConfigureAwait(false); + + return await NextStepAsync(authorizeUrlResult).ConfigureAwait(false); + } + + /// + public async Task ValidateAsync(string validateUrl) + { + var result = await OldValidateAsync(validateUrl, _authParams.Phone).ConfigureAwait(false); + + return new AuthorizationResult + { + AccessToken = result.AccessToken, + ExpiresIn = result.ExpiresIn, + UserId = result.ExpiresIn, + State = result.State + }; + } + /// /// Асинхронное получение json по url-адресу /// @@ -29,58 +51,6 @@ public async Task GetJsonAsync(string methodUrl, IEnumerable - /// Асинхронная авторизация на сервере ВК - /// - /// Параметры авторизации - /// Информация об авторизации приложения - [UsedImplicitly] - public async Task AuthorizeAsync(IApiAuthParams authParams) - { - var authorizeUrlResult = await OpenAuthDialogAsync(authParams.ApplicationId, authParams.Settings) - .ConfigureAwait(false); - - return await NextStepAsync(authorizeUrlResult).ConfigureAwait(false); - -/* - - var loginFormPostResult = await FilledLoginFormAsync(authParams.Login, - authParams.Password, - authParams.CaptchaSid, - authParams.CaptchaKey, - authorizeUrlResult) - .ConfigureAwait(false); - - if (IsAuthSuccessfull(webCallResult: loginFormPostResult)) - { - return await EndAuthorizeAsync(loginFormPostResult, Proxy).ConfigureAwait(false); - } - - if (HasNotTwoFactor(authParams.TwoFactorAuthorization, loginFormPostResult)) - { - return await EndAuthorizeAsync(loginFormPostResult, Proxy).ConfigureAwait(false); - } - - _logger?.LogDebug(message: "Шаг 2.5.1. Заполнить код двухфакторной авторизации"); - - var twoFactorFormResult = - await FilledTwoFactorFormAsync(authParams.TwoFactorAuthorization, loginFormPostResult).ConfigureAwait(false); - - if (IsAuthSuccessfull(webCallResult: twoFactorFormResult)) - { - return await EndAuthorizeAsync(twoFactorFormResult, Proxy).ConfigureAwait(false); - } - - _logger?.LogDebug(message: "Шаг 2.5.2 Капча"); - var captchaForm = WebForm.From(result: twoFactorFormResult); - - var captcha = await WebCall.PostAsync(captchaForm, Proxy).ConfigureAwait(false); - - // todo: Нужно обработать капчу - - return await EndAuthorizeAsync(captcha, Proxy).ConfigureAwait(false);*/ - } - /// /// Заполнить форму двухфакторной авторизации асинхронно /// @@ -89,9 +59,9 @@ public async Task AuthorizeAsync(IApiAuthParams authParams) /// Ответ сервера vk private Task FilledTwoFactorFormAsync(Func code, WebCallResult loginFormPostResult) { - var codeForm = WebForm.From(result: loginFormPostResult) - .WithField(name: "code") - .FilledWith(value: code.Invoke()); + var codeForm = WebForm.From(loginFormPostResult) + .WithField("code") + .FilledWith(code.Invoke()); var task = WebCall.PostAsync(codeForm, Proxy); task.ConfigureAwait(false); @@ -101,7 +71,7 @@ private Task FilledTwoFactorFormAsync(Func code, WebCallR private Task FilledConsentAsync(WebCallResult loginFormPostResult) { - var form = WebForm.From(result: loginFormPostResult); + var form = WebForm.From(loginFormPostResult); var task = WebCall.PostAsync(form, Proxy); task.ConfigureAwait(false); @@ -109,26 +79,15 @@ private Task FilledConsentAsync(WebCallResult loginFormPostResult return task; } - private WebCallResult FilledConsent(WebCallResult loginFormPostResult) - { - var form = WebForm.From(result: loginFormPostResult); - - return WebCall.Post(form, Proxy); - } - /// /// Заполнить форму логин и пароль асинхронно /// /// Логин /// Пароль - /// ИД капчи - /// Значение капчи /// /// private Task FilledLoginFormAsync(string email , string password - , long? captchaSid - , string captchaKey , WebCallResult authorizeUrlResult) { var loginForm = WebForm.From(result: authorizeUrlResult) @@ -138,15 +97,36 @@ private Task FilledLoginFormAsync(string email .WithField(name: "pass") .FilledWith(value: password); - if (captchaSid.HasValue) - { - _logger?.LogDebug(message: "Шаг 2. Заполнение формы логина. Капча"); + var task = WebCall.PostAsync(loginForm, Proxy); + task.ConfigureAwait(false); - loginForm.WithField(name: "captcha_sid") - .FilledWith(value: captchaSid.Value.ToString()) - .WithField(name: "captcha_key") - .FilledWith(value: captchaKey); - } + return task; + } + + /// + /// Заполнить форму логин и пароль + /// + /// Логин + /// Пароль + /// + /// + private Task FilledCaptchaLoginFormAsync(string email, string password, WebCallResult authorizeUrlResult) + { + var loginForm = WebForm.From(authorizeUrlResult) + .WithField("email") + .FilledWith(email) + .And() + .WithField("pass") + .FilledWith(password); + + _logger?.LogDebug("Шаг 2. Заполнение формы логина. Капча"); + + var captchaKey = + _captchaSolver.Solve( + $"https://api.vk.com/captcha.php?sid={loginForm.GetFieldValue(AuthorizationFormFields.CaptchaSid)}&s=1"); + + loginForm.WithField("captcha_key") + .FilledWith(captchaKey); var task = WebCall.PostAsync(loginForm, Proxy); task.ConfigureAwait(false); @@ -243,17 +223,110 @@ private async Task EndAuthorizeAsync(WebCallResult result, IWe /// /// Открытие окна авторизации асинхронно /// - /// id приложения - /// Настройки приложения /// - private Task OpenAuthDialogAsync(ulong appId, [NotNull] Settings settings) + private Task OpenAuthDialogAsync() { - var url = CreateAuthorizeUrl(appId, settings.ToUInt64(), Display.Page, "123456"); + var url = CreateAuthorizeUrl(); var task = WebCall.MakeCallAsync(url.ToString(), Proxy); task.ConfigureAwait(false); return task; } + + private async Task OldValidateAsync(string validateUrl, string phoneNumber) + { + if (string.IsNullOrWhiteSpace(validateUrl)) + { + throw new ArgumentException("Не задан адрес валидации!"); + } + + if (string.IsNullOrWhiteSpace(phoneNumber)) + { + throw new ArgumentException("Не задан номер телефона!"); + } + + var validateUrlResult = await WebCall.MakeCallAsync(validateUrl, Proxy).ConfigureAwait(false); + + var codeForm = WebForm.From(validateUrlResult) + .WithField("code") + .FilledWith(phoneNumber.Substring(1, 8)); + + var codeFormPostResult = await WebCall.PostAsync(codeForm, Proxy).ConfigureAwait(false); + + return await EndAuthorizeAsync(codeFormPostResult, Proxy).ConfigureAwait(false); + } + + private async Task NextStepAsync(WebCallResult formResult) + { + var pageType = _vkAuthorization.GetPageType(formResult.ResponseUrl); + WebCallResult resultForm = null; + + switch (pageType) + { + case ImplicitFlowPageType.Error: + + { + _logger?.LogError("При авторизации произошла ошибка."); + + throw new VkAuthorizationException("При авторизации произошла ошибка."); + } + + case ImplicitFlowPageType.LoginPassword: + + { + LoginPasswordError++; + + if (LoginPasswordError >= MaxLoginPasswordError) + { + throw new VkAuthorizationException("Неверный логин или пароль."); + } + + _logger?.LogDebug("Ввод логина и пароля."); + + resultForm = await FilledLoginFormAsync(_authParams.Login, _authParams.Password, formResult) + .ConfigureAwait(false); + + break; + } + + case ImplicitFlowPageType.Captcha: + + { + _logger?.LogDebug("Капча."); + + resultForm = await FilledCaptchaLoginFormAsync(_authParams.Login, _authParams.Password, formResult) + .ConfigureAwait(false); + + break; + } + + case ImplicitFlowPageType.TwoFactor: + + { + _logger?.LogDebug("Двухфакторная авторизация."); + resultForm = await FilledTwoFactorFormAsync(_authParams.TwoFactorAuthorization, formResult).ConfigureAwait(false); + + break; + } + + case ImplicitFlowPageType.Consent: + + { + _logger?.LogDebug("Страница подтверждения доступа к скоупам."); + resultForm = await FilledConsentAsync(formResult).ConfigureAwait(false); + + break; + } + + case ImplicitFlowPageType.Result: + + { + return _vkAuthorization.GetAuthorizationResult(formResult.ResponseUrl); + } + } + + return await NextStepAsync(resultForm).ConfigureAwait(false); + } } } \ No newline at end of file diff --git a/VkNet/Utils/Browser.cs b/VkNet/Utils/Browser.cs index 583868aaf..7cb1fe765 100644 --- a/VkNet/Utils/Browser.cs +++ b/VkNet/Utils/Browser.cs @@ -2,16 +2,14 @@ using System.Collections.Generic; using System.Net; using System.Text; -using System.Threading.Tasks; using Flurl; using JetBrains.Annotations; using Microsoft.Extensions.Logging; using VkNet.Abstractions.Core; using VkNet.Enums; -using VkNet.Enums.Filters; using VkNet.Enums.SafetyEnums; using VkNet.Exception; -using VkNet.Infrastructure.Authorization.ImplicitFlow; +using VkNet.Infrastructure; using VkNet.Model; using VkNet.Utils.AntiCaptcha; @@ -20,14 +18,16 @@ namespace VkNet.Utils /// public partial class Browser : IBrowser { + private const ushort MaxLoginPasswordError = 2; + + private readonly ICaptchaSolver _captchaSolver; + /// /// Логгер /// [CanBeNull] private readonly ILogger _logger; - private IApiAuthParams _authParams; - /// /// Менеджер версий VkApi /// @@ -35,11 +35,7 @@ public partial class Browser : IBrowser private readonly IVkAuthorization _vkAuthorization; - private readonly ICaptchaSolver _captchaSolver; - - private ushort LoginPasswordError { get; set; } - - private const ushort MaxLoginPasswordError = 2; + private IApiAuthParams _authParams; /// public Browser([CanBeNull] ILogger logger, @@ -55,6 +51,8 @@ public Browser([CanBeNull] ILogger logger, _captchaSolver = captchaSolver; } + private ushort LoginPasswordError { get; set; } + /// public IWebProxy Proxy { get; set; } @@ -71,25 +69,30 @@ public void SetAuthorizationParams(IApiAuthParams authorizationParams) } /// - public async Task AuthorizeAsync() + [Obsolete( + "Используйте перегрузку Url CreateAuthorizeUrl();\nПараметры авторизации должны быть уставленны вызовом void SetAuthorizationParams(IApiAuthParams authorizationParams);")] + public Url CreateAuthorizeUrl(ulong clientId, ulong scope, Display display, string state) { - var result = await AuthorizeAsync(_authParams).ConfigureAwait(false); + _authParams.ApplicationId = clientId; + _authParams.Display = display; + _authParams.State = state; - return result; + return CreateAuthorizeUrl(); } /// - public Url CreateAuthorizeUrl(ulong clientId, ulong scope, Display display, string state) + public Url CreateAuthorizeUrl() { + _logger?.LogDebug("Построение url для авторизации."); var builder = new StringBuilder("https://oauth.vk.com/authorize?"); - builder.Append($"client_id={clientId}&"); - builder.Append("redirect_uri=https://oauth.vk.com/blank.html&"); - builder.Append($"display={display}&"); - builder.Append($"scope={scope}&"); - builder.Append("response_type=token&"); + builder.Append($"client_id={_authParams.ApplicationId}&"); + builder.Append($"redirect_uri={Constants.DefaultRedirectUri}&"); + builder.Append($"display={Display.Mobile}&"); + builder.Append($"scope={_authParams.Settings}&"); + builder.Append($"response_type={ResponseType.Token}&"); builder.Append($"v={_versionManager.Version}&"); - builder.Append($"state={state}&"); + builder.Append("state=123456&"); builder.Append("revoke=1"); return new Uri(builder.ToString()); @@ -112,148 +115,7 @@ public AuthorizationResult Validate(string validateUrl, string phoneNumber) /// public AuthorizationResult Validate(string validateUrl) { - var result = OldValidate(validateUrl, _authParams.Phone); - - return new AuthorizationResult - { - AccessToken = result.AccessToken, - ExpiresIn = result.ExpiresIn, - UserId = result.ExpiresIn, - State = result.State - }; - } - - /// - public async Task ValidateAsync(string validateUrl) - { - var result = await OldValidateAsync(validateUrl, _authParams.Phone).ConfigureAwait(false); - - return new AuthorizationResult - { - AccessToken = result.AccessToken, - ExpiresIn = result.ExpiresIn, - UserId = result.ExpiresIn, - State = result.State - }; - } - - /// - /// Заполнить форму двухфакторной авторизации - /// - /// Функция возвращающая код двухфакторной авторизации - /// Ответ сервера vk - /// Ответ сервера vk - private WebCallResult FilledTwoFactorForm(Func code, WebCallResult loginFormPostResult) - { - var codeForm = WebForm.From(loginFormPostResult) - .WithField("code") - .FilledWith(code.Invoke()); - - return WebCall.Post(codeForm, Proxy); - } - - /// - /// Проверка наличия двухфакторной авторизации - /// - /// Функция возвращающая код двухфакторной авторизации - /// Ответ сервера vk - /// - private bool HasNotTwoFactor(Func code, WebCallResult loginFormPostResult) - { - _logger?.LogDebug("Проверка наличия двухфакторной авторизации"); - - return code == null || WebForm.IsOAuthBlank(loginFormPostResult); - } - - /// - /// Заполнить форму логин и пароль - /// - /// Логин - /// Пароль - /// - /// - private WebCallResult FilledLoginForm(string email, string password, WebCallResult authorizeUrlResult) - { - var loginForm = WebForm.From(authorizeUrlResult) - .WithField("email") - .FilledWith(email) - .And() - .WithField("pass") - .FilledWith(password); - - return WebCall.Post(loginForm, Proxy); - } - - /// - /// Заполнить форму логин и пароль - /// - /// Логин - /// Пароль - /// - /// - private WebCallResult FilledCaptchaLoginForm(string email, string password, WebCallResult authorizeUrlResult) - { - var loginForm = WebForm.From(authorizeUrlResult) - .WithField("email") - .FilledWith(email) - .And() - .WithField("pass") - .FilledWith(password); - - _logger?.LogDebug("Шаг 2. Заполнение формы логина. Капча"); - - var captchaKey = - _captchaSolver.Solve( - $"https://api.vk.com//captcha.php?sid={loginForm.GetFieldValue(AuthorizationFormFields.CaptchaSid)}&s=1"); - - loginForm.WithField("captcha_key") - .FilledWith(captchaKey); - - return WebCall.Post(loginForm, Proxy); - } - - /// - /// Закончить авторизацию - /// - /// Результат - /// Настройки прокси - /// - /// - private VkAuthorization2 EndAuthorize(WebCallResult result, IWebProxy webProxy = null) - { - if (IsAuthSuccessfull(result)) - { - var auth = GetTokenUri(result); - - return VkAuthorization2.From(auth.ToString()); - } - - if (HasСonfirmationRights(result)) - { - _logger?.LogDebug("Требуется подтверждение прав"); - var authorizationForm = WebForm.From(result); - var authorizationFormPostResult = WebCall.Post(authorizationForm, webProxy); - - if (!IsAuthSuccessfull(authorizationFormPostResult)) - { - throw new VkApiException("URI должен содержать токен!"); - } - - var tokenUri = GetTokenUri(authorizationFormPostResult); - - return VkAuthorization2.From(tokenUri.ToString()); - } - - var captchaSid = HasCaptchaInput(result); - - if (!captchaSid.HasValue) - { - throw new VkApiException("Непредвиденная ошибка авторизации. Обратитесь к разработчику."); - } - - _logger?.LogDebug("Требуется ввод капчи"); - - throw new CaptchaNeededException(captchaSid.Value, "https://m.vk.com/captcha.php?sid=" + captchaSid.Value); + return ValidateAsync(validateUrl).Result; } private bool HasСonfirmationRights(WebCallResult result) @@ -282,19 +144,6 @@ private bool HasСonfirmationRights(WebCallResult result) return null; } - /// - /// Открытие окна авторизации - /// - /// id приложения - /// Настройки приложения - /// - private WebCallResult OpenAuthDialog(ulong appId, [NotNull] Settings settings) - { - var url = CreateAuthorizeUrl(appId, settings.ToUInt64(), Display.Page, "123456"); - - return WebCall.MakeCall(url.ToString(), Proxy); - } - /// /// Авторизация прошла успешно /// @@ -341,214 +190,9 @@ private Uri GetTokenUri(WebCallResult webCallResult) return null; } - private AuthorizationResult Authorize(IApiAuthParams authParams) - { - var authorizeUrlResult = OpenAuthDialog(authParams.ApplicationId, authParams.Settings); - - return NextStep(authorizeUrlResult); - } - private VkAuthorization2 OldValidate(string validateUrl, string phoneNumber) { - if (string.IsNullOrWhiteSpace(validateUrl)) - { - throw new ArgumentException("Не задан адрес валидации!"); - } - - if (string.IsNullOrWhiteSpace(phoneNumber)) - { - throw new ArgumentException("Не задан номер телефона!"); - } - - var validateUrlResult = WebCall.MakeCall(validateUrl, Proxy); - - var codeForm = WebForm.From(validateUrlResult) - .WithField("code") - .FilledWith(phoneNumber.Substring(1, 8)); - - var codeFormPostResult = WebCall.Post(codeForm, Proxy); - - return EndAuthorize(codeFormPostResult, Proxy); - } - - private async Task OldValidateAsync(string validateUrl, string phoneNumber) - { - if (string.IsNullOrWhiteSpace(validateUrl)) - { - throw new ArgumentException("Не задан адрес валидации!"); - } - - if (string.IsNullOrWhiteSpace(phoneNumber)) - { - throw new ArgumentException("Не задан номер телефона!"); - } - - var validateUrlResult = await WebCall.MakeCallAsync(validateUrl, Proxy).ConfigureAwait(false); - - var codeForm = WebForm.From(validateUrlResult) - .WithField("code") - .FilledWith(phoneNumber.Substring(1, 8)); - - var codeFormPostResult = await WebCall.PostAsync(codeForm, Proxy).ConfigureAwait(false); - - return await EndAuthorizeAsync(codeFormPostResult, Proxy).ConfigureAwait(false); - } - - private async Task NextStepAsync(WebCallResult formResult) - { - var pageType = _vkAuthorization.GetPageType(formResult.ResponseUrl); - WebCallResult resultForm = null; - - switch (pageType) - { - case ImplicitFlowPageType.Error: - - { - _logger?.LogError("При авторизации произошла ошибка."); - - throw new VkAuthorizationException("При авторизации произошла ошибка."); - } - - case ImplicitFlowPageType.LoginPassword: - - { - LoginPasswordError++; - - if (LoginPasswordError >= MaxLoginPasswordError) - { - throw new VkAuthorizationException("Неверный логин или пароль."); - } - - _logger?.LogDebug("Ввод логина и пароля."); - - resultForm = await FilledLoginFormAsync(_authParams.Login, - _authParams.Password, - _authParams.CaptchaSid, - _authParams.CaptchaKey, - formResult) - .ConfigureAwait(false); - - break; - } - - case ImplicitFlowPageType.Captcha: - - { - _logger?.LogDebug("Капча."); - - resultForm = await FilledLoginFormAsync(_authParams.Login, - _authParams.Password, - _authParams.CaptchaSid, - _authParams.CaptchaKey, - formResult) - .ConfigureAwait(false); - - break; - } - - case ImplicitFlowPageType.TwoFactor: - - { - _logger?.LogDebug("Двухфакторная авторизация."); - resultForm = await FilledTwoFactorFormAsync(_authParams.TwoFactorAuthorization, formResult).ConfigureAwait(false); - - break; - } - - case ImplicitFlowPageType.Consent: - - { - _logger?.LogDebug("Страница подтверждения доступа к скоупам."); - resultForm = await FilledConsentAsync(formResult).ConfigureAwait(false); - - break; - } - - case ImplicitFlowPageType.Result: - - { - return _vkAuthorization.GetAuthorizationResult(formResult.ResponseUrl); - } - } - - return await NextStepAsync(resultForm).ConfigureAwait(false); - } - - private AuthorizationResult NextStep(WebCallResult formResult) - { - var pageType = _vkAuthorization.GetPageType(formResult.ResponseUrl); - WebCallResult resultForm = null; - - switch (pageType) - { - case ImplicitFlowPageType.Error: - - { - _logger?.LogError("При авторизации произошла ошибка."); - - throw new VkAuthorizationException("При авторизации произошла ошибка."); - } - - case ImplicitFlowPageType.LoginPassword: - - { - LoginPasswordError++; - - if (LoginPasswordError >= MaxLoginPasswordError) - { - throw new VkAuthorizationException("Неверный логин или пароль."); - } - - _logger?.LogDebug("Ввод логина и пароля."); - - resultForm = FilledLoginForm(_authParams.Login, - _authParams.Password, - formResult); - - break; - } - - case ImplicitFlowPageType.Captcha: - - { - _logger?.LogDebug("Капча."); - - resultForm = FilledLoginForm(_authParams.Login, - _authParams.Password, - formResult); - - break; - } - - case ImplicitFlowPageType.TwoFactor: - - { - _logger?.LogDebug("Двухфакторная авторизация."); - resultForm = FilledTwoFactorForm(_authParams.TwoFactorAuthorization, formResult); - - break; - } - - case ImplicitFlowPageType.Consent: - - { - _logger?.LogDebug("Страница подтверждения доступа к скоупам."); - resultForm = FilledConsent(formResult); - - break; - } - - case ImplicitFlowPageType.Result: - - { - return _vkAuthorization.GetAuthorizationResult(formResult.ResponseUrl); - } - default: - - throw new VkApiException("Не найден ни один тип для параметра " + nameof(pageType)); - } - - return NextStep(resultForm); + return OldValidateAsync(validateUrl, phoneNumber).Result; } } } \ No newline at end of file diff --git a/VkNet/Utils/ImplicitFlow.cs b/VkNet/Utils/ImplicitFlow.cs index cc558a5d6..9f2ac6ea8 100644 --- a/VkNet/Utils/ImplicitFlow.cs +++ b/VkNet/Utils/ImplicitFlow.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; @@ -75,21 +76,40 @@ public void SetAuthorizationParams(IApiAuthParams authorizationParams) } /// + [Obsolete("Используйте перегрузку Url CreateAuthorizeUrl();\nПараметры авторизации должны быть уставленны вызовом void SetAuthorizationParams(IApiAuthParams authorizationParams);")] public Url CreateAuthorizeUrl(ulong clientId, ulong scope, Display display, string state) + { + _authorizationParameters.ApplicationId = clientId; + _authorizationParameters.Display = display; + _authorizationParameters.State = state; + + return CreateAuthorizeUrl(); + } + + /// + public Url CreateAuthorizeUrl() { _logger?.LogDebug("Построение url для авторизации."); - var builder = new StringBuilder("https://oauth.vk.com/authorize?"); - - builder.Append($"client_id={clientId}&"); - builder.Append("redirect_uri=https://oauth.vk.com/blank.html&"); - builder.Append($"display={display}&"); - builder.Append($"scope={scope}&"); - builder.Append("response_type=token&"); - builder.Append($"v={_versionManager.Version}&"); - builder.Append($"state={state}&"); - builder.Append("revoke=1"); - - return new Uri(builder.ToString()); + + const string url = "https://oauth.vk.com/authorize?"; + + var vkAuthParams = new VkParameters + { + { "client_id", _authorizationParameters.ApplicationId }, + { "redirect_uri", _authorizationParameters.RedirectUri }, + { "display", _authorizationParameters.Display }, + { "scope", _authorizationParameters.Settings }, + { "response_type", ResponseType.Token }, + { "v", _versionManager.Version }, + { "state", _authorizationParameters.State }, + { "revoke", _authorizationParameters.Revoke } + }; + + var query = vkAuthParams.Select(x => $"{x.Key}={x.Value}"); + var stringQuery = string.Join("&",query); + var result = $"{url}{stringQuery}"; + + return new Uri(result); } private async Task NextStepAsync(AuthorizationFormResult formResult) @@ -105,6 +125,7 @@ private async Task NextStepAsync(AuthorizationFormResult fo throw new VkAuthorizationException("При авторизации произошла ошибка."); } + case ImplicitFlowPageType.LoginPassword: { @@ -112,6 +133,7 @@ private async Task NextStepAsync(AuthorizationFormResult fo break; } + case ImplicitFlowPageType.Captcha: { @@ -119,6 +141,7 @@ private async Task NextStepAsync(AuthorizationFormResult fo break; } + case ImplicitFlowPageType.TwoFactor: { @@ -126,6 +149,7 @@ private async Task NextStepAsync(AuthorizationFormResult fo break; } + case ImplicitFlowPageType.Consent: { @@ -133,6 +157,7 @@ private async Task NextStepAsync(AuthorizationFormResult fo break; } + case ImplicitFlowPageType.Result: { @@ -155,24 +180,30 @@ private void ValidateAuthorizationParameters() { var errorsBuilder = new StringBuilder(); - if (_authorizationParameters.ApplicationId == 0) + if (_authorizationParameters == null) { - errorsBuilder.AppendLine("ApplicationId обязательный параметр"); - } - - if (string.IsNullOrWhiteSpace(_authorizationParameters.Login)) + errorsBuilder.AppendLine("Параметры авторизации не установленны"); + } else { - errorsBuilder.AppendLine("Login обязательный параметр"); - } + if (_authorizationParameters.ApplicationId == 0) + { + errorsBuilder.AppendLine($"{nameof(_authorizationParameters.ApplicationId)} обязательный параметр"); + } - if (string.IsNullOrWhiteSpace(_authorizationParameters.Password)) - { - errorsBuilder.AppendLine("Password обязательный параметр"); - } + if (string.IsNullOrWhiteSpace(_authorizationParameters.Login)) + { + errorsBuilder.AppendLine($"{nameof(_authorizationParameters.Login)} обязательный параметр"); + } - if (_authorizationParameters.Settings == null) - { - errorsBuilder.AppendLine("Settings обязательный параметр"); + if (string.IsNullOrWhiteSpace(_authorizationParameters.Password)) + { + errorsBuilder.AppendLine($"{nameof(_authorizationParameters.Password)} обязательный параметр"); + } + + if (_authorizationParameters.RedirectUri == null) + { + errorsBuilder.AppendLine($"{nameof(_authorizationParameters.RedirectUri)} обязательный параметр"); + } } var errors = errorsBuilder.ToString();