Skip to content

Commit 8f55391

Browse files
authored
feat(templates): improve social sign-in in Boilerplate #9620 (#9621)
1 parent 379603e commit 8f55391

File tree

9 files changed

+51
-26
lines changed

9 files changed

+51
-26
lines changed

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private async Task SocialSignIn(string provider)
9696
{
9797
try
9898
{
99-
var port = localHttpServer.Start(CurrentCancellationToken);
99+
var port = localHttpServer.ShouldUseForSocialSignIn() ? localHttpServer.Start(CurrentCancellationToken) : -1;
100100

101101
var redirectUrl = await identityController.GetSocialSignInUri(provider, ReturnUrlQueryString, port is -1 ? null : port, CurrentCancellationToken);
102102

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private async Task SocialSignUp(string provider)
7575
{
7676
try
7777
{
78-
var port = localHttpServer.Start(CurrentCancellationToken);
78+
var port = localHttpServer.ShouldUseForSocialSignIn() ? localHttpServer.Start(CurrentCancellationToken) : -1;
7979

8080
var redirectUrl = await identityController.GetSocialSignInUri(provider, localHttpPort: port is -1 ? null : port, cancellationToken: CurrentCancellationToken);
8181

Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
namespace Boilerplate.Client.Core.Services.Contracts;
22

3-
/// <summary>
4-
/// Social sign-in functions seamlessly on web browsers and on Android and iOS via universal app links.
5-
/// However, for blazor hybrid, a local HTTP server is needed to ensure a smooth social sign-in experience.
6-
/// </summary>
73
public interface ILocalHttpServer
84
{
95
int Start(CancellationToken cancellationToken);
6+
7+
/// <summary>
8+
/// 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.
9+
///
10+
/// # Navigating Back to the App After Social Sign-In
11+
/// 1. **Universal Deep Links**: Allow the app to directly handle specific web links (for iOS and Android apps).
12+
/// 2. **Local HTTP Server**: Works similarly to how `git.exe` manages sign-ins with services like GitHub (supported on iOS, Android, Windows, and macOS).
13+
///
14+
/// - **iOS, Windows, and macOS**: Use local HTTP server implementations in MAUI and Windows projects.
15+
/// - **Android**: Use universal links.
16+
/// </summary>
17+
bool ShouldUseForSocialSignIn();
1018
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
namespace Boilerplate.Client.Core.Services;
22

3-
/// <summary>
4-
/// <inheritdoc cref="ILocalHttpServer"/>
5-
/// The <see cref="NoopLocalHttpServer"/> is specifically registered for Android, iOS, and Web, where a local HTTP server is unnecessary.
6-
/// </summary>
73
public partial class NoopLocalHttpServer : ILocalHttpServer
84
{
9-
public int Start(CancellationToken cancellationToken) => -1;
5+
public int Start(CancellationToken cancellationToken) => throw new NotImplementedException();
6+
7+
/// <summary>
8+
/// <inheritdoc cref="ILocalHttpServer.ShouldUseForSocialSignIn"/>
9+
/// </summary>
10+
public bool ShouldUseForSocialSignIn() => false;
1011
}

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Maui.InAppReviews;
55
using System.Runtime.InteropServices;
66
//#endif
7+
using Microsoft.Extensions.Logging;
78

89
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
910

@@ -15,6 +16,7 @@ public partial class App
1516
//#if (framework == 'net9.0')
1617
private readonly IStorageService storageService;
1718
//#endif
19+
private readonly ILogger<App> logger;
1820
private readonly IExceptionHandler exceptionHandler;
1921
private readonly IBitDeviceCoordinator deviceCoordinator;
2022
private readonly IStringLocalizer<AppStrings> localizer;
@@ -24,10 +26,12 @@ public App(MainPage mainPage,
2426
PubSubService pubSubService,
2527
IStorageService storageService,
2628
//#endif
29+
ILogger<App> logger,
2730
IExceptionHandler exceptionHandler,
2831
IBitDeviceCoordinator deviceCoordinator,
2932
IStringLocalizer<AppStrings> localizer)
3033
{
34+
this.logger = logger;
3135
this.localizer = localizer;
3236
//#if (framework == 'net9.0')
3337
this.storageService = storageService;
@@ -72,11 +76,17 @@ protected override async void OnStart()
7276
//+:cnd:noEmit
7377
//#if (framework == 'net9.0')
7478
const int minimumSupportedWebViewVersion = 94;
79+
// Download link for Android emulator (x86 or x86_64)
80+
// https://www.apkmirror.com/apk/google-inc/chrome/chrome-94-0-4606-50-release/
81+
// https://www.apkmirror.com/apk/google-inc/android-system-webview/android-system-webview-94-0-4606-85-release/
7582
//#elif (framework == 'net8.0')
7683
//#if (IsInsideProjectTemplate)
7784
/*
7885
//#endif
7986
const int minimumSupportedWebViewVersion = 84;
87+
// Download link for Android emulator (x86 or x86_64)
88+
// https://www.apkmirror.com/apk/google-inc/chrome/chrome-84-0-4147-89-release/
89+
// https://www.apkmirror.com/apk/google-inc/android-system-webview/android-system-webview-84-0-4147-111-release/
8090
//#if (IsInsideProjectTemplate)
8191
*/
8292
//#endif
@@ -85,8 +95,10 @@ protected override async void OnStart()
8595
if (Version.TryParse(Android.Webkit.WebView.CurrentWebViewPackage?.VersionName, out var webViewVersion) &&
8696
webViewVersion.Major < minimumSupportedWebViewVersion)
8797
{
98+
var webViewName = Android.Webkit.WebView.CurrentWebViewPackage.PackageName;
99+
logger.LogWarning("{webViewName} version {version} is not supported.", webViewName, webViewVersion);
88100
await App.Current!.Windows[0].Page!.DisplayAlert("Boilerplate", localizer[nameof(AppStrings.UpdateWebViewThroughGooglePlay)], localizer[nameof(AppStrings.Ok)]);
89-
await Launcher.OpenAsync($"https://play.google.com/store/apps/details?id={Android.Webkit.WebView.CurrentWebViewPackage.PackageName}");
101+
await Launcher.OpenAsync($"https://play.google.com/store/apps/details?id={webViewName}");
90102
}
91103
//-:cnd:noEmit
92104
#endif

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs

+1-6
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,7 @@ public static void ConfigureServices(this MauiAppBuilder builder)
3939
return settings;
4040
});
4141
services.AddSingleton(ITelemetryContext.Current!);
42-
if (AppPlatform.IsAndroid is false)
43-
{
44-
// Handle social sign-in callback on local HTTP server.
45-
// But in Android, leverage Universal Links for smoother sign-in flows.
46-
services.AddSingleton<ILocalHttpServer, MauiLocalHttpServer>();
47-
}
42+
services.AddSingleton<ILocalHttpServer, MauiLocalHttpServer>();
4843

4944
services.AddMauiBlazorWebView();
5045
services.AddBlazorWebViewDeveloperTools();

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
using EmbedIO;
22
using System.Net;
3-
using System.Net.Sockets;
43
using EmbedIO.Actions;
4+
using System.Net.Sockets;
55
using Boilerplate.Client.Core.Components;
66

77
namespace Boilerplate.Client.Maui.Services;
88

9-
/// <summary>
10-
/// <inheritdoc cref="ILocalHttpServer"/>
11-
/// </summary>
129
public partial class MauiLocalHttpServer : ILocalHttpServer
1310
{
1411
[AutoInject] private IExceptionHandler exceptionHandler;
@@ -89,4 +86,12 @@ private int GetAvailableTcpPort()
8986
l.Stop();
9087
return port;
9188
}
89+
90+
/// <summary>
91+
/// <inheritdoc cref="ILocalHttpServer.ShouldUseForSocialSignIn"/>
92+
/// </summary>
93+
public bool ShouldUseForSocialSignIn()
94+
{
95+
return AppPlatform.IsAndroid is false;
96+
}
9297
}

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/assetlinks.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
[
1+
[
22
{
33
"relation": [
4-
"delegate_permission/common.handle_all_urls",
5-
"delegate_permission/common.get_login_creds"
4+
"delegate_permission/common.handle_all_urls"
65
],
76
"target": {
87
"namespace": "android_app",

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using EmbedIO;
22
using System.Net;
3-
using System.Net.Http;
43
using System.Net.Sockets;
54
using EmbedIO.Actions;
65
using Boilerplate.Client.Core.Components;
@@ -69,6 +68,12 @@ public int Start(CancellationToken cancellationToken)
6968
return port;
7069
}
7170

71+
/// <summary>
72+
/// <inheritdoc cref="ILocalHttpServer.ShouldUseForSocialSignIn"/>
73+
/// </summary>
74+
75+
public bool ShouldUseForSocialSignIn() => true;
76+
7277
private int GetAvailableTcpPort()
7378
{
7479
using TcpListener l = new TcpListener(IPAddress.Loopback, 0);

0 commit comments

Comments
 (0)