149 lines
5.1 KiB
C#
149 lines
5.1 KiB
C#
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<YandexAuthQr> 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<YandexAuthQr> 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<YandexAuthQrCheck?> 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 ?? "";
|
||
if (Service.Client.AuthStorage.HeaderToken is null)
|
||
Service.Client.AuthStorage.HeaderToken = new();
|
||
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<object>();
|
||
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<List<CookieData>>(serializedCookies);
|
||
if (cookies == null) return;
|
||
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; } = string.Empty;
|
||
public string Value { get; set; } = string.Empty;
|
||
public string Domain { get; set; } = string.Empty;
|
||
public string Path { get; set; } = string.Empty;
|
||
}
|
||
}
|