304 lines
12 KiB
C#
304 lines
12 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// API для пользователя
|
||
/// </summary>
|
||
public partial class YUserAPI : YCommonAPI
|
||
{
|
||
#region Вспомогательные функции
|
||
|
||
private async Task<bool> 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<bool> 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)
|
||
{
|
||
}
|
||
|
||
/// <summary>
|
||
/// Авторизация
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <param name="token">Токен авторизации</param>
|
||
/// <returns></returns>
|
||
public async Task AuthorizeAsync(AuthStorage storage, string token)
|
||
{
|
||
if (string.IsNullOrEmpty(token))
|
||
throw new Exception("Задан пустой токен авторизации.");
|
||
|
||
storage.Token = token;
|
||
|
||
// Пытаемся получить информацию о пользователе
|
||
YResponse<YAccountResult> authInfo = await GetUserAuthAsync(storage);
|
||
|
||
// Если не авторизован, то авторизуем
|
||
if (string.IsNullOrEmpty(authInfo.Result.Account.Uid))
|
||
throw new Exception("Пользователь незалогинен.");
|
||
|
||
// Флаг авторизации
|
||
storage.IsAuthorized = true;
|
||
storage.User = authInfo.Result.Account;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение информации об авторизации
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <returns></returns>
|
||
public Task<YResponse<YAccountResult>> GetUserAuthAsync(AuthStorage storage)
|
||
{
|
||
return new YGetAuthInfoBuilder(api, storage)
|
||
.Build(null)
|
||
.GetResponseAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Создание сеанса и получение доступных методов авторизации
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <param name="userName">Имя пользователя</param>
|
||
/// <returns></returns>
|
||
public async Task<YAuthTypes> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение ссылки на QR-код
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <returns></returns>
|
||
public async Task<string> 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}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Авторизация по QR-коду
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <returns></returns>
|
||
public async Task<YAuthQRStatus> 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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение <see cref="YAuthCaptcha"/>
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <returns></returns>
|
||
public Task<YAuthCaptcha> 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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Авторизация по captcha
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <param name="captchaValue">Значение captcha</param>
|
||
/// <returns></returns>
|
||
public Task<YAuthBase> 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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение письма авторизации на почту пользователя
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <returns></returns>
|
||
public Task<YAuthLetter> GetAuthLetterAsync(AuthStorage storage)
|
||
{
|
||
return new YGetAuthLetterBuilder(api, storage)
|
||
.Build(null)
|
||
.GetResponseAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Авторизация после подтверждения входа через письмо
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <returns></returns>
|
||
public async Task<bool> 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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Авторизация с помощью пароля из приложения Яндекс
|
||
/// </summary>
|
||
/// <param name="storage">Хранилище</param>
|
||
/// <param name="password">Пароль</param>
|
||
/// <returns></returns>
|
||
public async Task<YAuthBase> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение <see cref="YAccessToken"/> после авторизации с помощью QR, e-mail, пароля из приложения
|
||
/// </summary>
|
||
public async Task<YAccessToken> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение информации о пользователе через логин Яндекса
|
||
/// </summary>
|
||
public Task<YLoginInfo> GetLoginInfoAsync(AuthStorage storage)
|
||
{
|
||
return new YGetLoginInfoBuilder(api, storage)
|
||
.Build(null)
|
||
.GetResponseAsync();
|
||
}
|
||
|
||
|
||
}
|
||
}
|