localStorage access between two google app scripts deployed as web apps - javascript

I am trying to handover a string from GAS_web_app_A to GAS_web_app_B. GAS_web_app_B gets launched from a button on GAS_web_app_A's UI.
As localStorage is supposed to be accessible across the same origin, I stored 'stuff' from GAS_web_app_A and tried to read it from GAS_web_app_B - in the same browser - chrome.
But GAS_web_app_B is not able to read this 'stuff' stored by GAS_web_app_A.
1) do all google app scripts that are deployed as a web apps get resolved by browser's localStorage as being from the same origin?
step 1 : launch GAS_web_app_A via its URL - https: //script.google.com/a/macros/XXXX.com/s//dev
step 2 : localStorage.setItem("stuff", stuff);
step 3 : launch GAS_web_app_B via its URL - https: //script.google.com/a/macros/XXXX.com/s//dev (from GAS_web_app_A)
step 4 : console.log( 'stuff is ' + localStorage.getItem("stuff") );
I always get null in step 4. ( I am able to retrieve 'stuff' from GAS_web_app_A even after a reload though... )
Is this supposed to work or am I missing something trivial?

absolutely not supposed to work. it would be a huge security issue. look into passing url parameters to the 2nd script.

Related

Google Auth OAuth 2.0 SvelteKit wierd behavior

I am using Google Auth OAuth 2.0 One Tap Sign and Sveltekit,
and I got some really weird behavior,
I followed this doc https://developers.google.com/identity/gsi/web/guides/overview with javascript
onMount(async () => {
const handleCredentialResponse = async (response) => {
console.log('Encoded JWT ID token: ' + response.credential);
};
google.accounts.id.initialize({
client_id: clientId,
callback: handleCredentialResponse,
});
google.accounts.id.renderButton(
document.getElementById('buttonDiv'),
{ theme: 'outline', size: 'large' } // customization attributes
);
google.accounts.id.prompt();
});
the code from the doc.
Sometimes it works everything goes well,
Sometimes I got
Uncaught (in promise) ReferenceError: google is not defined
And some mobile / mobile browsers I get
[GSI_LOGGER]: The given origin is not allowed for the given client ID.
but works on laptop
Hope someone can understand and help.
Do we have any way to check logs for investigation?
I got your code to successfully run without modification: https://google-signin-kit.vercel.app/ (This app is in "dev" mode so signin may only succeed with my Google account. If you clone my repo and set your Google client id, signin will work for you.)
I would check how my code is different from your code outside onMount(). For example, how did you include the Google javascript? I describe one major change below.
You also need to check your Google app settings. [GSI_LOGGER]: The given origin is not allowed... is fixed by adding the HTTPS app domain to your Google app "Authorized JavaScript origins." Non-HTTPS domains are not allowed. For example, these domains would not work:
https://google-signin-kit-leftium.vercel.app/ (Not added as Authorized JavaScript origin)
http://google-signin-kit.vercel.app/ (Not HTTPS, if Vercel did not automatically redirect to HTTPS)
Of course, raw IP addresses will not work, either.
localhost is a special exception, but not easy (impossible?) to access from mobile.
ReferenceError: google is not defined (sometimes) happens because onMount() runs before the Google script is loaded.
To get a consistent reproduction, USB debug/inspect Android Chrome and set "disable caching" and throttling to "Slow 3G." (I could not reproduce on desktop Chrome).
Fixed by removing defer async when including Google's script: <script src="https://accounts.google.com/gsi/client"></script>
It should also be possible to call a function after the script is loaded, but I got inconsistent results in SvelteKit. For some reason Kit doesn't always call the load function. Possibly a bug?
Ideally, the Google script should be imported, but I couldn't find a version of the Google script that was built for importing. You may be able to construct a library that you can import? Importing would compile the Google script code into your app, tree-shaking the unused portions. Thus saving a network request and bandwidth.
Another workaround is to have your onMount function check for the google variable. If google is still undefined, call your function again after a certain timeout.

Silent Renewal in Ionic 3 with Identity Server 4

