UNPKG

quodolores

Version:

Monorepo for the Firebase JavaScript SDK

185 lines (166 loc) 5.54 kB
/** * @license * Copyright 2019 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. */ import { SubscriptionOptions, TokenDetails } from '../interfaces/token-details'; import { arrayToBase64, base64ToArray } from '../helpers/array-base64-translator'; import { dbGet, dbRemove, dbSet } from './idb-manager'; import { requestDeleteToken, requestGetToken, requestUpdateToken } from './requests'; import { FirebaseInternalDependencies } from '../interfaces/internal-dependencies'; import { MessagingService } from '../messaging-service'; // UpdateRegistration will be called once every week. const TOKEN_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days export async function getTokenInternal( messaging: MessagingService ): Promise<string> { const pushSubscription = await getPushSubscription( messaging.swRegistration!, messaging.vapidKey! ); const subscriptionOptions: SubscriptionOptions = { vapidKey: messaging.vapidKey!, swScope: messaging.swRegistration!.scope, endpoint: pushSubscription.endpoint, auth: arrayToBase64(pushSubscription.getKey('auth')!), p256dh: arrayToBase64(pushSubscription.getKey('p256dh')!) }; const tokenDetails = await dbGet(messaging.firebaseDependencies); if (!tokenDetails) { // No token, get a new one. return getNewToken(messaging.firebaseDependencies, subscriptionOptions); } else if ( !isTokenValid(tokenDetails.subscriptionOptions!, subscriptionOptions) ) { // Invalid token, get a new one. try { await requestDeleteToken( messaging.firebaseDependencies!, tokenDetails.token ); } catch (e) { // Suppress errors because of #2364 console.warn(e); } return getNewToken(messaging.firebaseDependencies!, subscriptionOptions); } else if (Date.now() >= tokenDetails.createTime + TOKEN_EXPIRATION_MS) { // Weekly token refresh return updateToken(messaging, { token: tokenDetails.token, createTime: Date.now(), subscriptionOptions }); } else { // Valid token, nothing to do. return tokenDetails.token; } } /** * This method deletes the token from the database, unsubscribes the token from FCM, and unregisters * the push subscription if it exists. */ export async function deleteTokenInternal( messaging: MessagingService ): Promise<boolean> { const tokenDetails = await dbGet(messaging.firebaseDependencies); if (tokenDetails) { await requestDeleteToken( messaging.firebaseDependencies, tokenDetails.token ); await dbRemove(messaging.firebaseDependencies); } // Unsubscribe from the push subscription. const pushSubscription = await messaging.swRegistration!.pushManager.getSubscription(); if (pushSubscription) { return pushSubscription.unsubscribe(); } // If there's no SW, consider it a success. return true; } async function updateToken( messaging: MessagingService, tokenDetails: TokenDetails ): Promise<string> { try { const updatedToken = await requestUpdateToken( messaging.firebaseDependencies, tokenDetails ); const updatedTokenDetails: TokenDetails = { ...tokenDetails, token: updatedToken, createTime: Date.now() }; await dbSet(messaging.firebaseDependencies, updatedTokenDetails); return updatedToken; } catch (e) { await deleteTokenInternal(messaging); throw e; } } async function getNewToken( firebaseDependencies: FirebaseInternalDependencies, subscriptionOptions: SubscriptionOptions ): Promise<string> { const token = await requestGetToken( firebaseDependencies, subscriptionOptions ); const tokenDetails: TokenDetails = { token, createTime: Date.now(), subscriptionOptions }; await dbSet(firebaseDependencies, tokenDetails); return tokenDetails.token; } /** * Gets a PushSubscription for the current user. */ async function getPushSubscription( swRegistration: ServiceWorkerRegistration, vapidKey: string ): Promise<PushSubscription> { const subscription = await swRegistration.pushManager.getSubscription(); if (subscription) { return subscription; } return swRegistration.pushManager.subscribe({ userVisibleOnly: true, // Chrome <= 75 doesn't support base64-encoded VAPID key. For backward compatibility, VAPID key // submitted to pushManager#subscribe must be of type Uint8Array. applicationServerKey: base64ToArray(vapidKey) }); } /** * Checks if the saved tokenDetails object matches the configuration provided. */ function isTokenValid( dbOptions: SubscriptionOptions, currentOptions: SubscriptionOptions ): boolean { const isVapidKeyEqual = currentOptions.vapidKey === dbOptions.vapidKey; const isEndpointEqual = currentOptions.endpoint === dbOptions.endpoint; const isAuthEqual = currentOptions.auth === dbOptions.auth; const isP256dhEqual = currentOptions.p256dh === dbOptions.p256dh; return isVapidKeyEqual && isEndpointEqual && isAuthEqual && isP256dhEqual; }