UNPKG

@tryfinch/react-connect

Version:

The SDK for Finch's authorization flow, Finch Connect

177 lines (171 loc) 7.21 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.FinchConnect = factory()); })(this, (function () { 'use strict'; const appendBaseParams = (url) => { url.searchParams.append('app_type', 'spa'); url.searchParams.append('sdk_host_url', window.location.origin); url.searchParams.append('mode', 'employer'); url.searchParams.append('sdk_version', 'unified-5.0.1'); }; const constructAuthUrl = ({ sessionId, state, apiConfig, }) => { const BASE_FINCH_CONNECT_URI = 'https://connect.tryfinch.com'; const CONNECT_URL = (apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.connectUrl) || BASE_FINCH_CONNECT_URI; const authUrl = new URL(`${CONNECT_URL}/authorize`); authUrl.searchParams.append('session', sessionId); if (state) authUrl.searchParams.append('state', state); appendBaseParams(authUrl); return authUrl.href; }; const constructPreviewUrl = ({ clientId, products, apiConfig, }) => { const BASE_FINCH_CONNECT_URI = 'https://connect.tryfinch.com'; const CONNECT_URL = (apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.connectUrl) || BASE_FINCH_CONNECT_URI; const previewUrl = new URL(`${CONNECT_URL}/authorize`); previewUrl.searchParams.append('preview', 'true'); previewUrl.searchParams.append('client_id', clientId); previewUrl.searchParams.append('products', products.join(' ')); previewUrl.searchParams.append('redirect_uri', 'https://www.tryfinch.com'); appendBaseParams(previewUrl); return previewUrl.href; }; const POST_MESSAGE_NAME = 'finch-auth-message-v2'; const BASE_FINCH_CONNECT_URI = 'https://connect.tryfinch.com'; const FINCH_CONNECT_IFRAME_ID = 'finch-connect-iframe'; const createAndAttachIFrame = ({ src, zIndex = 999, }) => { const existingIframe = document.getElementById(FINCH_CONNECT_IFRAME_ID); if (existingIframe) { existingIframe.remove(); } const iframe = document.createElement('iframe'); iframe.src = src; iframe.frameBorder = '0'; iframe.id = FINCH_CONNECT_IFRAME_ID; iframe.style.position = 'fixed'; iframe.style.zIndex = zIndex.toString(); iframe.style.height = '100%'; iframe.style.width = '100%'; iframe.style.top = '0'; iframe.style.backgroundColor = 'none transparent'; iframe.style.border = 'none'; iframe.allow = 'clipboard-write; clipboard-read'; document.body.prepend(iframe); document.body.style.overflow = 'hidden'; return iframe; }; const removeIFrame = () => { const frameToRemove = document.getElementById(FINCH_CONNECT_IFRAME_ID); if (frameToRemove) { frameToRemove.remove(); document.body.style.overflow = 'inherit'; } }; const createMessageHandler = (callbacks, closeFn) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (event) => { var _a, _b, _c, _d; const typedEvent = event; if (!typedEvent.data) return; if (typedEvent.data.name !== POST_MESSAGE_NAME) return; const CONNECT_URL = ((_a = callbacks.apiConfig) === null || _a === void 0 ? void 0 : _a.connectUrl) || BASE_FINCH_CONNECT_URI; if (!typedEvent.origin.startsWith(CONNECT_URL)) return; if (typedEvent.data.kind !== 'error') closeFn(); switch (typedEvent.data.kind) { case 'closed': callbacks.onClose(); break; case 'error': if ((_b = typedEvent.data.error) === null || _b === void 0 ? void 0 : _b.shouldClose) closeFn(); callbacks.onError({ errorMessage: ((_c = typedEvent.data.error) === null || _c === void 0 ? void 0 : _c.message) || 'Unknown error', errorType: (_d = typedEvent.data.error) === null || _d === void 0 ? void 0 : _d.type, }); break; case 'success': callbacks.onSuccess({ code: typedEvent.data.code, state: typedEvent.data.state, idpRedirectUri: typedEvent.data.idpRedirectUri, }); break; default: callbacks.onError({ errorMessage: `Report to developers@tryfinch.com: unable to handle window.postMessage for: ${JSON.stringify(typedEvent.data)}`, }); } }; }; const createFinchConnectCore = (callbacks) => { const close = () => { removeIFrame(); }; const messageHandler = createMessageHandler(callbacks, close); const open = (args) => { const url = constructAuthUrl({ sessionId: args.sessionId, state: args.state, apiConfig: callbacks.apiConfig, }); createAndAttachIFrame({ src: url, zIndex: args.zIndex, }); }; const openPreview = (args) => { const url = constructPreviewUrl({ clientId: args.clientId, products: args.products, apiConfig: callbacks.apiConfig, }); createAndAttachIFrame({ src: url, }); }; const destroy = () => { close(); window.removeEventListener('message', messageHandler); }; window.addEventListener('message', messageHandler); return { open, openPreview, close, destroy, }; }; let isInitialized = false; class FinchConnect { constructor(args) { this.open = (args) => { this.core.open(args); }; this.openPreview = (args) => { this.core.openPreview(args); }; this.close = () => { this.core.close(); }; this.destroy = () => { this.core.destroy(); isInitialized = false; }; if (isInitialized) { console.error('FinchConnect.initialize has already been called. Please ensure to only call FinchConnect.initialize once to avoid your event callbacks being triggered multiple times.'); } else { isInitialized = true; } this.core = createFinchConnectCore(args); } static initialize(args) { return new FinchConnect(args); } } return FinchConnect; }));