So I am trying to implement silent renewal in an ionic 3 application, I am still learning about the whole thing so I'll try to be as descriptive as possible please correct me if I am wrong.
Authentication
I am using Implicit flow for my authentication using the In App Browser.
The user is redirected to the authentication server page
After a success authentication I retrieve the id-token & access-token
As I understand the id-token is for authentication and access-token is for authorization with the API.
I have followed this article to help me set this up (Particularly the "Deploy to Mobile Device" section) for my Identity Server.
As done in the article I am using angular-oauth2-oidc to assist me with storing information about redirect links, issuer, etc...
I have attempted to achieve this in 3 different ways, 2 of them work but I don't understand how to retrieve the new access-token and id-token, and the last one returns an error. each of them left me with questions.
First: oauthService
The angular-oauth2-oidc library has a silentRefresh() method, and this github MD describes how to use it using a hidden iframe very vaguely so I barely understand how this works. I have created a silent-refresh.html page in the same directory, but using http://localhost:8000/silent-refresh.html return's a 404. Calling the silentRefresh() method successfully re-authenticates on the server side but the request times-out on the client side, as said in the MD file, something is wrong with the iframe.
Question 1: Does the library create a new iframe and then waits for a response to the http://localhost:8000/silent-refresh.html page but is never found so I never get my response? How to proceed to retrieve my tokens?
Second: iframe
So here I follow this article where I create an iframe and add it to the body. I create my own url (similar to the one made by the silentRefresh() method), and assign it to the src of the iframe. Again on the server side everything is fine and it tries to return the tokens to http://localhost:8000.
public startRenew(url: string) {
this._sessionIframe.src = url;
return new Promise((resolve) => {
this._sessionIframe.onload = () => {
resolve();
}
});
}
Question 2: How to proceed to retrieve the tokens? as it doesn't update my tokens automatically, and I don't see how the code above can do it.
Third: In App Browser
I thought this would work fine as I already know how to process the request using the In App Browser. So I tried to use the same url that worked for the iframe in the 2nd part. However this returns an error: prompt=none was requested. But user is not authenticated. on the server side, which tells that the server can't find the session so it doesn't know who is requesting the token renewal.
Question 3: Is there a specific reason this won't work other than I made a mistake?
NOTE: Took longer than expected to write this will edit this in a bit.
oAuthService
So I looked in to the implementation of the silent refresh, to see what it does. It creates an iframe with a default id, unless you override it. That cleared up a lot of confusion as to what was actually happening.
The next mistake I did was placing the silent-refresh.html file in the src/ folder, that makes it inaccessible to the iframe. Instead the file should have been placed in the www/ folder.
Then inside the iframe I kept getting the net::ERR_CONNECTION_REFUSED error. This was due to CORS and is solved by editing the Client int the Config.cs file on the Authentication Server:
AllowedCorsOrigins = { "http://localhost:8100", "http://<ip>:8100/", "http://<ip>:8100/silent-refresh.html" },
WARNING: This didn't work once I wanted to take this outside serving in the browser (ionic serve) or emulating on my device with ionic cordova run android -c-l-s, these made the root url return something like http://<ip>/. But once ran with ionic cordova run android (without the flags), window.location.href would return file:///<example>/<something>/index.html, using this sort of path (file:///<example>/<something>/silent-refresh.html) as a redirect url caused an ERR_UNSAFE_REDIRECT error to display in the iframe.
Perhaps silentRefresh() is an Angular only solution?
In App Browser
The mistake that caused the original error was having clearsessioncache and clearcache set to yes when creating the browser, caused the session to be wiped so the authentication server didn't know who it was duh, now reduced to this:
const browser = window.cordova.InAppBrowser.open(oauthUrl, '_blank',
'location=no, hidden=yes'
);
Regular redirect url of http://localhost:8100 could be used to catch the request with the new tokens. And the silent-refresh.html page is not needed.
Here is the code for creating the oauthUrl:
buildOAuthRefreshUrl(nonce): string {
return this.oauthService.issuer + '/connect/authorize?' +
'response_type=id_token%20token' +
'&client_id=' + this.oauthService.clientId +
'&state=' + nonce +
'&redirect_uri=' + encodeURIComponent(this.oauthService.redirectUri) +
'&scope=' + encodeURI(this.oauthService.scope) +
'&nonce=' + nonce +
'&prompt=none';
}
The rest of the code is pretty much identical to the originally mentioned article

How to force client reload after deployment?

