voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
117 lines (107 loc) • 3.92 kB
text/typescript
/**
* @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 { SDK_VERSION } from '@firebase/app-exp';
import { querystring } from '@firebase/util';
import { DefaultConfig } from '../../../internal';
import { AuthErrorCode } from '../../core/errors';
import { _assert, _createError } from '../../core/util/assert';
import { Delay } from '../../core/util/delay';
import { _emulatorUrl } from '../../core/util/emulator';
import { AuthInternal } from '../../model/auth';
import { _window } from '../auth_window';
import * as gapiLoader from './gapi';
const PING_TIMEOUT = new Delay(5000, 15000);
const IFRAME_PATH = '__/auth/iframe';
const EMULATED_IFRAME_PATH = 'emulator/auth/iframe';
const IFRAME_ATTRIBUTES = {
style: {
position: 'absolute',
top: '-100px',
width: '1px',
height: '1px'
}
};
// Map from apiHost to endpoint ID for passing into iframe. In current SDK, apiHost can be set to
// anything (not from a list of endpoints with IDs as in legacy), so this is the closest we can get.
const EID_FROM_APIHOST = new Map([
[DefaultConfig.API_HOST, 'p'], // production
['staging-identitytoolkit.sandbox.googleapis.com', 's'], // staging
['test-identitytoolkit.sandbox.googleapis.com', 't'] // test
]);
function getIframeUrl(auth: AuthInternal): string {
const config = auth.config;
_assert(config.authDomain, auth, AuthErrorCode.MISSING_AUTH_DOMAIN);
const url = config.emulator
? _emulatorUrl(config, EMULATED_IFRAME_PATH)
: `https://${auth.config.authDomain}/${IFRAME_PATH}`;
const params: Record<string, string> = {
apiKey: config.apiKey,
appName: auth.name,
v: SDK_VERSION
};
const eid = EID_FROM_APIHOST.get(auth.config.apiHost);
if (eid) {
params.eid = eid;
}
const frameworks = auth._getFrameworks();
if (frameworks.length) {
params.fw = frameworks.join(',');
}
return `${url}?${querystring(params).slice(1)}`;
}
export async function _openIframe(
auth: AuthInternal
): Promise<gapi.iframes.Iframe> {
const context = await gapiLoader._loadGapi(auth);
const gapi = _window().gapi;
_assert(gapi, auth, AuthErrorCode.INTERNAL_ERROR);
return context.open(
{
where: document.body,
url: getIframeUrl(auth),
messageHandlersFilter: gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER,
attributes: IFRAME_ATTRIBUTES,
dontclear: true
},
(iframe: gapi.iframes.Iframe) =>
new Promise(async (resolve, reject) => {
await iframe.restyle({
// Prevent iframe from closing on mouse out.
setHideOnLeave: false
});
const networkError = _createError(
auth,
AuthErrorCode.NETWORK_REQUEST_FAILED
);
// Confirm iframe is correctly loaded.
// To fallback on failure, set a timeout.
const networkErrorTimer = _window().setTimeout(() => {
reject(networkError);
}, PING_TIMEOUT.get());
// Clear timer and resolve pending iframe ready promise.
function clearTimerAndResolve(): void {
_window().clearTimeout(networkErrorTimer);
resolve(iframe);
}
// This returns an IThenable. However the reject part does not call
// when the iframe is not loaded.
iframe.ping(clearTimerAndResolve).then(clearTimerAndResolve, () => {
reject(networkError);
});
})
);
}