【问题标题】:PWA: force window.open to open browser instead of PWAPWA:强制 window.open 打开浏览器而不是 PWA
【发布时间】:2020-06-18 14:57:31
【问题描述】:

我已经构建了一个带有 Angular 前端的 ASP.NET Core 应用程序。 Angular 应用程序具有 @angular/pwa 节点包设置,因此它是一个渐进式 Web 应用程序,可以安装在 android/windows 上,其行为类似于原生应用程序。

我已经使用 Microsoft.AspNetCore.Identity 设置了外部登录(Microsoft、Google、Facebook、Twitter)。从我的 Angular 应用程序中,我打开了一个到外部登录页面的弹出窗口:

  this.authWindow = window.open(`${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}`, null, 'width=600,height=400');

弹出窗口的 url 路由到我有 return Challenge() 调用的 ASP.NET Core 端点,它返回特定外部提供商(Microsoft、Google、Facebook、Twitter)的登录页面。

在 Windows 上的 Chrome 中,单击触发 window.open() 的按钮以打开带有外部登录页面的窗口。成功登录后,您将被重定向到回调页面,这是一个剃须刀页面,它向包含 Angular 应用程序的主窗口发送消息。正在处理消息并正在关闭弹出窗口。

问题

当我在 Android 版 Chrome 上使用该网站时,我可以将 PWA 安装为应用程序,这会在我的 android 主页上添加一个图标。当我打开 PWA 并单击按钮打开弹出窗口时,弹出窗口正在我的 PWA 的弹出窗口中打开,所以没有问题。

当我在 Android 上打开 Chrome 并访问该网站时,在安装 PWA 的情况下,window.open() 调用不会打开 Chrome 浏览器的弹出窗口,而是尝试打开 Progressive Web App 的弹出窗口。由于是这种情况,PWA 内的弹出窗口无法在 Chrome 中通知网站登录成功(呃……)。

但是当没有安装 PWA 时,window.open() 可以正常工作并在 Chrome 本身中打开弹出窗口。

所以底线是,PWA 安装在 android 上。我希望能够在 Chrome 中从我的网站调用 window.open(),并让它在 Chrome 浏览器而不是 PWA 中打开弹出窗口。

我尝试过的事情

  1. 修改 ngsw-config.json

    { ..., “导航网址”:[ "/", "!//.", "!//__", "!//__/", "!/web/v2/Account/connect//", "!/web/v2/Account/add//**" ] }

  2. target='_system'打开窗口

    this.authWindow = window.open(${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}, '_system', 'width=600,height=400');

  3. target='_blank'打开窗口

    this.authWindow = window.open(${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}, '_blank', 'width=600,height=400');

  4. target='_blank'打开窗口,不用baseUrl,只是一个绝对路径。

    this.authWindow = window.open(/web/v2/Account/${this.action}/${medium}/${this.platform}, '_blank', 'width=600,height=400');

  5. 使用 ngsw-bypass

    this.authWindow = window.open(/web/v2/Account/${this.action}/${medium}/${this.platform}?ngsw-bypass=true, '_blank', 'width=600,height=400');

但所有技巧似乎都表现相同,并且仍然在 PWA 中打开窗口。

【问题讨论】:

标签: progressive-web-apps window.open


【解决方案1】:

我最终创建了一个子域来托管我的外部登录端点(ExternalLogin、ExternalLoginCallback、AddExternalLogin、AddExternalLoginCallback):

[Controller]
[Route("web/v2/[controller]")]
public class AccountController : Controller
{
    private IAccountService accountService;
    public AccountController(IAccountService accountService)
    {
        this.accountService = accountService;
    }

    ...

    // GET: web/Account/providers
    [AllowAnonymous]
    [HttpGet("providers", Name = "web-v2-account-external-providers")]
    public async Task<ActionResult<IEnumerable<string>>> Providers()
    {
        var result = await accountService.GetProviders();
        return Ok(result);
    }

