Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Boilerplate social sign-in (#9620) #9621

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ private async Task SocialSignIn(string provider)
{
try
{
var port = localHttpServer.Start(CurrentCancellationToken);
var port = localHttpServer.ShouldUseForSocialSignIn() ? localHttpServer.Start(CurrentCancellationToken) : -1;

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private async Task SocialSignUp(string provider)
{
try
{
var port = localHttpServer.Start(CurrentCancellationToken);
var port = localHttpServer.ShouldUseForSocialSignIn() ? localHttpServer.Start(CurrentCancellationToken) : -1;

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
namespace Boilerplate.Client.Core.Services.Contracts;

/// <summary>
/// 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.
/// </summary>
public interface ILocalHttpServer
{
int Start(CancellationToken cancellationToken);

/// <summary>
/// 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.
/// </summary>
bool ShouldUseForSocialSignIn();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
namespace Boilerplate.Client.Core.Services;

/// <summary>
/// <inheritdoc cref="ILocalHttpServer"/>
/// The <see cref="NoopLocalHttpServer"/> is specifically registered for Android, iOS, and Web, where a local HTTP server is unnecessary.
/// </summary>
public partial class NoopLocalHttpServer : ILocalHttpServer
{
public int Start(CancellationToken cancellationToken) => -1;
public int Start(CancellationToken cancellationToken) => throw new NotImplementedException();

/// <summary>
/// <inheritdoc cref="ILocalHttpServer.ShouldUseForSocialSignIn"/>
/// </summary>
public bool ShouldUseForSocialSignIn() => false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Maui.InAppReviews;
using System.Runtime.InteropServices;
//#endif
using Microsoft.Extensions.Logging;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

Expand All @@ -15,6 +16,7 @@ public partial class App
//#if (framework == 'net9.0')
private readonly IStorageService storageService;
//#endif
private readonly ILogger<App> logger;
private readonly IExceptionHandler exceptionHandler;
private readonly IBitDeviceCoordinator deviceCoordinator;
private readonly IStringLocalizer<AppStrings> localizer;
Expand All @@ -24,10 +26,12 @@ public App(MainPage mainPage,
PubSubService pubSubService,
IStorageService storageService,
//#endif
ILogger<App> logger,
IExceptionHandler exceptionHandler,
IBitDeviceCoordinator deviceCoordinator,
IStringLocalizer<AppStrings> localizer)
{
this.logger = logger;
this.localizer = localizer;
//#if (framework == 'net9.0')
this.storageService = storageService;
Expand Down Expand Up @@ -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
Expand All @@ -85,8 +95,10 @@ protected override async void OnStart()
if (Version.TryParse(Android.Webkit.WebView.CurrentWebViewPackage?.VersionName, out var webViewVersion) &&
webViewVersion.Major < minimumSupportedWebViewVersion)
{
var webViewName = Android.Webkit.WebView.CurrentWebViewPackage.PackageName;
logger.LogWarning("{webViewName} version {version} is not supported.", webViewName, 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}");
await Launcher.OpenAsync($"https://play.google.com/store/apps/details?id={webViewName}");
}
//-:cnd:noEmit
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ILocalHttpServer, MauiLocalHttpServer>();
}
services.AddSingleton<ILocalHttpServer, MauiLocalHttpServer>();

services.AddMauiBlazorWebView();
services.AddBlazorWebViewDeveloperTools();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
using EmbedIO;
using System.Net;
using System.Net.Sockets;
using EmbedIO.Actions;
using System.Net.Sockets;
using Boilerplate.Client.Core.Components;

namespace Boilerplate.Client.Maui.Services;

/// <summary>
/// <inheritdoc cref="ILocalHttpServer"/>
/// </summary>
public partial class MauiLocalHttpServer : ILocalHttpServer
{
[AutoInject] private IExceptionHandler exceptionHandler;
Expand Down Expand Up @@ -89,4 +86,12 @@ private int GetAvailableTcpPort()
l.Stop();
return port;
}

/// <summary>
/// <inheritdoc cref="ILocalHttpServer.ShouldUseForSocialSignIn"/>
/// </summary>
public bool ShouldUseForSocialSignIn()
{
return AppPlatform.IsAndroid is false;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[
[
{
"relation": [
"delegate_permission/common.handle_all_urls",
"delegate_permission/common.get_login_creds"
"delegate_permission/common.handle_all_urls"
],
"target": {
"namespace": "android_app",
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -69,6 +68,12 @@ public int Start(CancellationToken cancellationToken)
return port;
}

/// <summary>
/// <inheritdoc cref="ILocalHttpServer.ShouldUseForSocialSignIn"/>
/// </summary>

public bool ShouldUseForSocialSignIn() => true;

private int GetAvailableTcpPort()
{
using TcpListener l = new TcpListener(IPAddress.Loopback, 0);
Expand Down
Loading