veritatisdoloremque
Version:
Monorepo for the Firebase JavaScript SDK
178 lines (167 loc) • 5.67 kB
text/typescript
/**
* @license
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Service worker for Firebase Auth test app application. The
* service worker caches all content and only serves cached content in offline
* mode.
*/
import { initializeApp } from '@firebase/app-exp';
import { getAuth, User } from '@firebase/auth-exp';
import { config } from '../config';
declare let self: ServiceWorkerGlobalScope;
const CACHE_NAME = 'cache-v1';
const urlsToCache = [
'/',
'/manifest.json',
'/config.js',
'/common.js',
'/style.css'
];
// Initialize the Firebase app in the service worker.
const app = initializeApp(config);
const auth = getAuth(app);
/**
* Returns a promise that resolves with an ID token if available.
* @return {!Promise<?string>} The promise that resolves with an ID token if
* available. Otherwise, the promise resolves with null.
*/
function getIdToken(): Promise<string | null> {
return new Promise(resolve => {
auth.onAuthStateChanged((user: User | null) => {
if (user) {
user
.getIdToken()
.then(resolve)
.catch(() => {
resolve(null);
});
} else {
resolve(null);
}
});
});
}
/**
* @param {string} url The URL whose origin is to be returned.
* @return {string} The origin corresponding to given URL.
*/
function getOriginFromUrl(url: string): string {
// https://stackoverflow.com/questions/1420881/how-to-extract-base-url-from-a-string-in-javascript
const pathArray = url.split('/');
const protocol = pathArray[0];
const host = pathArray[2];
return protocol + '//' + host;
}
self.addEventListener('install', (event: ExtendableEvent) => {
// Perform install steps.
event.waitUntil(
async (): Promise<void> => {
const cache = await caches.open(CACHE_NAME);
// Add all URLs of resources we want to cache.
try {
await cache.addAll(urlsToCache);
} catch {
// Suppress error as some of the files may not be available for the
// current page.
}
}
);
});
// As this is a test app, let's only return cached data when offline.
self.addEventListener('fetch', (event: FetchEvent) => {
event.respondWith(
(async function () {
const idToken = await getIdToken();
// Try to fetch the resource first after checking for the ID token.
let req = event.request;
// For same origin https requests, append idToken to header.
if (
self.location.origin === getOriginFromUrl(event.request.url) &&
(self.location.protocol === 'https:' ||
self.location.hostname === 'localhost') &&
idToken
) {
// Clone headers as request headers are immutable.
const headers = new Headers();
req.headers.forEach((value, key) => {
headers.append(key, value);
});
// Add ID token to header. We can't add to Authentication header as it
// will break HTTP basic authentication.
headers.append('x-id-token', idToken);
try {
req = new Request(req.url, {
method: req.method,
headers,
mode: 'same-origin',
credentials: req.credentials,
cache: req.cache,
redirect: req.redirect,
referrer: req.referrer,
body: req.body
});
} catch (e) {
// This will fail for CORS requests. We just continue with the
// fetch caching logic below and do not pass the ID token.
}
}
return fetch(req)
.then(response => {
// Check if we received a valid response.
// If not, just funnel the error response.
if (response.status !== 200 || response.type !== 'basic') {
return response;
}
// If response is valid, clone it and save it to the cache.
var responseToCache = response.clone();
// Save response to cache.
caches.open(CACHE_NAME).then(function (cache) {
cache.put(event.request, responseToCache);
});
// After caching, return response.
return response;
})
.catch(() => {
// For fetch errors, attempt to retrieve the resource from cache.
return caches.match(event.request.clone()) as Promise<Response>;
})
.catch(error => {
// If error getting resource from cache, do nothing.
console.log(error);
throw error;
});
})()
);
});
self.addEventListener('activate', (event: ExtendableEvent) => {
// Update this list with all caches that need to remain cached.
const cacheWhitelist = ['cache-v1'];
event.waitUntil(
async (): Promise<void> => {
const cacheNames = await caches.keys();
await Promise.all(
cacheNames.map(cacheName => {
// Check if cache is not whitelisted above.
if (cacheWhitelist.indexOf(cacheName) === -1) {
// If not whitelisted, delete it.
return caches.delete(cacheName);
}
})
);
}
);
});