UNPKG

@krowdy/kds-auth

Version:

React components that implement Google's Material Design.

598 lines (544 loc) 16.7 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import LoginContext from './LoginContext'; import AuthClient from '../Client'; import { initialState, updateStorage, clearStorage, ViewType } from './utils'; import { parseQueryString } from '../utils'; import PasswordNotify from '../LoginSinglePage/PasswordNotify'; import OneTapAuth from '../OnetapAuth'; import OneTapGoogle from '../OnetapGoogle/OnetapGoogle'; import LinkedInPopUp from '../LoginSinglePage/LinkedInPopUp'; var _ref = /*#__PURE__*/React.createElement(OneTapGoogle, null); var _ref2 = /*#__PURE__*/React.createElement(LinkedInPopUp, null); const AuthProvider = ({ children, stateContext = {}, baseUrl, storage = 'localStorage', urlLogin, social: { google, linkedin, microsoft, facebook } = {}, referrer, clientSecret, loginWith = 'only-email', clientId, withGoogle, isBolsa, customLegalBase, onError = () => {} }) => { const authClient = useRef(); const iframeRef = useRef(); const [state, setState] = useState(initialState); const paramsLinkedIn = typeof window !== 'undefined' ? parseQueryString(window.location.search) : {}; useEffect(() => { if (baseUrl) authClient.current = new AuthClient(baseUrl); }, [baseUrl]); const sendMessageToLoginApp = useCallback((eventId, credentials) => { const iframe = iframeRef ? iframeRef.current : null; if (iframe) iframe.contentWindow.postMessage({ credentials, eventId }, `${urlLogin}/set-credentials`); }, // eslint-disable-next-line react-hooks/exhaustive-deps [urlLogin, iframeRef, iframeRef.current]); const _handleError = useCallback(error => { onError(error, state.typeView); }, [onError, state.typeView]); const _handleVerifySession = useCallback(credentials => authClient.current.verifySession(credentials).then(res => { if (res.success) { setState(prev => _extends({}, prev, {}, credentials)); updateStorage(storage, credentials); } return res.success; }).catch(() => { setState(prev => _extends({}, prev, { error: true })); return false; }), // eslint-disable-next-line react-hooks/exhaustive-deps [authClient, authClient.current]); const _handleMessage = useCallback(async ({ data }) => { const { success, eventId, credentials } = data; if (success && eventId === 'authStatus') { const successLogin = await _handleVerifySession(credentials); setState(prev => _extends({}, prev, { flowFinished: successLogin, loadingAuth: false, successLogin })); } else { setState(prev => _extends({}, prev, { loadingAuth: false })); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { if (urlLogin) { window.addEventListener('message', _handleMessage, false); return () => window.removeEventListener('message', _handleMessage); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const _handleVerifyAccount = useCallback(async (source, recoveryPass = false) => { var _data; setState(prev => _extends({}, prev, { loading: true })); let data = {}; if (authClient === null || authClient === void 0 ? void 0 : authClient.current) data = await authClient.current.validateAccount({ recoveryPass, source }); let result = data; if ((_data = data) === null || _data === void 0 ? void 0 : _data.success) { const { hasPassword, success, value, type } = data; setState(prev => _extends({}, prev, { typeCode: type })); result = { hasPassword, success, type, value }; } else { _handleError(data.error); } setState(prev => _extends({}, prev, { loading: false })); return result; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const _handleSendVerifyCode = useCallback(source => { if (authClient === null || authClient === void 0 ? void 0 : authClient.current) authClient.current.sendVerifyCode(source); }, [authClient]); const _handleLoginByPassword = useCallback(({ email, password, keepSession }) => { if (authClient === null || authClient === void 0 ? void 0 : authClient.current) { setState(prev => _extends({}, prev, { loading: true })); return authClient.current.loginByPassword({ allowAds: state.allowAds ? 1 : 0, clientId, clientSecret, email, keepSession: keepSession ? 1 : 0, password }).then(data => { let result = {}; if (data.success) { const { accessToken, refreshToken, userId } = data; updateStorage(storage, { accessToken, iduser: userId, refreshToken }); setState(prev => _extends({}, prev, { accessToken, flowFinished: true, refreshToken, successLogin: true, userId })); sendMessageToLoginApp('logged', { accessToken, iduser: userId, refreshToken }); result = { accessToken, refreshToken, success: true, userId }; } setState(prev => _extends({}, prev, { loading: false })); return result; }).catch(error => { setState(prev => _extends({}, prev, { loading: false })); _handleError(error); return {}; }); } }, [clientId, clientSecret, _handleError, sendMessageToLoginApp, state.allowAds, storage]); const _handleLoginByCode = useCallback(async ({ code, value, type, keepSession }) => { if (authClient === null || authClient === void 0 ? void 0 : authClient.current) { setState(prev => _extends({}, prev, { loading: true })); return authClient.current.verifyCode({ allowAds: state.allowAds ? 1 : 0, clientId, clientSecret, code, keepSession: keepSession ? 1 : 0, type, value }).then(data => { let result = {}; if (data.success) { const { accessToken, refreshToken, userId, isNew } = data; if (state.typeView !== ViewType.Recovery) updateStorage(storage, { accessToken, iduser: userId, refreshToken }); setState(prev => _extends({}, prev, { accessToken, flowFinished: !isNew, isNew, refreshToken, userId })); sendMessageToLoginApp('logged', { accessToken, iduser: userId, refreshToken }); result = { accessToken, isNew, refreshToken, success: true, userId }; } if (data.error) result = { error: data.error, success: false }; setState(prev => _extends({}, prev, { loading: false })); return result; }).catch(error => { setState(prev => _extends({}, prev, { loading: false })); _handleError(error); return {}; }); } }, [state.allowAds, state.typeView, clientId, clientSecret, storage, sendMessageToLoginApp, _handleError]); const _handleSuccessLogin = useCallback(successLogin => { setState(prev => _extends({}, prev, { successLogin })); }, []); const _handleUpdateAccount = useCallback(async body => { setState(prev => _extends({}, prev, { loading: true })); let data; if (authClient === null || authClient === void 0 ? void 0 : authClient.current) data = await authClient.current.updateAccount(state.accessToken, body); setState(prev => { var _data2; return _extends({}, prev, { loading: false, successLogin: Boolean((_data2 = data) === null || _data2 === void 0 ? void 0 : _data2.success) }); }); return data; }, // eslint-disable-next-line react-hooks/exhaustive-deps [authClient, authClient.current, state.accessToken]); const _handleValidateSocial = useCallback(async (network, response) => { const { tokenId, method } = response; if (authClient === null || authClient === void 0 ? void 0 : authClient.current) { setState(prev => _extends({}, prev, { loadingSignIn: true, method })); const { error, refreshToken, accessToken, userId, isNew } = await authClient.current.loginSocialNetwork({ allowAds: state.allowAds ? 1 : 0, clientId, clientSecret, keepSession: state.keepSession ? 1 : 0, network, tokenId }, referrer); if (!error) { sendMessageToLoginApp('logged', { accessToken, iduser: userId, refreshToken }); updateStorage(storage, { accessToken, iduser: userId, refreshToken }); if (network === 'microsoft') window.sessionStorage.clear(); setState(prev => _extends({}, prev, { accessToken, flowFinished: true, isNew, refreshToken, successLogin: true, userId })); } else { _handleError(error); setState(prev => _extends({}, prev, { loadingSignIn: false })); } } }, [clientId, clientSecret, _handleError, referrer, sendMessageToLoginApp, state.allowAds, state.keepSession, storage]); const _handleAllowAds = useCallback(() => { setState(prev => _extends({}, prev, { allowAds: !prev.allowAds })); }, []); const _handleFlowFinished = useCallback(flowFinished => { setState(prev => _extends({}, prev, { flowFinished })); }, []); const createPassword = useCallback(async password => { if (authClient === null || authClient === void 0 ? void 0 : authClient.current) { setState(prev => _extends({}, prev, { loading: true })); const res = await authClient.current.updatePassword({ accessToken: state.accessToken, password }); if (!(res === null || res === void 0 ? void 0 : res.success)) { setState(prev => _extends({}, prev, { loading: false })); _handleError(res ? res.error : null); return { success: false }; } const { accessToken, userId, refreshToken } = state; if (state.keepSession) updateStorage(storage, { accessToken, iduser: userId, refreshToken }); setState(prev => _extends({}, prev, { flowFinished: true, loading: false, successLogin: true })); return { success: true }; } else { return { success: false }; } }, // eslint-disable-next-line react-hooks/exhaustive-deps [authClient, authClient.current, state.accessToken]); const _handleLogOut = useCallback(() => { const deleteSession = () => { if (window.google) window.google.accounts.id.disableAutoSelect(); clearStorage(storage, ['accessToken', 'iduser', 'refreshToken']); sessionStorage.clear(); }; const { accessToken, refreshToken } = state; if (accessToken || refreshToken) { authClient.current.logout({ accessToken, refreshToken }).then(res => { const { success } = res; if (!success) return console.error('Error when user closing session'); deleteSession(); sendMessageToLoginApp('unlogged'); }).catch(error => { _handleError(error); deleteSession(); sendMessageToLoginApp('unlogged'); }); } else { deleteSession(); sendMessageToLoginApp('unlogged'); } }, [_handleError, sendMessageToLoginApp, state, storage]); const _handleUpdateState = useCallback(data => { setState(prev => _extends({}, prev, {}, data)); }, []); const _handlePasswordNotify = useCallback(value => { setState(prev => _extends({}, prev, { openPasswordNotify: value })); }, []); const _handlePasswordCreate = useCallback(() => { setState(prev => _extends({}, prev, { openBackdrop: true, openPasswordNotify: false, typeView: ViewType.NewPassword })); }, []); const _handleClosePasswordNotify = useCallback(() => { setState(prev => _extends({}, prev, { flowFinished: true, openPasswordNotify: false })); }, []); const _handleChangeView = useCallback(typeView => { setState(prev => _extends({}, prev, { typeView })); }, []); const _handleCloseModal = useCallback(() => { setState(prev => _extends({}, prev, { customMainText: '', openBackdrop: false })); }, []); const _handleOpenModal = useCallback(customMainText => { setState(prev => _extends({}, prev, { customMainText: typeof customMainText === 'string' ? customMainText : '', openBackdrop: true })); }, []); const _handleSetLoadingSignIn = useCallback(value => { setState(prev => _extends({}, prev, { loadingSignIn: value })); }, []); const now = useMemo(() => new Date().getTime(), []); return /*#__PURE__*/React.createElement(LoginContext.Provider, { value: _extends({}, state, {}, stateContext, { customLegalBase, facebookCredentials: facebook, googleCredentials: google, isBolsa, linkedinCredentials: linkedin, loginByCode: _handleLoginByCode, loginByPassword: _handleLoginByPassword, loginWith, logout: _handleLogOut, microsoftCredentials: microsoft, onAllowAds: _handleAllowAds, onChangeView: _handleChangeView, onClose: _handleCloseModal, onError: _handleError, onFlowFinished: _handleFlowFinished, onOpen: _handleOpenModal, onPasswordNotify: _handlePasswordNotify, onSuccessLogin: _handleSuccessLogin, onUpdatePassword: createPassword, onUpdateState: _handleUpdateState, referrer, sendVerifyOrCode: _handleSendVerifyCode, setLoadingSignIn: _handleSetLoadingSignIn, updateAccount: _handleUpdateAccount, validateSocialNetwork: _handleValidateSocial, verifyAccount: _handleVerifyAccount }) }, withGoogle && !state.successLogin && !state.loadingAuth ? _ref : null, urlLogin && /*#__PURE__*/React.createElement("iframe", { height: "0px", ref: iframeRef, src: `${urlLogin}/set-credentials?timestamp=${now}`, style: { display: 'none' }, title: "Alternative login", width: "0px" }), state.openPasswordNotify ? /*#__PURE__*/React.createElement(PasswordNotify, { onClose: _handleClosePasswordNotify, onCreate: _handlePasswordCreate }) : null, children, paramsLinkedIn.code || paramsLinkedIn.error ? _ref2 : null, state.openBackdrop ? /*#__PURE__*/React.createElement(OneTapAuth, { customMainText: state.customMainText }) : null); }; process.env.NODE_ENV !== "production" ? AuthProvider.propTypes = { baseUrl: PropTypes.string.isRequired, children: PropTypes.any, clientId: PropTypes.string, clientSecret: PropTypes.string, customLegalBase: PropTypes.shape({ footerHTML: PropTypes.string, urlPrivacyPolicy: PropTypes.string, urlTermsAndConditions: PropTypes.string }), isBolsa: PropTypes.bool, loginWith: PropTypes.oneOf(['only-email', 'only-phone', 'phone-and-email']), onError: PropTypes.func, referrer: PropTypes.string, social: PropTypes.shape({ facebook: PropTypes.shape({ clientId: PropTypes.string, redirectUri: PropTypes.string }), google: PropTypes.shape({ clientId: PropTypes.string, redirectUri: PropTypes.string }), linkedin: PropTypes.shape({ clientId: PropTypes.string, redirectUri: PropTypes.string }), microsoft: PropTypes.shape({ clientId: PropTypes.string, redirectUri: PropTypes.string }) }), stateContext: PropTypes.any, storage: PropTypes.string, urlLogin: PropTypes.string, withGoogle: PropTypes.bool } : void 0; export default React.memo(AuthProvider);