UNPKG

voluptasmollitia

Version:
140 lines (120 loc) 3.85 kB
/** * @license * Copyright 2020 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 { FirebaseApp } from '@firebase/app-types'; import { getState, setState } from './state'; import { Deferred } from '@firebase/util'; import { getRecaptcha, ensureActivated } from './util'; export const RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js'; export function initialize( app: FirebaseApp, siteKey: string ): Promise<GreCAPTCHA> { const state = getState(app); const initialized = new Deferred<GreCAPTCHA>(); setState(app, { ...state, reCAPTCHAState: { initialized } }); const divId = `fire_app_check_${app.name}`; const invisibleDiv = document.createElement('div'); invisibleDiv.id = divId; invisibleDiv.style.display = 'none'; document.body.appendChild(invisibleDiv); const grecaptcha = getRecaptcha(); if (!grecaptcha) { loadReCAPTCHAScript(() => { const grecaptcha = getRecaptcha(); if (!grecaptcha) { // it shouldn't happen. throw new Error('no recaptcha'); } grecaptcha.ready(() => { // Invisible widgets allow us to set a different siteKey for each widget, so we use them to support multiple apps renderInvisibleWidget(app, siteKey, grecaptcha, divId); initialized.resolve(grecaptcha); }); }); } else { grecaptcha.ready(() => { renderInvisibleWidget(app, siteKey, grecaptcha, divId); initialized.resolve(grecaptcha); }); } return initialized.promise; } export async function getToken(app: FirebaseApp): Promise<string> { ensureActivated(app); // ensureActivated() guarantees that reCAPTCHAState is set const reCAPTCHAState = getState(app).reCAPTCHAState!; const recaptcha = await reCAPTCHAState.initialized.promise; return new Promise((resolve, _reject) => { // Updated after initialization is complete. const reCAPTCHAState = getState(app).reCAPTCHAState!; recaptcha.ready(() => { resolve( // widgetId is guaranteed to be available if reCAPTCHAState.initialized.promise resolved. recaptcha.execute(reCAPTCHAState.widgetId!, { action: 'fire_app_check' }) ); }); }); } /** * * @param app * @param container - Id of a HTML element. */ function renderInvisibleWidget( app: FirebaseApp, siteKey: string, grecaptcha: GreCAPTCHA, container: string ): void { const widgetId = grecaptcha.render(container, { sitekey: siteKey, size: 'invisible' }); const state = getState(app); setState(app, { ...state, reCAPTCHAState: { ...state.reCAPTCHAState!, // state.reCAPTCHAState is set in the initialize() widgetId } }); } function loadReCAPTCHAScript(onload: () => void): void { const script = document.createElement('script'); script.src = `${RECAPTCHA_URL}`; script.onload = onload; document.head.appendChild(script); } declare global { interface Window { grecaptcha: GreCAPTCHA | undefined; } } export interface GreCAPTCHA { ready: (callback: () => void) => void; execute: (siteKey: string, options: { action: string }) => Promise<string>; render: ( container: string | HTMLElement, parameters: GreCAPTCHARenderOption ) => string; } export interface GreCAPTCHARenderOption { sitekey: string; size: 'invisible'; }