I have a React PWA application on Aws Amplify, everytime there are repository updates, user is constrain to make this actions: 1. open chrome console, 2. Go to Application Tab, 3. Go to Storage 4. Clear Site Data 5. Refresh Application.
I noticed that every update main.js go in 404.
Is there a way for clear cache after new changes?
public/serviceWorker.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('serviceWorker.js').then(function (registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function (err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
Partially solved.
Cache issue is solved, alert doesn't appear.
Removed js code from index.html
Moved serviceWorker from public to src
Added follow code to App.js;
import * as serviceWorker from "./serviceWorker";
serviceWorker.register({
onUpdate: registration => {
alert('New version available!');
if (registration && registration.waiting) {
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
}
window.location.reload();
}
});
Related
I am using a brand new app generated by create-react-app 3.4.1. It uses the default service worker file:
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
type Config = {
onSuccess?: (registration: ServiceWorkerRegistration) => void;
onUpdate?: (registration: ServiceWorkerRegistration) => void;
};
export function register(config?: Config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(
process.env.PUBLIC_URL,
window.location.href
);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker.'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl: string, config?: Config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl: string, config?: Config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
.then(registration => {
registration.unregister();
})
.catch(error => {
console.error(error.message);
});
}
}
I turned on service worker by changing the code in index.ts to
serviceWorker.register();
I hosted the static files generated by yarn build through https by an Express.js server with strict Content Security Policy (CSP) turned on by helmet.
helmet({
contentSecurityPolicy: {
directives: {
scriptSrc: [
/* Content Security Policy Level 3 */
"'strict-dynamic'",
`'nonce-${cspNonce}'`,
/* Content Security Policy Level 2 (backward compatible) */
"'self'",
// Workbox
'https://storage.googleapis.com',
// ...
],
styleSrc: [
"'self'",
],
// ...
},
},
})
When I first time opening the page, the browser fetch files from server. Both JS and CSS have CSP headers. The page shows well.
When I second time opening the page, the files are loaded from service worker. Many got blocked by CSP, as my console shows:
When I further check, CSS files served by service worker still have CSP headers (and nonce inside also changed to new value, create-react-app did it for us?), which load well.
However, the CSP headers on JS files are missing, which got blocked.
Any guide will be helpful. Thanks!
UPDATE
One thing I notice in Chrome, it shows
CAUTION: provisional headers are shown
and I found more info at
"CAUTION: provisional headers are shown" in Chrome debugger
Another thing I found, the page won't load on second call on Chrome and Safari after service worker (create-react-app uses Workbox internally) registered.
For Firefox, although CSP headers are not shown neither in JS and CSS files when read from cache, Firefox still can show the page.
It is likely that the first time you load the page the nonce in your CSP and your script tags are in sync. On the second load they are no longer present or in sync in your script tags. Check the difference in nonce values in the CSP header and inline script tags.
CSP applies to pages being rendered in the browser (content-type: "text/html"), it doesn't have any effect when set on the other resources loaded. Missing CSP header on js files doesn't have any effect. Your CSS files are included because you include "style-src 'self'", you should add this to script-src as well. If it is not sufficient you could add localhost:5000 in development.
As noticed Halvor Sakshaug above, you do not need to serve JS/CSS with CSP headers, CSP work only for page/code having document property.
As seen from you Chrome console warnings, there is at least 2 issues:
an inline scripts blocked (you do use < script>...< /script> or < tag unClick='...'> somewhere). So you have to add 'unsafe-inline' to script-src (or add nonce='server_generated_value' attribute to < script>...< /script>), BUT:
'strict-dynamic' cancels host-based allowlisting (incliding 'self') in CSP3-browsers, so your https://localhost (and other hosts) will be disabled. Also 'strict-dynamic' cancels 'unsafe-inline' ('nonce-value' and 'hash-value' cancel it too). Probably you do not sign inline scripts with nonce='server_generated_nonce' attribute. Or you do use scripts calls incompatible with 'strict-dynamic' (parser-inserted scripts, inline event handlers etc)
You have to revise Content Security Policy rules, they are inconsistent.
I have a React + Electron app using Google API to authenticate and get a list of calendar events.
The API script is being loaded on the head of my index.html and initialised on my App.js like so:
// Initializes the API client library and sets up sign-in state listeners.
initClient() {
let gapi = window["gapi"];
let that = this;
gapi.load("client", start);
function start() {
gapi.client
.init({
apiKey: GOOGLE_API_KEY,
clientId: CLIENT_ID,
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"
],
scope: "https://www.googleapis.com/auth/calendar.readonly"
})
.then(() => {
gapi.auth2
.getAuthInstance()
.isSignedIn.listen(that.updateSigninStatus);
that.updateSigninStatus(
gapi.auth2.getAuthInstance().isSignedIn.get()
);
that.setState({
apiLoaded: true
});
});
}
}
It works completely fine on a local environment, where I have a server running, but once I build my Electron app and run the app "natively", I get the following error: gapi.auth2.ExternallyVisibleError: Invalid cookiePolicy
I don't have an advanced understanding of APIs and Servers to figure this out but through research, I found something about the API not working from a "file://" protocol, which is the case on an Electron app.
Thoughts? Ideas?
I am installing a notification system called Subscribers with a project built with create react app. They ask the developer to download their service worker and include it in the root directory of the project. I have never had to install / use a service worker before, which is most likely the root of my misunderstanding.
How do you add a service worker into the root directory of a React project? The instructions say the service worker should appear in the root directory as https://yoursite.com/firebase-messaging-sw.js. In an attempt to register that URL, I included a service worker under src/index.js:
import Environment from './Environment'
export default function LocalServiceWorkerRegister() {
const swPath = `${Environment.getSelfDomain()}/firebase-messaging-sw.js`;
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register(swPath).then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
}
In production, I receive a 404 error. I have tried placing the firebase-messaging-sw.js file in the root directory, and under the src folder. Same error each time.
Here are the instructions from Subscribers:
https://subscribers.freshdesk.com/support/solutions/articles/35000013054-diy-installation-instructions
The public folder is the provided 'escape hatch' for adding assets from outside of the module system.
From the docs:
If you put a file into the public folder, it will not be processed by Webpack. Instead it will be copied into the build folder untouched. To reference assets in the public folder, you need to use a special variable called PUBLIC_URL
So, once copied to the public folder, you would reference the file like this:
import Environment from './Environment'
export default function LocalServiceWorkerRegister() {
const swPath = `${process.env.PUBLIC_URL}/firebase-messaging-sw.js`;
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register(swPath).then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
}
I'm implementing an AngularJS app that will use IdentityServer4 for Authorization.
I have a stand alone Angular app within a .Net Core 2.0 app that calls the api controller in the .net core app. if I browse to http://localhost:5050/.well-known/openid-configuration I am getting the json returned.
I have used this example as a basis for my auth service:
function authService() {
var config = {
authority: "http://localhost:5050",
client_id: "js",
redirect_uri: "http://localhost:5050/LocalizationAdmin/callback.html",
response_type: "id_token token",
scope: "openid profile api1",
post_logout_redirect_uri: "http://localhost:5050/LocalizationAdmin/index.html"
};
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
} else {
log("User not logged in");
}
});
var service = {
login: login,
logout: logout,
};
return service;
function login() {
mgr.signinRedirect();
}
In callback.html I have added:
<body>
<script src="scripts/oidc-client.js"></script>
<script>
new Oidc.UserManager().signinRedirectCallback().then(function () {
window.location = "index.html";
}).catch(function (e) {
console.error(e);
});
</script>
</body>
It is trying to redirect to:
http://localhost:5050/account/login?returnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A5050%252FLocalizationAdmin%252Fcallback.html%26response_type%3Did_token%2520token%26scope%3Dopenid%2520profile%2520api1%26state%3Dd526351a26f74202badb7685022a6549%26nonce%3D6c858921378645ca8fcad973eb26cc72
However I just want it to redirect to the IdentityServer4 login screen. How can I achieve this? Any help appreciated.
Edit:
I have added the UI templates from here:
https://github.com/IdentityServer/IdentityServer4.Quickstart.UI
But I am getting a number of errors, could this be because I am using .Net Core 2.0 version: assemblyref://IdentityServer4 (2.0.0-rc1-update1)
Error CS0104 'AuthenticationProperties' is an ambiguous reference between 'Microsoft.AspNetCore.Authentication.AuthenticationProperties' and 'Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties'
Demo Project showing issue added to github here.
I have no idea how stable this is but I just used the powershell command pointing to the dev branch and it seems to be working.
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/dev/get.ps1'))
you might want to look instead at the quickstarts relating to 2.0.0-rc1-update1 which can be found here on the dev branch:
https://github.com/IdentityServer/IdentityServer4/tree/dev/docs/quickstarts
as they have been also updated.
I'm creating an app using ionic and angular.js and I'm having difficulties registering a service worker which I'm intending to use to add the new app install banner feature. I'm adding the below code on my app.js file as instructed, but I'm note getting any signals of the registration happening nor any error.
This is the code I'm adding to my app.js:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js').then(function(registration) {
//Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
//registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}
Make sure you either load the page at localhost or you have to use https
You’ll also need to serve your code via HTTPS — Service Workers are restricted to running across HTTPS for security reasons. GitHub is therefore a good place to host experiments, as it supports HTTPS.
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API/Using_Service_Workers
Check if your Browser supports this feature.
if ('serviceWorker' in navigator) {
[...]
} else {
console.log("this browser does NOT support service worker");
}
This might help: http://caniuse.com/#feat=serviceworkers
If you want to see the current state of your serviceworker you could do something like this:
navigator.serviceWorker.register('/serviceworker.js', {scope: '/'})
.then(function (registration) {
var serviceWorker;
if (registration.installing) {
serviceWorker = registration.installing;
} else if (registration.waiting) {
serviceWorker = registration.waiting;
} else if (registration.active) {
serviceWorker = registration.active;
}
if (serviceWorker) {
console.log("ServiceWorker phase:", serviceWorker.state);
serviceWorker.addEventListener('statechange', function (e) {
console.log("ServiceWorker phase:", e.target.state);
});
}
}).catch(function (err) {
console.log('ServiceWorker registration failed: ', err);
});
If you're not seeing anything logged, then the most likely cause is that you're running in a browser that doesn't support service workers. In other words, the if ('serviceWorker' in navigator) check fails. You can confirm this by adding in a logging statement in an else clause associated with that if.
Which browser are you testing with? Service workers are coming to more browsers in the future, but as of right now, they're only enabled by default in the current versions of Chrome on desktop and Android platforms.