using System.Application.Models; using System.Application.Mvvm; using System.Application.Services; using System.Application.Services.CloudService; using System.Application.UI.Resx; using System.Net; using System.Net.WebSocket; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using static System.Application.Services.CloudService.Constants; using static System.Application.UI.ViewModels.FastLRBHelper; #if __MOBILE__ using LRViewModel = System.Application.UI.ViewModels.LoginOrRegisterPageViewModel; using Microsoft.Identity.Client; #else using LRViewModel = System.Application.UI.ViewModels.LoginOrRegisterWindowViewModel; #endif namespace System.Application.UI.ViewModels { public static class FastLRBHelper { public interface IViewModel : IDisposableHolder { /// /// 当前 WebSocket 服务 /// ServerWebSocket? ServerWebSocket { get; set; } /// /// 当前 WebSocket 服务监听端口号 /// int ServerWebSocketListenerPort { get; set; } /// /// 临时 Aes 对象 /// Aes? TempAes { get; set; } /// /// 关闭当前窗口 /// Action? Close { get; } /// /// 是否为绑定账号 /// bool IsBind { get; } } /// /// 当接收到 WebSocket Client 发送的消息时 /// /// /// /// static async Task OnClientReceived(this IViewModel vm, ReceivedEventArgs e) { if (vm.TempAes == null) return; var byteArray = e.Message.Base64UrlDecodeToByteArray(); byteArray = vm.TempAes.Decrypt(byteArray); var response = ApiResponse.Deserialize(byteArray); var webResponse = new FastLRBWebResponse(); if (response.IsSuccess && response.Content == null) { webResponse.Msg = ApiResponse.GetMessage(ApiResponseCode.NoResponseContent); await e.Client.WriteAsync(webResponse); return; } var conn_helper = DI.Get(); webResponse.State = response.IsSuccess; await e.Client.WriteAsync(webResponse); // 仅可在 close 之前传递消息 if (response.IsSuccess) { if (vm.IsBind) { var channel = response.Content?.FastLRBChannel; if (!channel.HasValue) return; await MainThread2.InvokeOnMainThreadAsync(async () => { string msg; if (channel.HasValue) { msg = AppResources.Success_.Format(AppResources.User_AccountBind); await UserService.Current.BindAccountAfterUpdateAsync(channel.Value, response.Content!); } else { msg = "Account bind fail, unknown channel."; } vm?.Close?.Invoke(); Toast.Show(msg); }); } else { await conn_helper.OnLoginedAsync(response.Content!, response.Content!); await MainThread2.InvokeOnMainThreadAsync(async () => { await LRViewModel.SuccessAsync(response.Content!); vm.Close?.Invoke(); }); } } else { await MainThread2.InvokeOnMainThreadAsync(() => { vm?.Close?.Invoke(); conn_helper.ShowResponseErrorMessage(response); }); } } #if __MOBILE__ /// /// 通过 Microsoft Authentication Library (MSAL) 实现 的版本 /// static async void MSAL() { var publicClientApp = DI.Get_Nullable(); if (publicClientApp == null) return; var uiHost = IMobilePlatformService.Instance.CurrentPlatformUIHost; // 页面后退不会结束等待,此处使用 ReactiveCommand.CreateFromTask,所以需要 async void var authResult = await publicClientApp .AcquireTokenInteractive(new string[] { "user.read" }) .WithParentActivityOrWindow(uiHost) .ExecuteAsync(); var dstr = Serializable.SJSON(authResult); Toast.Show(dstr); } /// /// 适用于移动端平台实现 的版本 /// /// static void MobilePlatform(Action action) { if (IMobilePlatformService.Instance.CurrentPlatformUIHost is IPlatformUIHost uiHost) { action(uiHost); } } /// /// 移动端平台实现定义接口 /// public interface IPlatformUIHost { void QQLogin(); } #endif /// /// 开始第三方快速登录、注册、绑定 /// /// /// /// public static async Task StartLRBAsync(this IViewModel vm, FastLoginChannel channel) { #if __MOBILE__ switch (channel) { case FastLoginChannel.Microsoft: MSAL(); return; case FastLoginChannel.QQ: MobilePlatform(uiHost => uiHost.QQLogin()); return; } #endif if (vm.ServerWebSocket == null) { vm.ServerWebSocket = new(IPAddress.Loopback, out var port); vm.ServerWebSocket.AddTo(vm); vm.ServerWebSocketListenerPort = port; vm.ServerWebSocket.OnClientReceived += e => vm.OnClientReceived(e); } await vm.ServerWebSocket.StartAsync(); var conn_helper = DI.Get(); var apiBaseUrl = "https://127.0.0.1:28110"; //ICloudServiceClient.Instance.ApiBaseUrl; vm.TempAes.RemoveTo(vm); vm.TempAes = AESUtils.Create(); vm.TempAes.AddTo(vm); var skey_bytes = vm.TempAes.ToParamsByteArray(); var skey_str = conn_helper.RSA.EncryptToString(skey_bytes); var csc = DI.Get(); var padding = RSAUtils.DefaultPadding; var url = apiBaseUrl + $"/ExternalLoginDetection/{(int)channel}" + $"?websocket=true&" + $"port={vm.ServerWebSocketListenerPort}&" + $"sKey={skey_str}&" + $"sKeyPadding={padding.OaepHashAlgorithm}&" + $"version={csc.Settings.AppVersionStr}" + $"isBind={vm.IsBind}"; BrowserOpen(url); } } partial class #if __MOBILE__ LoginOrRegisterPageViewModel #else LoginOrRegisterWindowViewModel #endif : IViewModel { ServerWebSocket? IViewModel.ServerWebSocket { get; set; } int IViewModel.ServerWebSocketListenerPort { get; set; } Aes? IViewModel.TempAes { get; set; } bool IViewModel.IsBind => false; } partial class #if __MOBILE__ UserProfilePageViewModel #else UserProfileWindowViewModel #endif : IViewModel { ServerWebSocket? IViewModel.ServerWebSocket { get; set; } int IViewModel.ServerWebSocketListenerPort { get; set; } Aes? IViewModel.TempAes { get; set; } bool IViewModel.IsBind => true; } } //async Task HandleOnClientReceived(ReceivedEventArgs msg) //{ // var response = ApiResponse.Deserialize(aes.Decrypt(msg.Message.Base64UrlDecodeToByteArray())); // var retResponse = new FastLRBWebResponse(); // if (response.IsSuccess && response.Content == null) // { // //retResponse.Msg = AppResources.UserChange_NoUserTip; // await msg.Client.WriteAsync(retResponse); // return; // } // var conn_helper = DI.Get(); // if (response.IsSuccess) // { // retResponse.State = true; // if (IsBind) // { // await MainThread2.InvokeOnMainThreadAsync(async () => // { // await UserService.Current.BindAccountAfterUpdateAsync(Channel, response.Content!); // var msg = AppResources.Success_.Format(AppResources.User_AccountBind); // Toast.Show(msg); // Close?.Invoke(); // }); // } // else // { // await conn_helper.OnLoginedAsync(response.Content!, response.Content!); // await MainThread2.InvokeOnMainThreadAsync(async () => // { // await SuccessAsync(response.Content!); // Close?.Invoke(); // }); // } // await msg.Client.WriteAsync(retResponse); // //登录完成释放 // server!.Dispose(); // return; // } // else // { // MainThread2.BeginInvokeOnMainThread(() => // { // conn_helper.ShowResponseErrorMessage(response); // }); // } // await msg.Client.WriteAsync(retResponse); //} //internal static Task FastLoginOrRegisterAsync(Action? close = null, FastLoginChannel channel = FastLoginChannel.Steam, bool isBind = false) //{ // var conn_helper = DI.Get(); // var apiBaseUrl = "https://127.0.0.1:28110"; //ICloudServiceClient.Instance.ApiBaseUrl; // if (aes == null) // aes = AESUtils.Create(); // var skey_bytes = aes.ToParamsByteArray(); // var skey_str = conn_helper.RSA.EncryptToString(skey_bytes); // var sc = DI.Get(); // var url = apiBaseUrl + // (channel == FastLoginChannel.Steam ? // "/ExternalLoginDetection" : $"/ExternalLoginDetection/{(int)channel}") + $"?websocket=true&port={port}&sKey={skey_str}&Version={sc.Settings.AppVersionStr}" + // (isBind ? "&isBind=true" : string.Empty); // IsBind = isBind; // Channel = channel; // Services.CloudService.Constants.BrowserOpen(url); // //if (!AppHelper.IsSystemWebViewAvailable) return; // ////var apiBaseUrl = ICloudServiceClient.Instance.ApiBaseUrl; // //var urlExternalLoginCallback = apiBaseUrl + "/ExternalLoginCallback"; // //WebView3WindowViewModel? vm = null; // //vm = new WebView3WindowViewModel // //{ // // Url = apiBaseUrl + // // (channel == FastLoginChannel.Steam ? // // "/ExternalLogin" : // // $"/ExternalLogin/{(int)channel}") + // // (isBind ? // // "?isBind=true" : // // string.Empty), // // StreamResponseFilterUrls = new[] // // { // // urlExternalLoginCallback, // // }, // // OnStreamResponseFilterResourceLoadComplete = _OnStreamResponseFilterResourceLoadComplete, // // FixedSinglePage = true, // // Title = AppResources.User_FastLogin_.Format(channel), // // TimeoutErrorMessage = channel == FastLoginChannel.Steam ? AppResources.User_SteamFastLoginTimeoutErrorMessage : null, // // IsSecurity = true, // // UseLoginUsingSteamClient = channel == FastLoginChannel.Steam, // // Close = close, // //}; // //async void _OnStreamResponseFilterResourceLoadComplete(string url, Stream data) // //{ // // if (url.StartsWith(urlExternalLoginCallback, StringComparison.OrdinalIgnoreCase)) // // { // // var response = await ApiResponse.DeserializeAsync(data, default); // // if (response.IsSuccess && response.Content == null) // // { // // response.Code = ApiResponseCode.NoResponseContent; // // } // // var conn_helper = DI.Get(); // // if (response.IsSuccess) // // { // // if (isBind) // // { // // await MainThread2.InvokeOnMainThreadAsync(async () => // // { // // await UserService.Current.BindAccountAfterUpdateAsync(channel, response.Content!); // // vm?.Close?.Invoke(); // // var msg = AppResources.Success_.Format(AppResources.User_AccountBind); // // Toast.Show(msg); // // }); // // } // // else // // { // // await conn_helper.OnLoginedAsync(response.Content!, response.Content!); // // await MainThread2.InvokeOnMainThreadAsync(async () => // // { // // await SuccessAsync(response.Content!, vm?.Close); // // }); // // } // // } // // else // // { // // MainThread2.BeginInvokeOnMainThread(() => // // { // // vm?.Close?.Invoke(); // // conn_helper.ShowResponseErrorMessage(response); // // }); // // } // // } // //} // //await IShowWindowService.Instance.Show(CustomWindow.WebView3, vm, // // resizeMode: ResizeModeCompat.CanResize, // // isDialog: true // 锁定父窗口,防止打开多个 WebViewWindow // // ); //}