    // GET: web/Account/connect/{provider}
    [AllowAnonymous]
    [HttpGet("connect/{medium}/{provider}", Name = "web-v2-account-external-connect-challenge")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> ExternalLogin([FromRoute]string medium, [FromRoute]string provider)
    {
        var redirectUrl = Url.RouteUrl("web-v2-account-external-connect-callback", new { medium, provider });
        var properties = await accountService.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
        return Challenge(properties, provider);
    }

    // GET: web/Account/connect/{provider}/callback
    [HttpGet("connect/{medium}/{provider}/callback", Name = "web-v2-account-external-connect-callback")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> ExternalLoginCallback([FromRoute]string medium, [FromRoute]string provider)
    {
        try
        {
            var login_result = await accountService.PerfromExternalLogin();
            if (login_result.Status)
            {
                var model = new LoginResultVM
                {
                    Status = true,
                    Medium = medium,
                    Platform = login_result.Platform
                };
                return View(model);
            }
            else
            {
                var model = new LoginResultVM
                {
                    Status = false,
                    Medium = medium,
                    Platform = login_result.Platform,

                    Error = login_result.Error,
                    ErrorDescription = login_result.ErrorDescription
                };
                return View(model);
            }
        }
        catch (OtherAccountException otherAccountEx)
        {
            var model = new LoginResultVM
            {
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = otherAccountEx.Message
            };
            return View(model);
        }
        catch (Exception ex)
        {
            var model = new LoginResultVM
            {
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = "There was an error with your social login"
            };
            return View(model);
        }
    }

    // GET: web/Account/logins
    [Authorize]
    [HttpGet("logins", Name = "web-v2-account-external-logins")]
    public async Task<ActionResult<IEnumerable<string>>> GetExternalLogins()
    {
        var logins = await accountService.GetExternalLogins(User);
        return Ok(logins.Select(l => l.ProviderDisplayName));
    }

    // GET: web/Account/add/{provider}
    [Authorize]
    [HttpGet("add/{medium}/{provider}", Name = "web-v2-account-external-add-challenge")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> AddExternalLogin([FromRoute]string medium, [FromRoute]string provider)
    {
        var redirectUrl = Url.RouteUrl("web-v2-account-external-add-callback", new { medium, provider });
        var properties = await accountService.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
        return Challenge(properties, provider);
    }

    // GET: web/Account/add/{provider}/callback
    [Authorize]
    [HttpGet("add/{medium}/{provider}/callback", Name = "web-v2-account-external-add-callback")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> AddExternalLoginCallback([FromRoute]string medium, [FromRoute]string provider)
    {
        try
        {
            await accountService.AddExternalLogin(User);
            var model = new LoginResultVM
            {
                Status = true,
                Medium = medium,
                Platform = provider
            };
            return View(model);
        }
        catch (Exception)
        {
            var model = new LoginResultVM
            {
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = "There was an error with your social login"
            };
            return View(model);
        }
    }
}

在 PWA 中运行时,window.open 仍将在 PWA 内的嵌入式浏览器中打开链接,从浏览器运行时 window.open 仍将在新的浏览器窗口中打开链接(不在 PWA 中) )。在这两种情况下,我仍然可以访问开启程序来发送消息 (window.opener.postMessage)。

【讨论】:

  • 嗯,我想实现 2FA 似乎遇到了麻烦。因为当使用外部登录时,ExternalLoginSigninAsync 是从 external.example.com 调用的,TwoFactorAuthenticatorSignInAsync 是从 example.com 调用的。所以2FA因此失败了......
  • 通过在子域上创建一个新的 Razor 视图来解决它,并在弹出窗口中重定向到它。
猜你喜欢
  • 2017-09-08
  • 2021-04-02
  • 1970-01-01
  • 2019-04-09
  • 2013-04-13
  • 2018-02-16
  • 2014-01-29
  • 2011-06-19
  • 1970-01-01
相关资源
最近更新 更多