Archived
1
0
This repository has been archived on 2026-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FoxTube/FoxTube.Core/Services/UserService.cs
T
Michael Gordeev 787a6e9f48 Refactored core
UI navigation framework

Related Work Items: #408, #414, #416
2020-06-15 15:46:38 +03:00

225 lines
6.6 KiB
C#

using Google.Apis.Auth.OAuth2;
using Google.Apis.Oauth2.v2;
using System;
using System.Threading.Tasks;
using Windows.Security.Authentication.Web;
using YouTube.Authorization;
using Windows.Security.Credentials;
using FoxTube.Models;
using YouTube;
using System.Threading;
using Google.Apis.YouTube.v3;
using Windows.UI.Xaml.Controls;
using FoxTube.Utils;
using YoutubeExplode;
using System.Linq;
using Newtonsoft.Json;
using Google.Apis.Oauth2.v2.Data;
namespace FoxTube.Services
{
public static class UserService
{
public const int MaxUsersCount = 1;
#region Private members
private static readonly ExtendedYouTubeService _defaultService = new ExtendedYouTubeService(new Google.Apis.Services.BaseClientService.Initializer
{
ApplicationName = "FoxTube",
ApiKey = SecretConstants.YoutubeApiKey, // TODO: Replace with an actual API key
});
private static readonly YoutubeClient _defaultYtClient = new YoutubeClient();
private static string[] Scopes { get; } = new string[]
{
Oauth2Service.Scope.UserinfoProfile,
Oauth2Service.Scope.UserinfoEmail,
YouTubeService.Scope.YoutubeForceSsl
};
private static ClientSecrets[] ClientSecrets { get; } = SecretConstants.ClientSecrets;
#endregion
public static Userinfoplus[] Users { get; private set; } = new Userinfoplus[MaxUsersCount];
public static bool IncognitoMode { get; set; } = false;
public static bool CanAddAccounts => Users.Any(i => i == null);
public static User CurrentUser { get; set; }
public static bool Authorized => CurrentUser != null;
public static ExtendedYouTubeService Service
{
get
{
if (IncognitoMode || CurrentUser == null)
return _defaultService;
else
return CurrentUser.Service;
}
}
public static YoutubeClient YouTubeClient
{
get
{
if (IncognitoMode || CurrentUser == null)
return _defaultYtClient;
else
return CurrentUser.Client;
}
}
public static event EventHandler<bool> UserStateUpdated;
public static event EventHandler<Subscription> SubscriptionsChanged;
public static async Task<bool> AddUser()
{
int queueIndex = Users.ToList().FindIndex(i => i == null);
if (queueIndex < 0)
throw new StackOverflowException("The maximum accounts limit is reached");
ClientSecrets secrets = ClientSecrets[queueIndex];
Uri requestString = AuthorizationHelpers.FormQueryString(secrets, Scopes);
WebAuthenticationResult result = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, requestString, AuthorizationHelpers.Endpoint);
switch (result.ResponseStatus)
{
case WebAuthenticationStatus.Success:
string successCode = AuthorizationHelpers.ParseSuccessCode(result.ResponseData);
YouTube.Authorization.UserCredential credential = await AuthorizationHelpers.ExchangeToken(secrets, successCode);
await LoadUser(credential, queueIndex);
return true;
case WebAuthenticationStatus.UserCancel:
break;
case WebAuthenticationStatus.ErrorHttp:
await new ContentDialog
{
Title = "Something went wrong...",
Content = "It may be a bug or temporary server issues. Please, try again later"
}.ShowAsync();
Metrics.SendReport(new Exception("Authorization failed (HTTP Error)"), null,
("Response status", result.ResponseStatus.ToString()),
("Response data", result.ResponseData),
("Error details", result.ResponseErrorDetail.ToString()));
break;
}
return false;
}
public static async Task Initialize()
{
Users = JsonConvert.DeserializeObject<Userinfoplus[]>(StorageService.UserInfos ?? "") ?? new Userinfoplus[MaxUsersCount];
int? lastUserIndex = StorageService.LastUserIndex;
if (lastUserIndex.HasValue && Users[lastUserIndex.Value] != null ||
(lastUserIndex = Users.ToList().FindIndex(i => i != null)) > -1)
await SwitchUser(lastUserIndex.Value);
}
public static async Task<bool> Logout()
{
if (CurrentUser?.UserInfo?.Id == null)
return false;
try
{
string userId = CurrentUser.UserInfo.Id;
PasswordVault passwordVault = new PasswordVault();
try
{
PasswordCredential credential = passwordVault.Retrieve("foxtube", userId);
passwordVault.Remove(credential);
}
catch { }
await CurrentUser.Credential.RevokeTokenAsync(CancellationToken.None);
CurrentUser = null;
Users[Users.ToList().FindIndex(i => i.Id == userId)] = null;
StorageService.UserInfos = JsonConvert.SerializeObject(Users);
StorageService.LastUserIndex = null;
if (Users.Any(i => i != null))
await SwitchUser(Users.ToList().FindIndex(i => i != null));
else
UserStateUpdated?.Invoke(Users, false);
return true;
}
catch (Exception e)
{
Metrics.SendReport(new Exception("Failed to logout", e));
await new ContentDialog
{
Title = "Something went wrong...",
Content = "It may be a bug or temporary server issues. Please, try again later"
}.ShowAsync();
return false;
}
}
public static async Task<bool> SwitchUser(int userIndex)
{
Userinfoplus userInfo = Users[userIndex];
PasswordVault valut = new PasswordVault();
try
{
PasswordCredential vaultCredential = valut.Retrieve("foxtube", userInfo.Id);
vaultCredential.RetrievePassword();
string token = vaultCredential.Password;
YouTube.Authorization.UserCredential credential = await AuthorizationHelpers.RestoreUser(ClientSecrets[userIndex], token);
await LoadUser(credential, userIndex);
return true;
}
catch
{
return false;
}
}
private static async Task LoadUser(YouTube.Authorization.UserCredential credential, int userIndex)
{
CurrentUser = await User.GetUser(credential);
Users[userIndex] = CurrentUser.UserInfo;
StorageService.UserInfos = JsonConvert.SerializeObject(Users);
StorageService.LastUserIndex = userIndex;
credential.RefreshTokenUpdated += (s, e) => UpdateToken(CurrentUser.UserInfo.Id, credential.Token.RefreshToken);
UpdateToken(CurrentUser.UserInfo.Id, credential.Token.RefreshToken);
UserStateUpdated?.Invoke(Users, true);
}
private static void UpdateToken(string id, string refreshToken)
{
PasswordVault passwordVault = new PasswordVault();
try
{
PasswordCredential vaultCredential = passwordVault.Retrieve("foxtube", id);
vaultCredential.Password = refreshToken;
}
catch
{
PasswordCredential vaultCredential = new PasswordCredential("foxtube", CurrentUser.UserInfo.Id, refreshToken);
passwordVault.Add(vaultCredential);
}
}
internal static void SubscriptionsChangedInvoker(User sender, Subscription subscription) =>
SubscriptionsChanged?.Invoke(sender, subscription);
}
}