diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs
index 5bfd0acac0..28b302e0fe 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs
@@ -96,7 +96,7 @@ private async Task SocialSignIn(string provider)
{
try
{
- var port = localHttpServer.Start(CurrentCancellationToken);
+ var port = localHttpServer.UseLocalHttpServerForSocialSignIn() ? localHttpServer.Start(CurrentCancellationToken) : -1;
var redirectUrl = await identityController.GetSocialSignInUri(provider, ReturnUrlQueryString, port is -1 ? null : port, CurrentCancellationToken);
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs
index 436fa869d1..8d12a6ee53 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs
@@ -75,7 +75,7 @@ private async Task SocialSignUp(string provider)
{
try
{
- var port = localHttpServer.Start(CurrentCancellationToken);
+ var port = localHttpServer.UseLocalHttpServerForSocialSignIn() ? localHttpServer.Start(CurrentCancellationToken) : -1;
var redirectUrl = await identityController.GetSocialSignInUri(provider, localHttpPort: port is -1 ? null : port, cancellationToken: CurrentCancellationToken);
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ILocalHttpServer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ILocalHttpServer.cs
index ef429f3504..02622c2d9e 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ILocalHttpServer.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ILocalHttpServer.cs
@@ -1,10 +1,18 @@
namespace Boilerplate.Client.Core.Services.Contracts;
-///
-/// Social sign-in functions seamlessly on web browsers and on Android and iOS via universal app links.
-/// However, for blazor hybrid, a local HTTP server is needed to ensure a smooth social sign-in experience.
-///
public interface ILocalHttpServer
{
int Start(CancellationToken cancellationToken);
+
+ ///
+ /// Social sign-in on the web version of the app uses simple redirects. However, for Android, iOS, Windows, and macOS, social sign-in requires an in-app or external browser.
+ ///
+ /// # Navigating Back to the App After Social Sign-In
+ /// 1. **Universal Deep Links**: Allow the app to directly handle specific web links (for iOS and Android apps).
+ /// 2. **Local HTTP Server**: Works similarly to how `git.exe` manages sign-ins with services like GitHub (supported on iOS, Android, Windows, and macOS).
+ ///
+ /// - **iOS, Windows, and macOS**: Use local HTTP server implementations in MAUI and Windows projects.
+ /// - **Android**: Use universal links.
+ ///
+ bool UseLocalHttpServerForSocialSignIn();
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/NoopLocalHttpServer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/NoopLocalHttpServer.cs
index ed0060400a..6453be400b 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/NoopLocalHttpServer.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/NoopLocalHttpServer.cs
@@ -1,10 +1,11 @@
namespace Boilerplate.Client.Core.Services;
-///
-///
-/// The is specifically registered for Android, iOS, and Web, where a local HTTP server is unnecessary.
-///
public partial class NoopLocalHttpServer : ILocalHttpServer
{
- public int Start(CancellationToken cancellationToken) => -1;
+ public int Start(CancellationToken cancellationToken) => throw new NotImplementedException();
+
+ ///
+ ///
+ ///
+ public bool UseLocalHttpServerForSocialSignIn() => false;
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs
index b2ca0d9828..132d2742fd 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs
@@ -2,6 +2,7 @@
//#if (framework == 'net9.0')
using Maui.AppStores;
using Maui.InAppReviews;
+using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;
//#endif
@@ -15,6 +16,7 @@ public partial class App
//#if (framework == 'net9.0')
private readonly IStorageService storageService;
//#endif
+ private readonly ILogger logger;
private readonly IExceptionHandler exceptionHandler;
private readonly IBitDeviceCoordinator deviceCoordinator;
private readonly IStringLocalizer localizer;
@@ -24,10 +26,12 @@ public App(MainPage mainPage,
PubSubService pubSubService,
IStorageService storageService,
//#endif
+ ILogger logger,
IExceptionHandler exceptionHandler,
IBitDeviceCoordinator deviceCoordinator,
IStringLocalizer localizer)
{
+ this.logger = logger;
this.localizer = localizer;
//#if (framework == 'net9.0')
this.storageService = storageService;
@@ -72,11 +76,17 @@ protected override async void OnStart()
//+:cnd:noEmit
//#if (framework == 'net9.0')
const int minimumSupportedWebViewVersion = 94;
+ // Download link for Android emulator (x86 or x86_64)
+ // https://www.apkmirror.com/apk/google-inc/chrome/chrome-94-0-4606-50-release/
+ // https://www.apkmirror.com/apk/google-inc/android-system-webview/android-system-webview-94-0-4606-85-release/
//#elif (framework == 'net8.0')
//#if (IsInsideProjectTemplate)
/*
//#endif
const int minimumSupportedWebViewVersion = 84;
+ // Download link for Android emulator (x86 or x86_64)
+ // https://www.apkmirror.com/apk/google-inc/chrome/chrome-84-0-4147-89-release/
+ // https://www.apkmirror.com/apk/google-inc/android-system-webview/android-system-webview-84-0-4147-111-release/
//#if (IsInsideProjectTemplate)
*/
//#endif
@@ -85,6 +95,7 @@ protected override async void OnStart()
if (Version.TryParse(Android.Webkit.WebView.CurrentWebViewPackage?.VersionName, out var webViewVersion) &&
webViewVersion.Major < minimumSupportedWebViewVersion)
{
+ logger.LogWarning("Web view version {version} is not supported", webViewVersion);
await App.Current!.Windows[0].Page!.DisplayAlert("Boilerplate", localizer[nameof(AppStrings.UpdateWebViewThroughGooglePlay)], localizer[nameof(AppStrings.Ok)]);
await Launcher.OpenAsync($"https://play.google.com/store/apps/details?id={Android.Webkit.WebView.CurrentWebViewPackage.PackageName}");
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs
index c169776987..78ba7c58a2 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs
@@ -39,12 +39,7 @@ public static void ConfigureServices(this MauiAppBuilder builder)
return settings;
});
services.AddSingleton(ITelemetryContext.Current!);
- if (AppPlatform.IsAndroid is false)
- {
- // Handle social sign-in callback on local HTTP server.
- // But in Android, leverage Universal Links for smoother sign-in flows.
- services.AddSingleton();
- }
+ services.AddSingleton();
services.AddMauiBlazorWebView();
services.AddBlazorWebViewDeveloperTools();
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs
index d4c134d38b..eb413360eb 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs
@@ -1,16 +1,15 @@
using EmbedIO;
using System.Net;
-using System.Net.Sockets;
using EmbedIO.Actions;
+using System.Net.Sockets;
+using Microsoft.Extensions.Logging;
using Boilerplate.Client.Core.Components;
namespace Boilerplate.Client.Maui.Services;
-///
-///
-///
public partial class MauiLocalHttpServer : ILocalHttpServer
{
+ [AutoInject] private ILogger logger;
[AutoInject] private IExceptionHandler exceptionHandler;
[AutoInject] private AbsoluteServerAddressProvider absoluteServerAddress;
@@ -89,4 +88,12 @@ private int GetAvailableTcpPort()
l.Stop();
return port;
}
+
+ ///
+ ///
+ ///
+ public bool UseLocalHttpServerForSocialSignIn()
+ {
+ return AppPlatform.IsAndroid is false;
+ }
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/assetlinks.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/assetlinks.json
index 3c198471ba..9776fc1f50 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/assetlinks.json
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/assetlinks.json
@@ -1,4 +1,11 @@
[
+ {
+ "relation": [ "delegate_permission/common.get_login_creds" ],
+ "target": {
+ "namespace": "web",
+ "site": "https://use-your-web-app-url-here.com/"
+ }
+ },
{
"relation": [
"delegate_permission/common.handle_all_urls",
@@ -14,7 +21,8 @@
},
{
"relation": [
- "delegate_permission/common.handle_all_urls"
+ "delegate_permission/common.handle_all_urls",
+ "delegate_permission/common.get_login_creds"
],
"target": {
"namespace": "android_app",
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs
index cf52aed8b4..98eccd614f 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs
@@ -1,6 +1,5 @@
using EmbedIO;
using System.Net;
-using System.Net.Http;
using System.Net.Sockets;
using EmbedIO.Actions;
using Boilerplate.Client.Core.Components;
@@ -69,6 +68,12 @@ public int Start(CancellationToken cancellationToken)
return port;
}
+ ///
+ ///
+ ///
+
+ public bool UseLocalHttpServerForSocialSignIn() => true;
+
private int GetAvailableTcpPort()
{
using TcpListener l = new TcpListener(IPAddress.Loopback, 0);