I'm using the MEAN stack (mongo, express, angular and node). I'm deploying relatively frequently to production...every couple of days. My concern is that I'm changing the client side code and the API at times and I would rather not have to ensure backwards compatibility of the API with previous versions of the client code.
In such a scenario, what is the most effective way of ensuring that all clients reload when I push to production? I have seen that Evernote for example has a pop-up that says something along the lines of please reload your browser for the latest version of Evernote. I would like to do something similiar...do I need to go down the path of socket.io or sock.js or am I missing something simple and there is a simpler way to achieve this?
Update:
AppCache was deprecated summer 2015 so the below is no longer the best solution. The new recommendation is to use Service Workers instead. However, Service Workers are currently still experimental with sketchy (read: probably no) support in IE and Safari.
Alternatively, many build tools now seamlessly incorporate cache-busting and file "versioning" techniques to address OPs question. WebPack is arguably the current leader in this space.
This might be a good use case for using HTML5's AppCache
You'd probably want to automate some of these steps into your deployment scripts, but here is some code you might find useful to get you started.
First, create your appcache manifest file. This will also allow you to cache resources in the client's browser until you explicitly modify the appcache manifest file's date.
/app.appcache:
CACHE MANIFEST
#v20150327.114142
CACHE:
/appcache.js
/an/image.jpg
/a/javascript/file.js
http://some.resource.com/a/css/file.css
NETWORK:
*
/
In app.appcache, the comment on line #v20150327.114142 is how we indicate to the browser that the manifest has changed and resources should be reloaded. It can be anything, really, as long as the file will look different to the browser from the previous version. During deployment of new code in your application, this line should be modified. Could also use a build ID instead.
Second, on any pages you want to use the appcache, modify the header tag as such:
<html manifest="/app.appcache"></html>
Finally, you'll need to add some Javascript to check the appcache for any changes, and if there are, do something about it. Here's an Angular module. For this answer, here's a vanilla example:
appcache.js:
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
// Swap it in and reload the page to get the latest hotness.
window.applicationCache.swapCache();
if (confirm('A new version of the application is available. Would you like to load it?')) {
window.location.reload();
}
}
else {
// Manifest didn't changed. Don't do anything.
}
}, false);
Alternatively, if AppCache won't work for your situation, a more ghetto solution would be to create a simple API endpoint that returns the current build ID or last deployment date-time. Your Angular application occasionally hits this endpoint and compares the result to it's internal version, and if different, reloads itself.
Or, you may consider a live-reload script (example), but, while very helpful in development, I'm not sure how good of an idea it is to use live/in-place-reloading of assets in production.
I will tell you my problem first then I will recommend a tentative solution. I wanted to force my user to log out and then log in when a production build is been deployed. At any point in time, there will be two versions of software deployed on production. A version which software which FE knows and a version which Backend knows. Most of the time they would be the same. At any point in time if they go out of sync then we need to reload the client to let the client know that a new production build has been pushed.
I am assuming 99.99% of the time the backend would have the knowledge of the latest version of the deployed software on production.
following are the two approaches which I would love to recommend:-
The backend API should always return the latest version of the software in the response header. On the frontend, we should have a common piece of code that would check if the versions returned by the API and that present on the FE are the same. if not then reload.
Whenever a user logs in. the BE should encode the latest software version in the JWT. And the FE should keep sending this as a bearer token along with every API request. The BE should also write a common interceptor for every API request. which would compare the software version in the JWT received from the API request and the
Maybe you can add hash to your client code file name. eg app-abcd23.js.
So the browser will reload the file instead of get it from cache. or you can just add the hash to url.eg app.js?hash=abcd23 but some browser may still use the cached version.
i know rails has assets-pipline to handle it, but i am not familiar with MEAN stack. there should be some package in npm for that purpose.
And i dont think it is really necessary to use socket.io if you want to notify the user their client code is out of date. you can define your version in both html meta tag and js file,if mismatch, show a popup and tell the user to refresh.
Try to limit your js/files to expire within smaller periodic time, ie: 1 days.
But in case you want something that pop-out and tell your user to reload (ctrl+f5) their browser, then simply make a script that popup that news if you just changed some of your files, mark the ip/session who have just reload/told to reload, so they will not be annoyed with multiple popup.
I was facing the same problem recently. I fixed this by appending my app's build number with my js/css files. All my script and style tags were included by a script in a common include files so it was trivial to add a 'build number' at the end of the js/css file path like this
/foo/bar/main.js?123
This 123 is a number that I keep track of in my same header file. I increment it whenever I want the client to force download all the js files of the app. This gives me control over when new versions are downloaded but still allows the browser to leverage cache for every request after the first one. That is until I push another update by increment the build number.
This also means I can have a cache expiry header of however long I want.
Set a unique key to local storage during the build process
I am using react static and loading up my own data file, in there i set the ID each time my content changes
Then the frontend client reads the key with from local storage
(if the key does not exist it must be the first visit of the browser)
if the key from local storage does not match it means the content has changed
fire line below to force reload
window.replace(window.location.href + '?' + key)
in my case i had to run this same line again a second latter
like
setTimeout( (window.replace(window.location.href + '?' + key))=> {} , 1000)
full code below:
const reloadIfFilesChanged = (cnt: number = 0, manifest: IManifest) => {
try {
// will fail if window does not exist
if (cnt > 10) {
return;
}
const id = localStorage.getItem('id');
if (!id) {
localStorage.setItem('id', manifest.id);
} else {
if (id !== manifest.id) {
// manifest has changed fire reload
// and set new id
localStorage.setItem('id', manifest.id);
location.replace(window.location.href + '?' + manifest.id);
setTimeout(() => {
location.replace(window.location.href + '?' + manifest.id + '1');
}, 1000);
}
}
} catch (e) {
// tslint:disable-next-line:no-parameter-reassignment
cnt++;
setTimeout(() => reloadIfFilesChanged(cnt, manifest), 1000);
}
};

