using System.Security.Authentication; using System.Text.RegularExpressions; using YandexMusic.API.Common; using YandexMusic.API.Models.Account; using YandexMusic.API.Models.Common; using YandexMusic.API.Requests.Account; namespace YandexMusic.API; /// /// API для пользователя /// public partial class YUserAPI : YCommonAPI { #region Вспомогательные функции private async Task GetCsrfTokenAsync(AuthStorage storage) { using HttpResponseMessage authMethodsResponse = await new YGetAuthMethodsBuilder(api, storage) .Build(null) .GetResponseAsync(); if (!authMethodsResponse.IsSuccessStatusCode) throw new HttpRequestException("Невозможно получить CFRF-токен."); string responseString = await authMethodsResponse.Content .ReadAsStringAsync(); Match match = Regex.Match(responseString, "\"csrf_token\" value=\"([^\"]+)\""); if (!match.Success || match.Groups.Count < 2) return false; storage.AuthToken = new YAuthToken { CsfrToken = match.Groups[1].Value }; return true; } private async Task LoginByCookiesAsync(AuthStorage storage) { if (storage.AuthToken == null) throw new AuthenticationException("Невозможно инициализировать сессию входа."); YAccessToken accessToken = await new YGetAuthCookiesBuilder(api, storage) .Build(null) .GetResponseAsync(); storage.IsAuthorized = !string.IsNullOrEmpty(accessToken.AccessToken); storage.AccessToken = accessToken; storage.Token = accessToken.AccessToken; YShortAccountInfo validateTokenResponse = await new YGetShortAccountInifoBuilder(api, storage) .Build(null) .GetResponseAsync(); if (validateTokenResponse.Status != YAuthStatus.Ok) throw new Exception("Вход в аккаунт не выполнен."); storage.IsAuthorized = !string.IsNullOrWhiteSpace(validateTokenResponse.Uid); return storage.IsAuthorized; } #endregion Вспомогательные функции public YUserAPI(YandexMusicApi yandex) : base(yandex) { } /// /// Авторизация /// /// Хранилище /// Токен авторизации /// public async Task AuthorizeAsync(AuthStorage storage, string token) { if (string.IsNullOrEmpty(token)) throw new Exception("Задан пустой токен авторизации."); storage.Token = token; // Пытаемся получить информацию о пользователе YResponse authInfo = await GetUserAuthAsync(storage); // Если не авторизован, то авторизуем if (string.IsNullOrEmpty(authInfo.Result.Account.Uid)) throw new Exception("Пользователь незалогинен."); // Флаг авторизации storage.IsAuthorized = true; storage.User = authInfo.Result.Account; } /// /// Получение информации об авторизации /// /// Хранилище /// public Task> GetUserAuthAsync(AuthStorage storage) { return new YGetAuthInfoBuilder(api, storage) .Build(null) .GetResponseAsync(); } /// /// Создание сеанса и получение доступных методов авторизации /// /// Хранилище /// Имя пользователя /// public async Task CreateAuthSessionAsync(AuthStorage storage, string userName) { if (!await GetCsrfTokenAsync(storage)) throw new Exception("Невозможно инициализировать сессию входа."); YAuthTypes types = await new YGetAuthLoginUserBuilder(api, storage) .Build((storage.AuthToken.CsfrToken, userName)) .GetResponseAsync(); storage.AuthToken.TrackId = types.TrackId; return types; } /// /// Получение ссылки на QR-код /// /// Хранилище /// public async Task GetAuthQRLinkAsync(AuthStorage storage) { if (!await GetCsrfTokenAsync(storage)) throw new Exception("Невозможно инициализировать сессию входа."); YAuthQR result = await new YGetAuthQRBuilder(api, storage) .Build(null) .GetResponseAsync(); if (result.Status != YAuthStatus.Ok) return string.Empty; storage.AuthToken = new YAuthToken { TrackId = result.TrackId, CsfrToken = result.CsrfToken }; return $"https://passport.yandex.ru/auth/magic/code/?track_id={result.TrackId}"; } /// /// Авторизация по QR-коду /// /// Хранилище /// public async Task AuthorizeByQRAsync(AuthStorage storage) { if (storage.AuthToken == null) throw new Exception("Не выполнен запрос на авторизацию по QR."); try { YAuthQRStatus qrStatus = await new YGetAuthLoginQRBuilder(api, storage) .Build(null) .GetResponseAsync(); if (qrStatus.Status != YAuthStatus.Ok) return qrStatus; bool ok = await LoginByCookiesAsync(storage); if (!ok) throw new AuthenticationException("Ошибка авторизации по QR."); return qrStatus; } catch (Exception ex) { throw new AuthenticationException("Ошибка авторизации по QR.", ex); } } /// /// Получение /// /// Хранилище /// public Task GetCaptchaAsync(AuthStorage storage) { if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken)) throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием."); return new YGetAuthCaptchaBuilder(api, storage) .Build(null) .GetResponseAsync(); } /// /// Авторизация по captcha /// /// Хранилище /// Значение captcha /// public Task AuthorizeByCaptchaAsync(AuthStorage storage, string captchaValue) { if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken)) throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием."); return new YGetAuthLoginCaptchaBuilder(api, storage) .Build(captchaValue) .GetResponseAsync(); } /// /// Получение письма авторизации на почту пользователя /// /// Хранилище /// public Task GetAuthLetterAsync(AuthStorage storage) { return new YGetAuthLetterBuilder(api, storage) .Build(null) .GetResponseAsync(); } /// /// Авторизация после подтверждения входа через письмо /// /// Хранилище /// public async Task AuthorizeByLetterAsync(AuthStorage storage) { YAuthLetterStatus status = await new YGetAuthLoginLetterBuilder(api, storage) .Build(null) .GetResponseAsync(); if (status.Status == YAuthStatus.Ok && !status.MagicLinkConfirmed) throw new Exception("Не подтвержден вход посредством e-mail."); return await LoginByCookiesAsync(storage); } /// /// Авторизация с помощью пароля из приложения Яндекс /// /// Хранилище /// Пароль /// public async Task AuthorizeByAppPasswordAsync(AuthStorage storage, string password) { if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken)) throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием."); YAuthBase response = await new YGetAuthAppPasswordBuilder(api, storage) .Build(password) .GetResponseAsync(); if (response.Status == YAuthStatus.Ok) { bool ok = await LoginByCookiesAsync(storage); if (!ok) throw new AuthenticationException("Ошибка авторизации."); } return response; } /// /// Получение после авторизации с помощью QR, e-mail, пароля из приложения /// public async Task GetAccessTokenAsync(AuthStorage storage) { if (storage.AuthToken == null) throw new Exception("Не найдена сессия входа."); YAccessToken accessToken = await new YGetMusicTokenBuilder(api, storage) .Build(null) .GetResponseAsync(); storage.Token = accessToken.AccessToken; return accessToken; } /// /// Получение информации о пользователе через логин Яндекса /// public Task GetLoginInfoAsync(AuthStorage storage) { return new YGetLoginInfoBuilder(api, storage) .Build(null) .GetResponseAsync(); } }