How to integrate Google Identity Toolkit with a single webpage app (e.g. GWT) - gwt

I need to integrate Google's identitytoolkit (Google's identitytoolkit) with my Google webtoolkit (GWT) application.
However rendering the gitkit signInButton or widget is already not straightforward because the way to do it is linked to "window.onload". And I need to render the widget at a later moment.
I managed to do it (see below), but I am not happy with this and I wonder if someone else found a better way of integration.
private native void showGitKitWidget() /*-{
$wnd.google.identitytoolkit.signInButton(
'#gitkitDivId', // accepts any CSS selector
{
widgetUrl: "//127.0.0.1:8888/gitkit/signin.html",
signOutUrl: "/gitkit/signout",
popupMode: true
}
);
var evt = $doc.createEvent('Event');
evt.initEvent('load', false, false);
$wnd.dispatchEvent(evt);
}-*/;
Update
Actually using the popupMode parameter for the signInButton makes a seamless integration even closer. This lets the widget popup in a browser window and leaving the GWT window unchanged. Then in the widget instead of redirecting to the successUrl I use the JS callback to trigger an AJAX call instead.
var config = {
idps: ["googleplus"],
signInSuccessUrl: '//127.0.0.1:8888/gwt/servlet/gitkit/signedin',
callbacks: {
signInSuccess: function(tokenString, accountInfo,
opt_signInSuccessUrl) {
/* !!! Tell GWT parent window that we are ready...
I believe using a cookie for which the parent is
regularly looking is the way to go, because it
will work in mobile browsers too.
*/
return false; // prevents redirect to signInSuccessUrl
}
}
Update Finally the GWT parent window will wait for the result cookie and if found make the AJAX call to the signInSuccessUrl. It will also have to render the signInButton again, which will then show the signed in user.
So the only ugly workaround is how the signInButton is rendered using the onload method call.
It would be very helpful if there would be a way to render the signInButton dynamically when needed, for instance if there were a "$wnd.google.identitytoolkit.update()" method. This could be called any time for the first time and should also be able to handle signin-status change!
In conclusion, I have answered my own question, which might be helpful to others, but also I would still like to ask if there would be a better way, which I missed.

As you've noted, the Identity Toolkit widget currently needs to be triggered by page load. Single-page applications (like those built with GWT) should place the widget on a separate page. Then you can redirect the user there - or render via popup, as you've noted - to sign in the user in.
If rendering the sign-in button is a problem, there is documentation on how to load the widget directly.

Related

In wicket, how to make the page re-init when back button is hit

I'm using Wicket 6, and we have a situation where a user is hitting back and it's loading the page without initializing it from the page history. I want the page init to run so that data is read fresh and things are in the proper state. How can I make wicket do this?
I thought I was already doing this with a custom MountedMapper that someone had suggested long ago, but I have a breakpoint in the page constructor (the one that accepts PageParameters) and it's not running.
The custom MountedMapper:
if (requestHandler instanceof ListenerInterfaceRequestHandler || requestHandler instanceof BookmarkableListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
You could make your page stateless, so it is recreated on each access.
Or improved your page, so that it loads fresh data on each render:
either use appropriate models that automatically deliver up-to-date data or override #onConfigure() and update,

Fiori launchpad: handle logout event with custom backend call without `attachLogoutEvent` (UI5 < 1.81)

So, I found that the launchpad Container API provides an option to register a logout event with returning a promise (https://ui5.sap.com/#/api/sap.ushell.services.Container%23methods/attachLogoutEvent).
Unfortunately, after the implementation I found out that the UI5 version must be 1.81 or higher for parameter bAsync to work. In my project, we're at 1.78, so no promises for me.
What's the problem?
I want to make a backend call in the said logout event. This doesn't work, since, as far as I understood my debugging, the launchpad destroys everything just after my logout event has "finished" (= every line of code in the event has been gone through, ignoring sub-functions). Timeouts etc. don't work, because their calls would also be after code progressing has already finished, meaning the calls are deleted.
What have I tried?
Instant backend call without sub-functions → didn't work for the same reason as above.
Infinite while-looping until the backend call is processed → stack overflow.
While-looping with timeout/await → await not allowed in strict mode, timeout didn't work because of the above issue.
What do I think might work?
Stall code progression until the backend call has been finished.
Using a completely different method to get my logic into handling the logout (e.g. full custom logout).
Ask here for further ideas.
Does anyone have an idea on how to solve the issue with UI5 1.78?
Alright, I have found a solution to this. It's probably not the technically nicest, but it works and the result looks clean enough. This is from a S4/HANA system, so it might not be a universal solution (e.g. it doesn't consider logging off within the left-side pane which doesn't exist in my launchpad).
What did I do?
Instead of attaching my individual logic to the Fiori logout-event, I created a custom logout button with my individual logic, followed by calling the SICF logout node.
How did I do it?
Create a Launchpad plugin
In Component.js, add a new header item with custom logout function
// ushellLib required from "sap/ushell/library"
var oRenderer = ushellLib.Container.getRenderer("fiori2");
oRenderer.addHeaderEndItem("sap.ushell.ui.shell.ShellHeadItem", {
id: "logoutButton",
icon: "sap-icon://log",
// ...
press: [this._logout, this],
}, true, false);
_logout: function() {
this._callMyStuff();
window.location.href = "/sap/public/bc/icf/logoff";
},
In style.css, hide the original logout button (logoutBtn) in desktop (__list0...) and mobile (__list1...) to prevent skipping my logic by logging off via default logout.
#__list0-7-logoutBtn {
display: none;
}
#__list1-7-logoutBtn {
display: none;
}

Wicket implement flash redirection

I would like to perform something like a flash redirection (not sure if it is really called flash redirection).
After a certain action like delete device, I am redirecting to the device list page.
Now, for the redirect URL... I am appending &sdr=true and it works.
The problem is... that parameter (&sdr=true) stays there even after just refreshing the page.
If I remember it correctly, when I do flash refresh... the parameter stays there but will disappear on refresh... or just good for one refresh only.
Below is my method for redirection:
public static void redirect(String redirectUrl) {
throw new RedirectToUrlException(redirectUrl);
}
now, how do I implement the flash redirection in wicket? I am using wicket 6 version.
Or... I would like the parameter &sdr=true be good only for 1 request. When the page is refreshed or reloaded... it should be gone.
Thanks :)
Sorry if this question is very easy, I am really new to wicket
May be there is another solution for your problem.
Wicket could be stateful, i.e. it can keep state between the pages navigation. So you can do:
setResponsePage(new SomePage(someState));
this way there is no need to pass anything in the url and SomePage's constructor can decide what to do with the passed state.
If you prefer to add request parameter in the url then you may use
PageParameters params = new PageParameters();
params.put("sdr", "true");
setResponsePage(SomePage.class, params);
In SomePage's constructor you will need to remove the sdr parameter from the passed PageParameters so that it is not rendered in all urls inside the page, e.g. in links, form action, etc.
But if you want the parameter to disappear in a page refresh/reload then you will need to make another redirect:
public SomePage(PageParameters params) {
super(params);
StringValue srdValue = params.get("srd");
if (!srdValue.isNull()) {
params.remove("srd");
// do something custom
throw new RestartResponseException(this);
// or throw new RestartResponseException(getPageClass(), params);
}
}

