I've a MPA(Multiple page application). published for Android and iOS. It simply changes the page when user want to navigate to other page(view). All things are working fine. I want to implement some backend sync features. Problem is, I make Ajax request silently in background and user can change page anytime so app can lose reference of Ajax call which is highly important for keeping track of synced data.
Is there any plugin that can make http request on native code level or some other work around.
Have a look at cordova-plugin-http, it is a native plugin that executes all HTTP requests on a background thread.
Installation:
cordova plugin add cordova-plugin-http
Example POST request:
cordovaHTTP.post("https://google.com/", {
id: 12,
message: "test"
}, { Authorization: "OAuth2: token" }, function(response) {
// prints 200
console.log(response.status);
try {
response.data = JSON.parse(response.data);
// prints test
console.log(response.data.message);
} catch(e) {
console.error("JSON parsing error");
}
}, function(response) {
// prints 403
console.log(response.status);
//prints Permission denied
console.log(response.error);
});
There is no OOB way to do this. You can use something like the Cordova HTTP plugin to move HTTP requests to the native side, which will continue to execute across multiple pages, but it won't know what to do with the response once the response comes back if the user navigated to another page.
If your processing really is all background and doesn't truly need any JavaScript post-processing, you could try to look into something like the cordova-plugin-background-download - that basically executes a GET request in the background and saves the result where you tell it. It only supports GET, but it can work even if your whole app is put into the background.
If you need post-processing or non-GET requests, you can consider implementing your logic in native code in a plugin (perhaps using one of the HTTP plugins for Cordova to help with the actual network marshalling).
It'd be awesome if Cordova could support something like service workers, and I've been looking into that here and there. There's an old implementation for iOS but it doesn't seem to work anymore (and may not really be workable without extensive changes): cordova-plugin-serviceworker.
One other option would be to make your app a pseudo-SPA with some iframes. Have an iframe doing your requests and processing, and create interaction between the content iframe as needed. But that isn't trivial either.
Related
I'm wondering what the standard solution is to ajax failing due to (OIDC) SSO redirect/refresh. It's insidious because the failure is silent, the web page appears to be working properly.
I have a simple web page with static HTML and simple javascript that populates the page via ajax and refreshes it periodically.
The webpage and JS are both access-controlled via the same OIDC SSO. This works, but can fail in the following ways when the ajax call is rejected 401 due to needing an authentication refresh. (This is not full password authentication, this is just "check that my token is ok, and see that it is, and keep going as if nothing had happened".)
Back end and front end are both served from the same server by a basic Apache service with the same Access Control and Authorization requirements.
If a user navigates to the page in such a way that a cached version of the HTML is loaded and just the ajax runs. (e.g. back button)
If the page is left sitting for long enough, I believe it refreshes will also fail for the same reason.
I have worked around the issue as shown below, but it feels like a hack, like there must be some much more standard way to do this.
// This function is called every 30s on a timer
function updateData(args, callback) {
$.ajax({
xhrFields: { withCredentials: true },
url: "/getit?" + args,
success: function(data) {
localStorage.removeItem('pagereloaded')
callback(data);
},
statusCode: {
// Reload is because SSO tokens can timeout causing ajax to fail.
// In these cases we want to reload the page right away.
// But what if it's just a genuine auth failure, we do not want to go into an infinite reload loop.
// So pagereloaded and timer try to reload quickly a first time, but then avoid a loop after that.
401: function () {
if (localStorage.getItem('pagereloaded') == null || (Date.now() - start_time) > 60000) {
localStorage.setItem("pagereloaded", 1)
location.reload();
}
}
}
});
}
WEB AND API MODULES
Sounds like the Apache module you are using is intended only for website requests, and is not intended for direct API requests. The more standard way to deal with this is via separate modules that are tailored to their clients - something like this:
PATH: https://example.com/www/** --> uses an OIDC module to verify credentials during web requests
PATH: https://example.com/api/** --> uses an OAuth module to verify credentials during API requests
If you search around you will see that there are a number of Apache modules available, some of which deal with security for web requests and some of which deal with security for API requests.
BEHAVIOUR
An API module should enable its clients to distinguish missing, invalid or expired API credential errors (usually classified as 401s) from other types of error. In normal usage, 401s should only occur in applications when access or refresh tokens expire, or, in some cases, when cookie encryption or token signing keys are renewed. These error cases are not permanent and re-authenticating the user will fix them.
Other types of error should return a different status code to the client, such as 400 or 500, and the client should display an error. As an example, if a client secret is misconfigured, it is a permanent error, and re-authenticating the user will not fix the problem. Instead it would result in a redirect loop. By testing these error conditions you will be satisfied that the behaviour is correct.
UPDATED CODE
You can then write simple code, perhaps as follows. The client side code should be in full control over behaviour after a 401. Whether you reload the page or just stop making background requests is up to you to decide.
function updateData(args, callback) {
$.ajax({
url: "/api/getit?" + args,
success: function(data) {
callback(data);
},
statusCode: {
401: function () {
location.reload();
}
}
});
}
Note also that the withCredentials flag is only needed for cross origin requests, such as those https://www.example.com to https://api.example.com, so I have omitted it from the above code, since it sounds like you have a same domain setup.
I want to react to some events that are triggered by a 3rd party on my site.
Those events are Fetch/XHR requests, I'd like to be notified when a "watched" request happens (I assume I would watch them based on the "Request URL"), and I would like to read its request/response headers and payload.
I've never seen that being done before, is it even possible?
The browser is aware of all requests happening, but I'm uncertain whether we can read this, can we?
Eventually, I hope to achieve a watcher that would trigger my own code when some endpoints have been called, while re-using the data sent by those requests. I'm hoping to both detect WHEN a particular endpoint is called, and WHAT the response is, so that I can use it for my own needs (without needing to perform yet another identical request)
I'm not sure if reading the data will be possible, I found out that reacting to fetch requests is possible with the help of Resource Timing (see https://www.w3.org/TR/resource-timing/)
For example, you can create decorator-function for fetch:
const origFetch = fetch;
window.fetch = function(...args) {
console.log(args);
return origFetch.apply(this, args);
}
Also you can create decorator for Response.prototype.json function.
It's possible using the WebRequest built-in library. The following example is specific to the Chrome Extension runtime, but something similar is doable for browsers, too.
For Chrome Extensions, it needs to run as a Service Worker (e.g: background.js)*
chrome.webRequest.onBeforeSendHeaders.addListener(
(requestHeadersDetails) => {
console.log('requestHeadersDetails', requestHeadersDetails);
},
{
urls: ['https://*'],
},
[
'requestHeaders',
'extraHeaders',
],
);
See https://developer.chrome.com/docs/extensions/reference/webRequest/#event-onBeforeRequest and https://developer.chrome.com/docs/extensions/reference/webRequest/#event-onBeforeSendHeaders (both are very similar)
I didn't find a way to re-use the data that were fetched, though.
But at least I'm notified when a request is sent.
I've read quite a lot of documentation about Webpush, and so far I've understood that push subscription should have a read-only propery expirationTime. Also, I understand how should I react if the browser decides that subscription is outdated (handle event in service worker, etc.). But is it possible to somehow set expiration date manually, without implementing complex client side logic? I guess that this is an ordinary problem for apps that have authentification.
My problem is that if user gets logged out automatically, webpush endpoint stays valid. I know multiple ways this can be solved with workarounds, but I guess that's not the optimal way for a relatively basic problem.
It's been a long time ago that I've fixed this, but I guess sharing my solution can be helpful.
The solution was to make a HTTP request from service worker to the app using fetch('/path') , because all cookies from the app are also attached to requests made from SW.
So, if user is not logged in, you are redirected to login page.
My code:
fetch('/path', {method: 'GET', redirect:'error'}).then(function(result) {
... //some code specific for my app
}).catch(function(e) {
registration.unregister(); //error on redirect to login
});
I'm working on an AngularJS enhancement of a Wordpress website. I'm using the WP-API (v2) plugin to create custom endpoints and then consuming them on front end using Angular $http service.
Here's a code sample of the angular request:
function getData(slug) {
return $http.get(endpoints.specialData.replace(':slug', slug)).then(function (response) {
var data = response.data || {};
return data;
}, function (response) {
return response;
});
}
When I visit the page making this call I can see the time for this XHR (via Chrome dev tools) is about 4.5 seconds.
If I hit the same endpoint URL directly in the browser it returns the JSON result in 900ms. The endpoint URL is defined using a relative path (/path/relative/from/site/root) rather than the full http:// path, if that makes any difference.
Any ideas why it would take so much longer via Angular?
Debugging perf is quite particular, so all I can do is help you on your journey. You might want to take a look at troubleshooting the request using the Network tab in DevTools, and figure out what exactly is causing the slowdown. Read this article fully from Google: https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/timeline-tool
TL;DR If you see lot of green in your request, it's most likely a server issue, if it's blue, you're sending over too many bytes and need to optimize your JSON.
From there you can start narrowing it down.
Good luck!
I'm in a dead end, because I know that Javascript isn't made for synchronous work, and specially not AngularJS. However, I am in a situation where I am stuck needing it.
I have one main page on the domain "www" (with AngularJS), that calls "api" a lot. Some of the resources on "api" requires authentication, so it returns an 401, which in turn AngularJS picks up and displays a login-box when it sees.
The www-login supports a bunch of login methods and sends the password/oauth/whatever to the "api", which returns a token which the angular app stores in a cookie, and sets in $http.defaults.headers.common['Authorization'] so it can be used to authenticate furter requests to the api.
The same method is also used to get the username that belongs to the api-token.
This works perfectly and solves my design goals. But if the browser is going to an url that requires authentication, the 401 (and hence the login box pops-up). This is (I guess) because angular is not able to populate the Authorization field in the header before the 401 hits the browser..
Solutions?
Have an async=false request using jquery?
Close the login-box when we are done getting the data we want. The login box might flicker...
Store more meta-data about the login (ie, username) in cookies, so we dont haveto do get this information from the server when the app is loading.
??
Is there a better solution? This one time, for this one request, I want async=false in my Angular resource....
The solution I went for (which works perfectly) is to set $http.defaults.headers.common['Authorization'] = 'Token ' + old_api_token;, before the async request. And then overwrite it when the async request is done (if it have changed).