I have built an ASP.NET Core application with an angular frontend. The angular app has the #angular/pwa node package setup, so it's a progressive web app that can be installed on android/windows behaving like a native app.
I have setup external logins (Microsoft, Google, Facebook, Twitter) with Microsoft.AspNetCore.Identity. From my angular app I'm opening a popup to an external login page:
this.authWindow = window.open(`${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}`, null, 'width=600,height=400');
The url for the popup routes to an ASP.NET Core endpoint where I have the return Challenge() call, which returns the login page for the specific external provider (Microsoft, Google, Facebook, Twitter).
In Chrome on Windows, you click a button which triggers the window.open() in order to open a window with the external login page. On successfull login you're being redirected to the callback page, which is a razor page which sends a message to the main window containing the angular app. The message is being handled and the popup is being closed.
Problem
When I use the website on Chrome for Android, I can install the PWA as app, which adds an icon on my android homepage. When I open the PWA and click the button to open the popup, the popup is being opened in a popup window for my PWA, so no problem there.
When I open Chrome on android and visit the website, while the PWA is installed, the window.open() call does not open a popup window for the Chrome browser, but instead tries to open the popup window for the Progressive Web App. Since this is the case, the popup window inside the PWA cannot notify the website in Chrome about the successful login (duh...).
But when the PWA is not installed, the window.open() works fine and opens the popup in Chrome itself.
So the bottom line is, the PWA is installed on android. And I want to be able to call window.open() from my website inside Chrome, and have it open the popup in Chrome browser instead of the PWA.
Things I've tried
Modify ngsw-config.json
{
...,
"navigationUrls": [
"/",
"!//.",
"!//__",
"!//__/",
"!/web/v2/Account/connect//",
"!/web/v2/Account/add//**"
]
}
Open the window with target='_system'
this.authWindow = window.open(${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}, '_system', 'width=600,height=400');
Open the window with target='_blank'
this.authWindow = window.open(${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}, '_blank', 'width=600,height=400');
Open the window with target='_blank' and without baseUrl, just an absolute path.
this.authWindow = window.open(/web/v2/Account/${this.action}/${medium}/${this.platform}, '_blank', 'width=600,height=400');
Use ngsw-bypass
this.authWindow = window.open(/web/v2/Account/${this.action}/${medium}/${this.platform}?ngsw-bypass=true, '_blank', 'width=600,height=400');
But all tricks seem to behave the same and still open the window in the PWA.
I ended up creating a subdomain hosting my endpoints for external login (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);
}
}
}
When running in the PWA, the window.open will still open the link inside an embedded browser within your PWA, and when running from the browser window.open will still open the link in a new browser window (not in your PWA). In both cases I'm still able to access the opener to send messages (window.opener.postMessage).
In my app I have main activity and child activities. An action on main activity opens a child activity. Each child activity loads web page that have various urls all pointing to server locations.
I am able to navigate to all the urls but when I press back, I am not redirected to the previous page. Instead it exists and brings me to main activity. The back button action should navigate to last url location. How to do that?
You need to add each url to a List object and then use that object to navigate back.
private LinkedList<String> navigationHistory;
#Override
protected void onCreate(Bundle savedInstanceState) {
....
mWebView.setWebViewClient(new MyWebViewClient());
....
}
#Override
public void onBackPressed() {
if (navigationHistory.size() > 0) // Check if the url is empty
{
navigationHistory.removeLast(); // Remove the last url
if (navigationHistory.size() > 0) {
// Load the last page
mWebView.loadUrl(navigationHistory.getLast());
} else {
super.onBackPressed();
}
} else {
super.onBackPressed();
}
}
private class MyWebViewClient extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) {
if (!navigationHistory.contains(url)) // Check for duplicate urls
navigationHistory.add(url); // add each loading url to List
super.onPageFinished(view, url);
}
}
i have installed Facebook Android app,but my code
public void shareUsingNativeDialog() {
if (playerChoice == INVALID_CHOICE || computerChoice == INVALID_CHOICE) {
ShareContent content = getLinkContent();
// share the app
if (shareDialog.canShow(content, ShareDialog.Mode.NATIVE)) {
shareDialog.show(content, ShareDialog.Mode.NATIVE);
} else {
showError(R.string.native_share_error);
}
} else {
ShareContent content = getThrowActionContent();
if (shareDialog.canShow(content, ShareDialog.Mode.NATIVE)) {
shareDialog.show(content, ShareDialog.Mode.NATIVE);
} else {
showError(R.string.native_share_error);
}
}
}
it's tip Native sharing requires the Facebook for Android application.
so why??
No need, default mode of ShareDialog will be Mode.AUTOMATIC. It will check if the facebook app is installed.
When the user clicks on back button, I've implemented the following code that works very well for links inside of my webview:
#Override
public void onBackPressed() {
if (this.webViewFragment != null && this.webViewFragment.canGoBack()) {
this.webViewFragment.goBack();
} else {
super.onBackPressed();
}
}
My problem is that I have native menu in Android, and when the user clicks on the menu Profile for example and then clicks on the menu Dashboard, the back button doesn't work. Nothing happens. As I said before, just works for links clicked inside of the webview.
Anyone knows a solution for that?
or try this
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && web.canGoBack()) {
web.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
I need to share a message on Facebook in my windows phone App by clicking share button. when click share button , if user has not logged in to Facebook, first we redirect to log in screen and then need to ask permission to publish.
public partial class FacebookLoginPage : PhoneApplicationPage
{
string uriToLaunch ;
// Create a Uri object from a URI string
Uri uri = null;
public FacebookLoginPage()
{
InitializeComponent();
uriToLaunch = #"fbconnect://authorize?client_id={AppID}&
scope=public_profile,publish_actions,read_stream&
redirect_uri=msft-{ProductId}%3a%2f%2fauthorize";
uri = new Uri(uriToLaunch);
this.Loaded += FacebookLoginPage_Loaded;
}
private void FacebookLoginPage_Loaded(object sender, RoutedEventArgs e)
{
DefaultLaunch();
}
// Launch the URI
async void DefaultLaunch()
{
// Launch the URI
var success = await Windows.System.Launcher.LaunchUriAsync(uri);
if (success)
{
// URI launched
}
else
{
// URI launch failed
}
}
}
I used above code , but permission screen not appears for publish. Output was as follows.
I followed example and used an AppId, then it works well. I feel that there is special configuration in Facebook App side . Please help me If anyone have idea about it.