How can I reload my page to redirect to an URI Fragment in VAADIN?

In my Vaadin webapp I have a tipical architecture with login. In some cases, the user can access directly to some resources using Vaadin URI Fragments (http://example.com/#fragment).
When a user tryes to access some resource, If the user has logged in, I take from the URL the #FRAGMENT and I bring him to it.
But if the user has no logged in, when he logs in I used to bring him to the main page using
getPage().open("/", "_self");
but since if I add an URI Fragment, the getPage().open(...) does not work.
Is there any way to redirect the user to a correct URL (URL with UriFragment in my case) from code?
Note that there is a fundamental difference in how navigation is handled in traditional web applications versus single-page applications as implemented with Vaadin. In traditional web applications you navigate through the app by making full HTTP GET-Requests on some path (such as www.example.com/myapp/home). On each such request, a full page reload is performed. You can't do that with Vaadin, as a full page reload means reloading the Vaadin widget set and rebuilding the page layout from the ground up. Therefore, single-page applications typically use the URI fragment for navigation purposes. Changes to this fragment are solely handled by the client-side JavaScript code. There will be no GET-Request induced by the browser when the URI fragment is changed.
That's why the approach you described doesn't work for you. Using Page.open(...) will open a web page through a HTTP GET-Request resulting in a complete reload of your Vaadin application.
The solution for your problem is to solely handle all navigation (including state-dependent redirects) through the Page object's URI fragment handling methods (or through the [Navigator][1] component). Redirecting in Vaadin can be achieved by programmatically setting the URI fragment with Page#setUriFragment() or Navigator#navigateTo() and having your URI handling code (or Navigator) take care of the rest. Only then it is assured that your users stay on the same page even when they are redirected to a login form or to some other place after logging in.
I would like to add to Roland's answer and share how I solved this.
My UI:
#Override
protected void init(VaadinRequest request) {
setSizeFull();
setContent(masterView);
getPage().addUriFragmentChangedListener(event -> present(event.getUriFragment()));
present(getPage().getUriFragment());
}
The masterView is just a CustomComponent that has a content section. When the menu is clicked, I simply setContent to the masterView's content section. Swapping out the middle, basically.
present method:
private void present(String fragment) {
masterView.setContent(getComponentFromFragment(fragment));
}
Finally:
private Component getComponentFromFragment(String fragment) {
if (fragment.equals(someOtherView.NAME))
return someOtherView;
return null; // null clears it out as in the welcome page
}
The important part is the present in the init. When the UI renders for the first time and fires the init, it goes ahead and grabs whatever the URI fragment is in the browser and presents that as well.
Works great.
Maybe this can work:
UI.getCurrent().getPage().executeJavaScript("window.location.href = 'http://google.com'");

Greasemonkey script not executed when unusual content loading is being used

I'm trying to write a Greasemonkey script for Facebook and having some trouble with the funky page/content loading that they do (I don't quite understand this - a lot of the links are actually just changing the GET, but I think they do some kind of server redirect to make the URL look the same to the browser too?). Essentially the only test required is putting a GM_log() on its own in the script. If you click around Facebook, even with facebook.com/* as the pattern, it is often not executed. Is there anything I can do, or is the idea of a "page load" fixed in Greasemonkey, and FB is "tricking" it into not running by using a single URL?
If I try to do some basic content manipulation like this:
GM.log("starting");
var GM_FB=new Object;
GM_FB.birthdays = document.evaluate("//div[#class='UIUpcoming_Item']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (i = GM_FB.birthdays.snapshotLength - 1; i >= 0; i--) {
if (GM_FB.birthdayRegex.test(GM_FB.birthdays.snapshotItem(i).innerHTML)) {
GM_FB.birthdays.snapshotItem(i).setAttribute('style','font-weight: bold; background: #fffe88');
}
}
The result is that sometimes only a manual page refresh will make it work. Pulling up the Firebug console and forcing the code to run works fine. Note that this isn't due to late loading of certain parts of the DOM: I have adding some code later to wait for the relevant elements and, crucially, the message never gets logged for certain transitions. For example, when I switch from Messages to News Feed and back.
Aren't they using ajax to load content in a div? You can find the element which is being updated by using Firebug for example.
When you click something and the URL changes, but with a # on the URL and after this some text, it means the text is not a path, it's a parameter, the browser won't change the page you are, so since GreaseMonkey inject the script on the page loads it won't inject again, because the page is not reloading.
As in your example the URL facebook.com/#!/sk=messages is not navigating away from facebook.com/ it will not fire window.load event.
So you need to find which element is being changed and add an event listener to that element, you can do is using Firebug as I mentioned before.
After you find out what element is getting the content, you have to add an event listener to that element and not the page (GreaseMonkey adds only on the window load event).
So in you GM script you would have ("air code")
document.getElement('dynamic_div').addEvent('load', /*your script*/);