Skip to content

Commit

Permalink
Improve Boilerplate social sign-in (#9620)
Browse files Browse the repository at this point in the history
  • Loading branch information
ysmoradi committed Jan 4, 2025
1 parent 379603e commit d8515a8
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 23 deletions.
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.UseLocalHttpServerForSocialSignIn() ? 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.UseLocalHttpServerForSocialSignIn() ? 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 UseLocalHttpServerForSocialSignIn();
}
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.UseLocalHttpServerForSocialSignIn"/>
/// </summary>
public bool UseLocalHttpServerForSocialSignIn() => false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//#if (framework == 'net9.0')
using Maui.AppStores;
using Maui.InAppReviews;
using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;
//#endif

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,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}");
}
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,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;

/// <summary>
/// <inheritdoc cref="ILocalHttpServer"/>
/// </summary>
public partial class MauiLocalHttpServer : ILocalHttpServer
{
[AutoInject] private ILogger<ILocalHttpServer> logger;
[AutoInject] private IExceptionHandler exceptionHandler;
[AutoInject] private AbsoluteServerAddressProvider absoluteServerAddress;

Expand Down Expand Up @@ -89,4 +88,12 @@ private int GetAvailableTcpPort()
l.Stop();
return port;
}

/// <summary>
/// <inheritdoc cref="ILocalHttpServer.UseLocalHttpServerForSocialSignIn"/>
/// </summary>
public bool UseLocalHttpServerForSocialSignIn()
{
return AppPlatform.IsAndroid is false;
}
}
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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",
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.UseLocalHttpServerForSocialSignIn"/>
/// </summary>

public bool UseLocalHttpServerForSocialSignIn() => true;

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

0 comments on commit d8515a8

Please sign in to comment.