我建立了一个ASP.NET Core应用程序和一个angular前端。Angular应用程序有 @angular/pwa
node包的设置,所以它是一个可以安装在androidwindows上的渐进式网络应用,表现得像一个本地应用。
我已经用Microsoft.AspNetCore.Identity.设置了外部登录(微软,谷歌,Facebook,Twitter)。在我的Angular应用中,我打开了一个弹出式的外部登录页面。
this.authWindow = window.open(`${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}`, null, 'width=600,height=400');
弹出窗口的url指向一个ASP.NET Core端点,我在那里设置了... return Challenge()
调用,返回特定外部提供者(Microsoft、Google、Facebook、Twitter)的登录页面。
在Windows的Chrome浏览器中,您点击一个按钮,触发window.open()以打开一个带有外部登录页面的窗口。登录成功后,你会被重定向到回调页面,这是一个剃须刀页面,它向包含angular应用的主窗口发送消息。该消息正在被处理,弹出窗口正在被关闭。
我在安卓版Chrome浏览器上使用网站时,可以把PWA安装成app,在我的安卓主页上增加一个图标。当我打开PWA并点击按钮打开弹出窗口时,弹出窗口正在打开我的PWA的弹出窗口,所以没有问题。
当我在android上打开Chrome浏览器并访问网站时,在安装PWA的同时,该 window.open()
调用并没有打开Chrome浏览器的弹出窗口,而是试图打开Progressive Web App的弹出窗口。既然如此,PWA里面的弹出窗口就无法通知Chrome浏览器中的网站登录成功(duh...)。
但是,当PWA没有安装时,就会出现 window.open()
可以正常工作,并在Chrome浏览器中自行打开弹出窗口。
所以底线是在安卓系统上安装了PWA。我希望能够调用 window.open()
从我的网站在Chrome浏览器内,并让它打开弹出窗口在 Chrome浏览器 而不是PWA。
1) 修改ngsw-config.json。
{
...,
"navigationUrls": [
"/**",
"!/**/*.*",
"!/**/*__*",
"!/**/*__*/**",
"!/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');
但是所有的技巧似乎都是一样的,仍然在PWA中打开窗口。
我最终创建了一个子域,托管我的外部登录端点(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)。