using Microsoft.EntityFrameworkCore; using PlaylistShared.Api.Data; using PlaylistShared.Api.Entities; using PlaylistShared.Shared.Yandex; using System.Net; using System.Text.Json; using YandexMusic.API; namespace PlaylistShared.Api.Services; public class YandexAuthService { private readonly YandexApiService _apiService; private readonly ApplicationDbContext _dbContext; public YandexApiService Service => _apiService; public YandexMusicApi Api => _apiService.Client.Api; public YandexAuthService(YandexApiService apiService, ApplicationDbContext dbContext) { _apiService = apiService; _dbContext = dbContext; } internal async Task GetQrOrGenerate(ApplicationUser user) { var existingSession = await _dbContext.YandexAuthSessions .Where(s => s.UserId == user.Id && !s.IsConfirmed && s.CreatedAt > DateTime.UtcNow.AddMinutes(-5)) .OrderByDescending(s => s.CreatedAt) .FirstOrDefaultAsync(); if (existingSession != null) { return new YandexAuthQr { QrLink = existingSession.QrCodeUrl, SessionId = existingSession.Id.ToString() }; } return await GenerateQrAsync(user); } internal async Task GenerateQrAsync(ApplicationUser user) { var qr = await Api.Passport.GetAuthQRLinkAsync(); var trackId = Service.Client.AuthStorage.AuthToken.TrackId; var csrfToken = Service.Client.AuthStorage.AuthToken.CsfrToken; var headerProcessUuid = Service.Client.AuthStorage.HeaderToken.ProcessUuid; var headerCsrfToken = Service.Client.AuthStorage.HeaderToken.CsfrToken; if (string.IsNullOrEmpty(qr)) throw new Exception("Не удалось получить QR-ссылку"); var cookiesJson = _apiService.EncryptToken(SerializeCookies(_apiService.CookieContainer)); var session = new YandexAuthSession { UserId = user.Id, QrCodeUrl = qr, SerializedCookies = cookiesJson, CreatedAt = DateTime.UtcNow, IsConfirmed = false, TrackId = trackId, CsrfToken = csrfToken, HeaderCsrfToken = headerCsrfToken, HeaderProcessId = headerProcessUuid, }; _dbContext.YandexAuthSessions.Add(session); await _dbContext.SaveChangesAsync(); return new YandexAuthQr { QrLink = qr, SessionId = session.Id.ToString() }; } internal async Task CheckQrAsync(int sessionId) { var session = await _dbContext.YandexAuthSessions.FindAsync(sessionId); if (session == null) return null; var decryptedCookies = _apiService.DecryptToken(session.SerializedCookies); if (decryptedCookies == null) return null; RestoreCookies(Service.CookieContainer, decryptedCookies); if (Service.Client.AuthStorage.AuthToken is null) { Service.Client.AuthStorage.AuthToken = new(); } Service.Client.AuthStorage.AuthToken.CsfrToken = session.CsrfToken ?? ""; Service.Client.AuthStorage.AuthToken.TrackId = session.TrackId ?? ""; Service.Client.AuthStorage.HeaderToken.CsfrToken = session.HeaderCsrfToken ?? ""; Service.Client.AuthStorage.HeaderToken.ProcessUuid = session.HeaderProcessId ?? ""; var status = await Api.Passport.CheckQRStatusAsync(); if (status?.State == "otp_auth_finished") { try { await Api.Passport.AuthorizeByQRAsync(); } catch { return new() { Status = Shared.Enums.YandexAuthQrStatus.Error }; } await _dbContext.YandexAuthSessions .Where(t => t.UserId == session.UserId) .ExecuteDeleteAsync(); await _dbContext.SaveChangesAsync(); return new() { Status = Shared.Enums.YandexAuthQrStatus.Authorized }; } return new() { Status = Shared.Enums.YandexAuthQrStatus.Pending }; } private string SerializeCookies(CookieContainer container) { var allCookies = new List(); foreach (Cookie cookie in container.GetAllCookies()) allCookies.Add(new { cookie.Name, cookie.Value, cookie.Domain, cookie.Path }); return JsonSerializer.Serialize(allCookies); } private void RestoreCookies(CookieContainer container, string serializedCookies) { var cookies = JsonSerializer.Deserialize>(serializedCookies); foreach (var c in cookies) container.Add(new Cookie(c.Name, c.Value, c.Path, c.Domain)); } private class CookieData { public string Name { get; set; } public string Value { get; set; } public string Domain { get; set; } public string Path { get; set; } } }