diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/settings.VisualStudio.json b/src/Templates/Boilerplate/Bit.Boilerplate/settings.VisualStudio.json
index b49045bc2a..1cbd73147e 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/settings.VisualStudio.json
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/settings.VisualStudio.json
@@ -1,6 +1,7 @@
/* Visual Studio Settings File */
{
- "debugging.general.disableJITOptimization": true,
- "environment.documents.saveWithSpecificEncoding": true,
- "environment.documents.saveEncoding": "utf-8;65001"
+ "languages.defaults.general.lineNumbers": true,
+ "debugging.general.disableJITOptimization": true,
+ "environment.documents.saveWithSpecificEncoding": true,
+ "environment.documents.saveEncoding": "utf-8;65001"
}
\ No newline at end of file
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 565bc7d98a..aca3662a8e 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
@@ -152,7 +152,7 @@ private async Task HandleOnSocialSignIn(string provider)
{
try
{
- var port = localHttpServer.ShouldUseForSocialSignIn() ? localHttpServer.EnsureStarted() : -1;
+ var port = localHttpServer.EnsureStarted();
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 082d8c7385..81416738c1 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.ShouldUseForSocialSignIn() ? localHttpServer.EnsureStarted() : -1;
+ var port = localHttpServer.EnsureStarted();
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/Scripts/ExternalJsRunner.ts b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/ExternalJsRunner.ts
index 4cca893542..70d0651180 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/ExternalJsRunner.ts
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/ExternalJsRunner.ts
@@ -1,4 +1,4 @@
-// Checkout external-js-runner.html
+// Checkout external-js-runner.html
class ExternalJsRunner {
public static async run() {
const host = window.origin.replace('http://', '');
@@ -12,11 +12,10 @@ class ExternalJsRunner {
} else if (request.type == 'createCredential') {
result = await WebAuthn.createCredential(request.options);
} else if (request.type == 'close') {
- result = {};
localWebSocket.close();
- setTimeout(() => {
- window.close();
- }, 100);
+ window.close();
+ window.location.assign('/close-browser');
+ return;
}
localWebSocket.send(JSON.stringify({ body: result }));
}
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 5c7eed8a03..e6fe6ea4d8 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
@@ -7,16 +7,4 @@ public interface ILocalHttpServer : IAsyncDisposable
int Port { get; }
string? Origin { get; }
-
- ///
- /// 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 ShouldUseForSocialSignIn();
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegatingHandler.cs
index 63cf728576..6925767e42 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegatingHandler.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegatingHandler.cs
@@ -12,7 +12,7 @@ protected override async Task SendAsync(HttpRequestMessage
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Omit);
request.SetBrowserResponseStreamingEnabled(true);
- request.Version = HttpVersion.Version30;
+ request.Version = HttpVersion.Version20;
request.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
if (request.Headers.UserAgent.Any() is false)
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 caf16cd168..248dc5832a 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
@@ -8,10 +8,5 @@ public partial class NoOpLocalHttpServer : ILocalHttpServer
public int Port => -1;
- ///
- ///
- ///
- public bool ShouldUseForSocialSignIn() => false;
-
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
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 ccc1da4e75..c55dc10154 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
@@ -33,23 +33,7 @@ public int EnsureStarted()
{
try
{
- // Redirect to SocialSignedInPage.razor that will close the browser window.
- var url = new Uri(absoluteServerAddress, $"/api/Identity/SocialSignedIn?culture={CultureInfo.CurrentUICulture.Name}").ToString();
- ctx.Redirect(url);
-
- if (AppPlatform.IsIOS)
- {
- // SocialSignedInPage.razor's `window.close()` does NOT work on iOS's in app browser.
- await MainThread.InvokeOnMainThreadAsync(() =>
- {
-#if iOS
- if (UIKit.UIApplication.SharedApplication.KeyWindow?.RootViewController?.PresentedViewController is SafariServices.SFSafariViewController controller)
- {
- controller.DismissViewController(animated: true, completionHandler: null);
- }
-#endif
- });
- }
+ ctx.Redirect("/close-browser");
_ = Task.Delay(1)
.ContinueWith(async _ =>
@@ -65,10 +49,42 @@ await MainThread.InvokeOnMainThreadAsync(async () =>
exceptionHandler.Handle(exp);
}
}))
+ .WithModule(new ActionModule("/close-browser", HttpVerbs.Get, async ctx =>
+ {
+ // Redirect to CloseBrowserPage.razor that will close the browser window.
+ var url = new Uri(absoluteServerAddress, $"/api/Identity/CloseBrowserPage?culture={CultureInfo.CurrentUICulture.Name}").ToString();
+ ctx.Redirect(url);
+
+ if (AppPlatform.IsIOS)
+ {
+ // CloseBrowserPage.razor's `window.close()` does NOT work on iOS's in app browser.
+ await MainThread.InvokeOnMainThreadAsync(() =>
+ {
+#if iOS
+ if (UIKit.UIApplication.SharedApplication.KeyWindow?.RootViewController?.PresentedViewController is SafariServices.SFSafariViewController controller)
+ {
+ controller.DismissViewController(animated: true, completionHandler: null);
+ }
+#endif
+ });
+ }
+ else if (AppPlatform.IsAndroid)
+ {
+#if Android
+ await MainThread.InvokeOnMainThreadAsync(() =>
+ {
+ var intent = new Android.Content.Intent(Platform.AppContext, typeof(Platforms.Android.MainActivity));
+ intent.SetFlags(Android.Content.ActivityFlags.NewTask | Android.Content.ActivityFlags.ClearTop);
+ Platform.AppContext.StartActivity(intent);
+ });
+#endif
+ }
+ }))
.WithModule(new ActionModule("/external-js-runner.html", HttpVerbs.Get, async ctx =>
{
try
{
+ ctx.Response.ContentType = "text/html";
await using var file = Assembly.Load("Boilerplate.Client.Maui").GetManifestResourceStream("Boilerplate.Client.Maui.wwwroot.external-js-runner.html")!;
await file.CopyToAsync(ctx.Response.OutputStream, ctx.CancellationToken);
}
@@ -81,6 +97,7 @@ await MainThread.InvokeOnMainThreadAsync(async () =>
{
try
{
+ ctx.Response.ContentType = "application/javascript";
await using var file = Assembly.Load("Boilerplate.Client.Maui").GetManifestResourceStream("Boilerplate.Client.Maui.wwwroot.scripts.app.js")!;
await file.CopyToAsync(ctx.Response.OutputStream, ctx.CancellationToken);
}
@@ -126,11 +143,4 @@ public async ValueTask DisposeAsync()
localHttpServer?.Dispose();
}
- ///
- ///
- ///
- public bool ShouldUseForSocialSignIn()
- {
- return AppPlatform.IsAndroid is false;
- }
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiWebAuthnService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiWebAuthnService.cs
index ead7c54308..8d82b1559c 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiWebAuthnService.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiWebAuthnService.cs
@@ -26,21 +26,7 @@ public override async ValueTask GetWebAuthnCr
private static async Task CloseExternalBrowser()
{
- await MauiExternalJsRunner.RequestToBeSent!.Invoke(JsonSerializer.SerializeToDocument(new { Type = "close" }, JsonSerializerOptions.Web));
-
- if (AppPlatform.IsIOS)
- {
- // SocialSignedInPage.razor's `window.close()` does NOT work on iOS's in app browser.
- await MainThread.InvokeOnMainThreadAsync(() =>
- {
-#if iOS
- if (UIKit.UIApplication.SharedApplication.KeyWindow?.RootViewController?.PresentedViewController is SafariServices.SFSafariViewController controller)
- {
- controller.DismissViewController(animated: true, completionHandler: null);
- }
-#endif
- });
- }
+ _ = MauiExternalJsRunner.RequestToBeSent!.Invoke(JsonSerializer.SerializeToDocument(new { Type = "close" }, JsonSerializerOptions.Web));
}
public override async ValueTask CreateWebAuthnCredential(CredentialCreateOptions options)
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/external-js-runner.html b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/external-js-runner.html
index ebab2f2fe3..b979768b18 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/external-js-runner.html
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/external-js-runner.html
@@ -1,36 +1,40 @@
-
+
- 🔒 Passwordless login
-
+ Boilerplate
+
@@ -41,7 +45,7 @@
-->
-
+
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 ca60c408f1..bd320f5a8a 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
@@ -35,17 +35,11 @@ public int EnsureStarted()
{
try
{
- var url = new Uri(absoluteServerAddress, $"/api/Identity/SocialSignedIn?culture={CultureInfo.CurrentUICulture.Name}").ToString();
-
- ctx.Redirect(url);
+ ctx.Redirect("/close-browser");
_ = Task.Delay(1)
.ContinueWith(async _ =>
{
- Application.OpenForms[0]!.Invoke(() =>
- {
- Application.OpenForms[0]!.Activate();
- });
await Routes.OpenUniversalLink(ctx.Request.Url.PathAndQuery, replace: true);
});
}
@@ -54,10 +48,22 @@ public int EnsureStarted()
exceptionHandler.Handle(exp);
}
}))
+ .WithModule(new ActionModule("/close-browser", HttpVerbs.Get, async ctx =>
+ {
+ // Redirect to CloseBrowserPage.razor that will close the browser window.
+ var url = new Uri(absoluteServerAddress, $"/api/Identity/CloseBrowserPage?culture={CultureInfo.CurrentUICulture.Name}").ToString();
+ ctx.Redirect(url);
+
+ Application.OpenForms[0]!.Invoke(() =>
+ {
+ Application.OpenForms[0]!.Activate();
+ });
+ }))
.WithModule(new ActionModule("/external-js-runner.html", HttpVerbs.Get, async ctx =>
{
try
{
+ ctx.Response.ContentType = "text/html";
await using var fileStream = File.OpenRead("wwwroot/external-js-runner.html");
await fileStream.CopyToAsync(ctx.Response.OutputStream, ctx.CancellationToken);
}
@@ -70,6 +76,7 @@ public int EnsureStarted()
{
try
{
+ ctx.Response.ContentType = "application/javascript";
var filePath = Path.Combine(AppContext.BaseDirectory, @"wwwroot\_content\Boilerplate.Client.Core\scripts\app.js");
if (File.Exists(filePath) is false)
{
@@ -106,12 +113,6 @@ public int EnsureStarted()
return port;
}
- ///
- ///
- ///
-
- public bool ShouldUseForSocialSignIn() => true;
-
public async ValueTask DisposeAsync()
{
localHttpServer?.Dispose();
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsWebAuthnService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsWebAuthnService.cs
index 7084a6ee58..badb8ca166 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsWebAuthnService.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsWebAuthnService.cs
@@ -26,12 +26,7 @@ public override async ValueTask GetWebAuthnCr
private static async Task CloseExternalBrowser()
{
- await WindowsExternalJsRunner.RequestToBeSent!.Invoke(JsonSerializer.SerializeToDocument(new { Type = "close" }, JsonSerializerOptions.Web));
-
- Application.OpenForms[0]!.Invoke(() =>
- {
- Application.OpenForms[0]!.Activate();
- });
+ _ = WindowsExternalJsRunner.RequestToBeSent!.Invoke(JsonSerializer.SerializeToDocument(new { Type = "close" }, JsonSerializerOptions.Web));
}
public override async ValueTask CreateWebAuthnCredential(CredentialCreateOptions options)
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/SocialSignedInPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/CloseBrowserPage.razor
similarity index 59%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/SocialSignedInPage.razor
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/CloseBrowserPage.razor
index 7d1fe4f328..6c1e66bd62 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/SocialSignedInPage.razor
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/CloseBrowserPage.razor
@@ -7,13 +7,15 @@
-
- @Localizer[nameof(AppStrings.SocialSignedInTitle)]
+
+ @Localizer[nameof(AppStrings.CloseBrowserMessageTitle)]