Opening a new window that needs to make ajax calls to another domain

Background
We have two web applications hosted on different sub-domains. Application 1 is an internal admin system. Application 2 is a helpdesk system.
We can modify the source code of Application 1 but we have no access to modify Application 2.
The Goal
To display a link against an order in Application 1 that will open a new window, the URL of which is that of a ticket in Application 2.
The idea being that our staff can see that an order has a helpdesk ticket raised against it and simply needs to click a link on the order to view the ticket and reply to it.
The problem
Regardless of how I open the new window (window.open, target="_blank", etc.) the ticket in the new window is unable to make any ajax requests back to the helpdesk system where it is hosted.
The URL of the new window is part of Application 2.
In Google dev tools it tells me "The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "https". Protocols must match." even when I open it using _blank.
If I go to the exact same URL manually everything works... but this doesn't help when I need it to work from the link.
Is there any way to achieve the above?
If not, is there any way I can open a new window that is "detached" from the window that opened it so that same origin policy no longer applies?
Edit 2014-03-28 10:23
I have no access to App2's code at all. I cannot make any changes to App2. Any answer must take this into account.
I am trying to open a new window from my application (App1) where the target URL of that window is a page in App2. That page inside App2 then needs to be able to use ajax to communicate with other areas of App2. This is where the problem lies. Because App1 opened the window the same origin policy is preventing that window from making it's ajax requests.
I suspect that JavaScript on the second (helpdesk) app is trying to access the first app via window.opener (which could lead to the cross-origin error you're seeing) and subsequent JavaScript (fetching stuff via AJAX) is then not getting executed. You can probably narrow things down by setting appropriate breakpoints in the second app.
If this is the cause and you can't modify the source for the helpdesk app, how about going to a URL in the internal domain that would then redirect to the help desk? The redirect should cause the window.opener property to become null (same as manually typing in the URL).
Assuming https://admin.mydomain.co.uk and http://helpdesk.mydomain.co.uk, clicking on the "Help Ticket" link would go to a URL in the internal app, e.g. https://admin.mydomain.co.uk/getHelp?ticketId, which would respond with a 301 response and an appropriate Location: http://helpdesk.domain.uk/help/ticketId header taking the user to the actual helpdesk URL.
You could use a proxy server or iframe proxying.
Use the following url //app2.mydomain.co.uk without the http or https.
It's not only a cross domain problem but a protocol issue :
You can't embed https into http page without this warning.
Consider using iframe inside your App1 :
<iframe src="https://app2.mydomain.co.uk" ></iframe>
Or maybe you can use CORS to access data between your two domains ( but i think it's not the point, you want the whole App2 page, isn't it ? )
Edit : By re-reading your question, i'm pretty sure of two thing :
You're not looking at the right direction. You say App2 don't use SSL, and that obviously false when Chrome say "Protocols must match"
It's not a "attach" or "detached" problem. If you put a link (blank or not) in a page, it can be load the new page without any problem, nor link with the referal page.
So my guess is : Your are calling App2 without SSL ( no https), BUT inside the App2, there is some https involved ( certainly some ajax query). So here is the problem : When you open the page without https, it's seem to load, but when the first https Ajax fires, it fail.
Try using https when calling your App2 url, and give us the result
My solution is this: in Application 1 you create a method your method that calling Application 2 on the server side, then you can use AJAX calling your method which will return result of Application 2.

jQuery ajax on internal network (phonegap android)

I am working on a phonegap application which runs simple ajax requests to a rendered website, pulls down the html and reskins content locally.
A requirement is that websites on an internal network can be requested. I have connections via global ip addresses working fine, but cannot get the ajax to work on internal networks.
For example connecting to a 10.0.0.xx address will always fail. The device is connected to the same network and I can access the websites on the internal network fine in browser.
Is there any way around this?
Make sure you modified your domain whitelist file correctly to allow external access.
For more information about domain whitelist, check the online doc: http://docs.phonegap.com/en/2.1.0/guide_whitelist_index.md.html#Domain%20Whitelist%20Guide
Hope this helps. Let me know if this works for after modifying your domain whitelist file.
Example of domain whitelist configuration for iOS:
Open your file Cordova.plist and check that you made the following:
1 - Set OpenAllWhitelistURLsInWebView to YES
2 - Right click on ExternalHosts -> Add Row
3 - Set the String value of your new added row to *, so you should have your new line look like this:
Item0 String *
Normally, you would replace * with the external URL you want provide access to. But, to make sure that the problem really comes from the whitelist, we will use *.

Categories

Resources