@krowdy/kds-auth
Version:
React components that implement Google's Material Design.
598 lines (544 loc) • 16.7 kB
JavaScript
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);