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 Google.Apis.YouTube.v3.Data; using Windows.UI.Xaml.Controls; using FoxTube.Utils; using YoutubeExplode; using System.Linq; using Newtonsoft.Json; using Windows.Storage; using Google.Apis.Oauth2.v2.Data; namespace FoxTube { public static class UserManagement { public const int MaxUsersCount = 2; #region Private members private static readonly ApplicationDataContainer storage = ApplicationData.Current.LocalSettings; private static readonly ExtendedYouTubeService _defaultService = new ExtendedYouTubeService(new Google.Apis.Services.BaseClientService.Initializer { ApplicationName = "FoxTube", ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", //ApiKey = "AIzaSyD7tpbuvmYDv9h4udo9L_g3r0sLPFAnN00" }); private static readonly YoutubeClient _defaultYteClient = new YoutubeClient(); private static string[] Scopes { get; } = new string[] { Oauth2Service.Scope.UserinfoProfile, Oauth2Service.Scope.UserinfoEmail, YouTubeService.Scope.YoutubeForceSsl }; private static ClientSecrets[] ClientSecrets { get; } = new ClientSecrets[MaxUsersCount] { new ClientSecrets { ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_" }, new ClientSecrets // DISABLED { ClientId = "1096685398208-u95rcpkqb4e1ijfmb8jdq3jsg37l8igv.apps.googleusercontent.com", ClientSecret = "IU5bbdjwvmx8ttJoXQ7e6JWd" } }; #endregion public static Userinfoplus[] Users { get; private set; } = new Userinfoplus[MaxUsersCount]; public static User CurrentUser { get; set; } public static bool Authorized => CurrentUser != null; public static ExtendedYouTubeService Service => CurrentUser?.Service ?? _defaultService; public static YoutubeClient YoutubeClient => CurrentUser?.Client ?? _defaultYteClient; public static event EventHandler UserStateUpdated; public static event EventHandler SubscriptionsChanged; public static async Task 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(storage.Values["UserManagement.Users"] as string); int? lastUserIndex = storage.Values["UserManagement.LastUser"] as int?; if (lastUserIndex.HasValue && Users[lastUserIndex.Value] != null || (lastUserIndex = Users.ToList().FindIndex(i => i != null)) > -1) await SwitchUser(lastUserIndex.Value); } public static async Task Logout() { if (CurrentUser?.UserInfo?.Id == null) return false; try { string userId = CurrentUser.UserInfo.Id; PasswordVault passwordVault = new PasswordVault(); PasswordCredential credential = passwordVault.Retrieve("foxtube", userId); passwordVault.Remove(credential); await CurrentUser.Credential.RevokeTokenAsync(CancellationToken.None); storage.Values.Remove($"Subscriptions.{CurrentUser.UserInfo.Id}"); CurrentUser = null; Users[Users.ToList().FindIndex(i => i.Id == userId)] = null; storage.Values["UserManagement.Users"] = JsonConvert.SerializeObject(Users); storage.Values["UserManagement.LastUser"] = 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 SwitchUser(int userIndex) { Userinfoplus userInfo = Users[userIndex]; PasswordVault valut = new PasswordVault(); PasswordCredential vaultCredential = valut.Retrieve("foxtube", userInfo.Id); if (vaultCredential == null) throw new NullReferenceException("No user found to switch on"); vaultCredential.RetrievePassword(); string token = vaultCredential.Password; YouTube.Authorization.UserCredential credential = await AuthorizationHelpers.RestoreUser(ClientSecrets[userIndex], token); await LoadUser(credential, userIndex); } private static async Task LoadUser(YouTube.Authorization.UserCredential credential, int userIndex) { CurrentUser = await User.GetUser(credential); Users[userIndex] = CurrentUser.UserInfo; storage.Values["UserManagement.Users"] = JsonConvert.SerializeObject(Users); storage.Values["UserManagement.LastUser"] = 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(); PasswordCredential vaultCredential = passwordVault.Retrieve("foxtube", id); if (vaultCredential == null) { vaultCredential = new PasswordCredential("foxtube", CurrentUser.UserInfo.Id, refreshToken); passwordVault.Add(vaultCredential); } else vaultCredential.Password = refreshToken; } internal static void SubscriptionsChangedInvoker(User sender, Subscription subscription) => SubscriptionsChanged?.Invoke(sender, subscription); } }