mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-23 05:13:04 +03:00
Merge pull request #2398 from ecency/nt/pin-encryption
Nt/pin encryption
This commit is contained in:
commit
a4d8edc311
@ -25,7 +25,7 @@ import { getSCAccessToken, getUnreadNotificationCount } from '../ecency/ecency';
|
||||
import AUTH_TYPE from '../../constants/authType';
|
||||
import { makeHsCode } from '../../utils/hive-signer-helper';
|
||||
|
||||
export const login = async (username, password, isPinCodeOpen) => {
|
||||
export const login = async (username, password) => {
|
||||
let loginFlag = false;
|
||||
let avatar = '';
|
||||
let authType = '';
|
||||
@ -98,9 +98,6 @@ export const login = async (username, password, isPinCodeOpen) => {
|
||||
accessToken: '',
|
||||
};
|
||||
|
||||
if (isPinCodeOpen) {
|
||||
account.local = userData;
|
||||
} else {
|
||||
const resData = {
|
||||
pinCode: Config.DEFAULT_PIN,
|
||||
password,
|
||||
@ -110,7 +107,6 @@ export const login = async (username, password, isPinCodeOpen) => {
|
||||
|
||||
account.local = updatedUserData;
|
||||
account.local.avatar = avatar;
|
||||
}
|
||||
|
||||
const authData = {
|
||||
isLoggedIn: true,
|
||||
@ -130,7 +126,7 @@ export const login = async (username, password, isPinCodeOpen) => {
|
||||
return Promise.reject(new Error('auth.invalid_credentials'));
|
||||
};
|
||||
|
||||
export const loginWithSC2 = async (code, isPinCodeOpen) => {
|
||||
export const loginWithSC2 = async (code) => {
|
||||
const scTokens = await getSCAccessToken(code);
|
||||
await hsApi.setAccessToken(get(scTokens, 'access_token', ''));
|
||||
const scAccount = await hsApi.me();
|
||||
@ -168,9 +164,6 @@ export const loginWithSC2 = async (code, isPinCodeOpen) => {
|
||||
};
|
||||
const isUserLoggedIn = await isLoggedInUser(account.name);
|
||||
|
||||
if (isPinCodeOpen) {
|
||||
account.local = userData;
|
||||
} else {
|
||||
const resData = {
|
||||
pinCode: Config.DEFAULT_PIN,
|
||||
accessToken: get(scTokens, 'access_token', ''),
|
||||
@ -179,7 +172,6 @@ export const loginWithSC2 = async (code, isPinCodeOpen) => {
|
||||
|
||||
account.local = updatedUserData;
|
||||
account.local.avatar = avatar;
|
||||
}
|
||||
|
||||
if (isUserLoggedIn) {
|
||||
reject(new Error('auth.already_logged'));
|
||||
@ -296,37 +288,38 @@ export const updatePinCode = (data) =>
|
||||
}
|
||||
});
|
||||
|
||||
export const verifyPinCode = async (data) => {
|
||||
try {
|
||||
const pinHash = await getPinCode();
|
||||
// export const verifyPinCode = async (data) => {
|
||||
// try {
|
||||
// const pinHash = await getPinCode();
|
||||
|
||||
const result = await getUserDataWithUsername(data.username);
|
||||
const userData = result[0];
|
||||
// const result = await getUserDataWithUsername(data.username);
|
||||
// const userData = result[0];
|
||||
|
||||
// This is migration for new pin structure, it will remove v2.2
|
||||
if (!pinHash) {
|
||||
try {
|
||||
if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
|
||||
decryptKey(get(userData, 'accessToken'), get(data, 'pinCode'));
|
||||
} else {
|
||||
decryptKey(userData.masterKey, get(data, 'pinCode'));
|
||||
}
|
||||
await setPinCode(get(data, 'pinCode'));
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error('Invalid pin code, please check and try again'));
|
||||
}
|
||||
}
|
||||
// // This is migration for new pin structure, it will remove v2.2
|
||||
// if (!pinHash) {
|
||||
// try {
|
||||
// //if decrypt fails, means key is invalid
|
||||
// if (userData.accessToken === AUTH_TYPE.STEEM_CONNECT) {
|
||||
// decryptKey(userData.accessToken, data.pinCode);
|
||||
// } else {
|
||||
// decryptKey(userData.masterKey, data.pinCode);
|
||||
// }
|
||||
// await setPinCode(data.pinCode);
|
||||
// } catch (error) {
|
||||
// return Promise.reject(new Error('Invalid pin code, please check and try again'));
|
||||
// }
|
||||
// }
|
||||
|
||||
if (sha256(get(data, 'pinCode')).toString() !== pinHash) {
|
||||
return Promise.reject(new Error('auth.invalid_pin'));
|
||||
}
|
||||
// if (sha256(get(data, 'pinCode')).toString() !== pinHash) {
|
||||
// return Promise.reject(new Error('auth.invalid_pin'));
|
||||
// }
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.warn('Failed to verify pin in auth: ', data, err);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
};
|
||||
// return true;
|
||||
// } catch (err) {
|
||||
// console.warn('Failed to verify pin in auth: ', data, err);
|
||||
// return Promise.reject(err);
|
||||
// }
|
||||
// };
|
||||
|
||||
export const refreshSCToken = async (userData, pinCode) => {
|
||||
const scAccount = await getSCAccount(userData.username);
|
||||
|
@ -32,7 +32,8 @@ import {
|
||||
SET_SETTINGS_MIGRATED,
|
||||
HIDE_POSTS_THUMBNAILS,
|
||||
SET_TERMS_ACCEPTED,
|
||||
SET_IS_BIOMETRIC_ENABLED
|
||||
SET_IS_BIOMETRIC_ENABLED,
|
||||
SET_ENC_UNLOCK_PIN
|
||||
} from '../constants/constants';
|
||||
|
||||
export const login = (payload) => ({
|
||||
@ -215,4 +216,9 @@ export const setIsBiometricEnabled = (enabled:boolean) => ({
|
||||
type: SET_IS_BIOMETRIC_ENABLED
|
||||
})
|
||||
|
||||
export const setEncryptedUnlockPin = (encryptedUnlockPin:string) => ({
|
||||
payload:encryptedUnlockPin,
|
||||
type: SET_ENC_UNLOCK_PIN
|
||||
})
|
||||
|
||||
|
||||
|
@ -37,6 +37,7 @@ export const SET_COLOR_THEME = 'SET_COLOR_THEME';
|
||||
export const SET_SETTINGS_MIGRATED = 'SET_SETTINGS_MIGRATED';
|
||||
export const SET_TERMS_ACCEPTED = 'SET_TERMS_ACCEPTED';
|
||||
export const SET_IS_BIOMETRIC_ENABLED = 'SET_IS_BIOMETRIC_ENABLED';
|
||||
export const SET_ENC_UNLOCK_PIN = 'SET_ENC_UNLOCK_PIN';
|
||||
|
||||
// Accounts
|
||||
export const ADD_OTHER_ACCOUNT = 'ADD_OTHER_ACCOUNT';
|
||||
|
@ -30,7 +30,8 @@ import {
|
||||
SET_SETTINGS_MIGRATED,
|
||||
HIDE_POSTS_THUMBNAILS,
|
||||
SET_TERMS_ACCEPTED,
|
||||
SET_IS_BIOMETRIC_ENABLED
|
||||
SET_IS_BIOMETRIC_ENABLED,
|
||||
SET_ENC_UNLOCK_PIN
|
||||
} from '../constants/constants';
|
||||
|
||||
interface State {
|
||||
@ -63,9 +64,10 @@ interface State {
|
||||
},
|
||||
upvotePercent: number;
|
||||
nsfw: string;
|
||||
pin: string|null;
|
||||
pin: string|null; //encrypted pin used for encrypting sensitive user data
|
||||
isPinCodeOpen: boolean;
|
||||
isRenderRequired: boolean;
|
||||
encUnlockPin: string; //ecryped pin used for user defined lock screen pass code
|
||||
lastAppVersion:string;
|
||||
settingsMigratedV2: boolean;
|
||||
hidePostsThumbnails: boolean;
|
||||
@ -106,6 +108,7 @@ const initialState:State = {
|
||||
pin: null,
|
||||
isPinCodeOpen: false,
|
||||
isRenderRequired: false,
|
||||
encUnlockPin: '',
|
||||
lastAppVersion:'',
|
||||
settingsMigratedV2: false,
|
||||
hidePostsThumbnails: false,
|
||||
@ -113,7 +116,7 @@ const initialState:State = {
|
||||
isBiometricEnabled: false
|
||||
};
|
||||
|
||||
export default function (state = initialState, action) {
|
||||
export default function (state = initialState, action):State {
|
||||
switch (action.type) {
|
||||
case LOGIN:
|
||||
return {
|
||||
@ -292,6 +295,12 @@ export default function (state = initialState, action) {
|
||||
isBiometricEnabled:action.payload
|
||||
}
|
||||
|
||||
case SET_ENC_UNLOCK_PIN:
|
||||
return {
|
||||
...state,
|
||||
encUnlockPin:action.payload
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -1,83 +0,0 @@
|
||||
import { Appearance } from 'react-native';
|
||||
|
||||
// Constants
|
||||
import THEME_OPTIONS from '../../../constants/options/theme';
|
||||
|
||||
// Services
|
||||
import {
|
||||
getSettings,
|
||||
} from '../../../realm/realm';
|
||||
|
||||
import {
|
||||
isDarkTheme,
|
||||
changeNotificationSettings,
|
||||
changeAllNotificationSettings,
|
||||
setApi,
|
||||
setCurrency,
|
||||
setLanguage,
|
||||
setUpvotePercent,
|
||||
setNsfw,
|
||||
isDefaultFooter,
|
||||
isPinCodeOpen,
|
||||
setColorTheme,
|
||||
setSettingsMigrated,
|
||||
} from '../../../redux/actions/applicationActions';
|
||||
import {
|
||||
hideActionModal,
|
||||
hideProfileModal,
|
||||
setRcOffer,
|
||||
toastNotification,
|
||||
} from '../../../redux/actions/uiAction';
|
||||
|
||||
|
||||
//migrates settings from realm to redux once and do no user realm for settings again;
|
||||
export const migrateSettings = async (dispatch: any, settingsMigratedV2: boolean) => {
|
||||
|
||||
if (settingsMigratedV2) {
|
||||
return;
|
||||
}
|
||||
|
||||
//reset certain properties
|
||||
dispatch(hideActionModal());
|
||||
dispatch(hideProfileModal());
|
||||
dispatch(toastNotification(''));
|
||||
dispatch(setRcOffer(false));
|
||||
|
||||
|
||||
const settings = await getSettings();
|
||||
|
||||
if (settings) {
|
||||
const isDarkMode = Appearance.getColorScheme() === 'dark';
|
||||
dispatch(isDarkTheme(settings.isDarkTheme !== null ? settings.isDarkTheme : isDarkMode));
|
||||
dispatch(setColorTheme(THEME_OPTIONS.findIndex(item => item.value === settings.isDarkTheme)));
|
||||
await dispatch(isPinCodeOpen(!!settings.isPinCodeOpen));
|
||||
if (settings.language !== '') dispatch(setLanguage(settings.language));
|
||||
if (settings.server !== '') dispatch(setApi(settings.server));
|
||||
if (settings.upvotePercent !== '') {
|
||||
dispatch(setUpvotePercent(Number(settings.upvotePercent)));
|
||||
}
|
||||
if (settings.isDefaultFooter !== '') dispatch(isDefaultFooter(settings.isDefaultFooter)); //TODO: remove as not being used
|
||||
|
||||
|
||||
if (settings.nsfw !== '') dispatch(setNsfw(settings.nsfw));
|
||||
|
||||
dispatch(setCurrency(settings.currency !== '' ? settings.currency : 'usd'));
|
||||
|
||||
if (settings.notification !== '') {
|
||||
dispatch(
|
||||
changeNotificationSettings({
|
||||
type: 'notification',
|
||||
action: settings.notification,
|
||||
}),
|
||||
);
|
||||
|
||||
dispatch(changeAllNotificationSettings(settings));
|
||||
}
|
||||
|
||||
await dispatch(setSettingsMigrated(true))
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
migrateSettings
|
||||
}
|
@ -69,6 +69,8 @@ import {
|
||||
setPinCode as savePinCode,
|
||||
isRenderRequired,
|
||||
logout,
|
||||
isPinCodeOpen,
|
||||
setEncryptedUnlockPin,
|
||||
} from '../../../redux/actions/applicationActions';
|
||||
import {
|
||||
setAvatarCacheStamp,
|
||||
@ -89,7 +91,7 @@ import { setMomentLocale } from '../../../utils/time';
|
||||
import parseAuthUrl from '../../../utils/parseAuthUrl';
|
||||
import { purgeExpiredCache } from '../../../redux/actions/cacheActions';
|
||||
import { fetchSubscribedCommunities } from '../../../redux/actions/communitiesAction';
|
||||
import MigrationHelpers from '../children/migrationHelpers';
|
||||
import MigrationHelpers from '../../../utils/migrationHelpers';
|
||||
|
||||
// Workaround
|
||||
let previousAppState = 'background';
|
||||
@ -666,9 +668,9 @@ class ApplicationContainer extends Component {
|
||||
};
|
||||
|
||||
_refreshAccessToken = async (currentAccount) => {
|
||||
const { pinCode, isPinCodeOpen, dispatch, intl } = this.props;
|
||||
const { pinCode, isPinCodeOpen, encUnlockPin, dispatch, intl } = this.props;
|
||||
|
||||
if (isPinCodeOpen) {
|
||||
if (isPinCodeOpen && !encUnlockPin) {
|
||||
return currentAccount;
|
||||
}
|
||||
|
||||
@ -703,14 +705,14 @@ class ApplicationContainer extends Component {
|
||||
};
|
||||
|
||||
_fetchUserDataFromDsteem = async (realmObject) => {
|
||||
const { dispatch, intl, pinCode, isPinCodeOpen } = this.props;
|
||||
const { dispatch, intl, pinCode, isPinCodeOpen, encUnlockPin } = this.props;
|
||||
|
||||
try {
|
||||
let accountData = await getUser(realmObject.username);
|
||||
accountData.local = realmObject;
|
||||
|
||||
//cannot migrate or refresh token since pin would null while pin code modal is open
|
||||
if (!isPinCodeOpen) {
|
||||
if (!isPinCodeOpen || encUnlockPin) {
|
||||
//migration script for previously mast key based logged in user not having access token
|
||||
if (realmObject.authType !== AUTH_TYPE.STEEM_CONNECT && realmObject.accessToken === '') {
|
||||
accountData = await migrateToMasterKeyWithAccessToken(accountData, realmObject, pinCode);
|
||||
@ -792,6 +794,7 @@ class ApplicationContainer extends Component {
|
||||
currentAccount: { name, local },
|
||||
dispatch,
|
||||
intl,
|
||||
|
||||
} = this.props;
|
||||
|
||||
removeUserData(name)
|
||||
@ -812,6 +815,8 @@ class ApplicationContainer extends Component {
|
||||
isLoggedIn: false,
|
||||
});
|
||||
setExistUser(false);
|
||||
dispatch(isPinCodeOpen(false));
|
||||
dispatch(setEncryptedUnlockPin(encryptKey(Config.DEFAULT_KEU, Config.PIN_KEY)))
|
||||
if (local.authType === AUTH_TYPE.STEEM_CONNECT) {
|
||||
removeSCAccount(name);
|
||||
}
|
||||
@ -984,6 +989,7 @@ export default connect(
|
||||
isDarkTheme: state.application.isDarkTheme,
|
||||
selectedLanguage: state.application.language,
|
||||
isPinCodeOpen: state.application.isPinCodeOpen,
|
||||
encUnlockPin: state.application.encUnlockPin,
|
||||
isLogingOut: state.application.isLogingOut,
|
||||
isLoggedIn: state.application.isLoggedIn, //TODO: remove as is not being used in this class
|
||||
isConnected: state.application.isConnected,
|
||||
|
@ -60,7 +60,7 @@ class LoginContainer extends PureComponent {
|
||||
|
||||
this.setState({ isLoading: true });
|
||||
|
||||
login(username, password, isPinCodeOpen)
|
||||
login(username, password)
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
const persistAccountData = persistAccountGenerator(result);
|
||||
@ -75,11 +75,12 @@ class LoginContainer extends PureComponent {
|
||||
userActivity(20);
|
||||
setExistUser(true);
|
||||
this._setPushToken(result.name);
|
||||
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
|
||||
dispatch(setPinCode(encryptedPin));
|
||||
|
||||
if (isPinCodeOpen) {
|
||||
dispatch(openPinCodeModal({ navigateTo: ROUTES.DRAWER.MAIN }));
|
||||
} else {
|
||||
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
|
||||
dispatch(setPinCode(encryptedPin));
|
||||
navigation.navigate({
|
||||
routeName: ROUTES.DRAWER.MAIN,
|
||||
});
|
||||
|
@ -6,17 +6,11 @@ import Config from 'react-native-config';
|
||||
import get from 'lodash/get';
|
||||
import FingerprintScanner from 'react-native-fingerprint-scanner';
|
||||
|
||||
//Contstants
|
||||
import AUTH_TYPE from '../../../constants/authType';
|
||||
|
||||
// Actions & Services
|
||||
import { navigate } from '../../../navigation/service';
|
||||
import {
|
||||
setUserDataWithPinCode,
|
||||
verifyPinCode,
|
||||
updatePinCode,
|
||||
migrateToMasterKeyWithAccessToken,
|
||||
refreshSCToken,
|
||||
} from '../../../providers/hive/auth';
|
||||
import {
|
||||
closePinCodeModal,
|
||||
@ -24,28 +18,24 @@ import {
|
||||
login,
|
||||
logout,
|
||||
logoutDone,
|
||||
setPinCode as savePinCode,
|
||||
setEncryptedUnlockPin,
|
||||
} from '../../../redux/actions/applicationActions';
|
||||
import {
|
||||
getExistUser,
|
||||
setExistUser,
|
||||
getUserDataWithUsername,
|
||||
removeAllUserData,
|
||||
removePinCode,
|
||||
setAuthStatus,
|
||||
setPinCodeOpen,
|
||||
} from '../../../realm/realm';
|
||||
import { updateCurrentAccount, removeOtherAccount } from '../../../redux/actions/accountAction';
|
||||
import { getDigitPinCode, getMutes, getUser } from '../../../providers/hive/dhive';
|
||||
import { getPointsSummary } from '../../../providers/ecency/ePoint';
|
||||
|
||||
// Utils
|
||||
import { encryptKey, decryptKey } from '../../../utils/crypto';
|
||||
import MigrationHelpers from '../../../utils/migrationHelpers';
|
||||
|
||||
// Component
|
||||
import PinCodeScreen from '../screen/pinCodeScreen';
|
||||
import { getUnreadNotificationCount } from '../../../providers/ecency/ecency';
|
||||
import { fetchSubscribedCommunities } from '../../../redux/actions/communitiesAction';
|
||||
|
||||
|
||||
class PinCodeContainer extends Component {
|
||||
screenRef = null;
|
||||
@ -62,10 +52,10 @@ class PinCodeContainer extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: if check for decide to set to pin or verify to pin page
|
||||
// TODO: these text should move to view!
|
||||
|
||||
//sets initial pin code screen label based on oldPinVerified param/state
|
||||
componentDidMount() {
|
||||
this._getDataFromStorage().then(() => {
|
||||
|
||||
const { intl } = this.props;
|
||||
const { isOldPinVerified } = this.state;
|
||||
|
||||
@ -82,29 +72,17 @@ class PinCodeContainer extends Component {
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this._processBiometric();
|
||||
}
|
||||
|
||||
_getDataFromStorage = () =>
|
||||
new Promise((resolve) => {
|
||||
getExistUser().then((isExistUser) => {
|
||||
this.setState(
|
||||
{
|
||||
isExistUser,
|
||||
},
|
||||
resolve,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
_processBiometric = async () => {
|
||||
try {
|
||||
const {
|
||||
intl,
|
||||
pinCodeParams: { isReset },
|
||||
applicationPinCode,
|
||||
encUnlockPin,
|
||||
isBiometricEnabled,
|
||||
} = this.props;
|
||||
|
||||
@ -122,7 +100,8 @@ class PinCodeContainer extends Component {
|
||||
|
||||
//code gets here means biometeric succeeded
|
||||
if (this.screenRef) {
|
||||
const verifiedPin = decryptKey(applicationPinCode, Config.PIN_KEY, this._onDecryptFail);
|
||||
const encPin = encUnlockPin || applicationPinCode;
|
||||
const verifiedPin = decryptKey(encPin, Config.PIN_KEY, this._onDecryptFail);
|
||||
this.screenRef.setPinThroughBiometric(verifiedPin);
|
||||
}
|
||||
} catch (err) {
|
||||
@ -153,38 +132,31 @@ class PinCodeContainer extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//routine for checking and setting new pin code, same routine is used for
|
||||
//setting pin for the first time
|
||||
_resetPinCode = (pin) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const {
|
||||
currentAccount,
|
||||
dispatch,
|
||||
pinCodeParams: { navigateTo, navigateParams, accessToken, callback },
|
||||
pinCodeParams: { navigateTo, navigateParams, callback },
|
||||
encUnlockPin,
|
||||
intl,
|
||||
} = this.props;
|
||||
const { isOldPinVerified, oldPinCode, newPinCode } = this.state;
|
||||
|
||||
const pinData = {
|
||||
pinCode: pin,
|
||||
password: currentAccount ? currentAccount.password : '',
|
||||
username: currentAccount ? currentAccount.name : '',
|
||||
accessToken,
|
||||
oldPinCode,
|
||||
};
|
||||
|
||||
//if old pin already verified, check new pin setup conditions.
|
||||
if (isOldPinVerified) {
|
||||
//if newPin already exist and pin is a valid pin, compare and set new pin
|
||||
if (pin !== undefined && pin === newPinCode) {
|
||||
this._updatePinCodeRealm(pinData).then((status) => {
|
||||
if (!status) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
this._savePinCode(pin);
|
||||
if (callback) {
|
||||
callback(pin, oldPinCode);
|
||||
}
|
||||
dispatch(closePinCodeModal());
|
||||
|
||||
if (navigateTo) {
|
||||
navigate({
|
||||
routeName: navigateTo,
|
||||
@ -192,7 +164,6 @@ class PinCodeContainer extends Component {
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// if newPin code exists and above case failed, that means pins did not match
|
||||
@ -223,8 +194,17 @@ class PinCodeContainer extends Component {
|
||||
|
||||
// if old pin code is not yet verified attempt to verify code
|
||||
else {
|
||||
verifyPinCode(pinData)
|
||||
.then(() => {
|
||||
|
||||
let unlockPin = decryptKey(encUnlockPin, Config.PIN_KEY)
|
||||
|
||||
//check if pins match
|
||||
if (unlockPin !== pin) {
|
||||
const err = new Error('alert.invalid_pincode');
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.setState({ isOldPinVerified: true });
|
||||
this.setState({
|
||||
informationText: intl.formatMessage({
|
||||
@ -234,62 +214,11 @@ class PinCodeContainer extends Component {
|
||||
oldPinCode: pin,
|
||||
});
|
||||
resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('Failed to verify pin code', err);
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: err.message,
|
||||
}),
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
_setFirstPinCode = (pin) =>
|
||||
new Promise((resolve) => {
|
||||
const {
|
||||
currentAccount,
|
||||
dispatch,
|
||||
pinCodeParams: { navigateTo, navigateParams, accessToken, callback },
|
||||
} = this.props;
|
||||
const { oldPinCode } = this.state;
|
||||
|
||||
const pinData = {
|
||||
pinCode: pin,
|
||||
password: currentAccount ? currentAccount.password : '',
|
||||
username: currentAccount ? currentAccount.name : '',
|
||||
accessToken,
|
||||
};
|
||||
|
||||
setUserDataWithPinCode(pinData).then((response) => {
|
||||
getUser(currentAccount.name).then((user) => {
|
||||
const _currentAccount = user;
|
||||
_currentAccount.local = response;
|
||||
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
|
||||
setExistUser(true).then(() => {
|
||||
this._savePinCode(pin);
|
||||
if (callback) {
|
||||
callback(pin, oldPinCode);
|
||||
}
|
||||
dispatch(closePinCodeModal());
|
||||
if (navigateTo) {
|
||||
navigate({
|
||||
routeName: navigateTo,
|
||||
params: navigateParams,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
_onRefreshTokenFailed = (error) => {
|
||||
setTimeout(() => {
|
||||
@ -308,98 +237,68 @@ class PinCodeContainer extends Component {
|
||||
}, 300);
|
||||
};
|
||||
|
||||
_verifyPinCode = (pin, { shouldUpdateRealm } = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
|
||||
|
||||
//verifies is the pin entered is right or wrong, also migrates to newer locking method
|
||||
_verifyPinCode = async (pin) => {
|
||||
try {
|
||||
const {
|
||||
intl,
|
||||
currentAccount,
|
||||
dispatch,
|
||||
pinCodeParams: { navigateTo, navigateParams, accessToken, callback },
|
||||
encUnlockPin,
|
||||
applicationPinCode,
|
||||
pinCodeParams: { navigateTo, navigateParams, callback },
|
||||
} = this.props;
|
||||
const { oldPinCode } = this.state;
|
||||
|
||||
// If the user is exist, we are just checking to pin and navigating to feed screen
|
||||
const pinData = {
|
||||
pinCode: pin,
|
||||
password: currentAccount ? currentAccount.password : '',
|
||||
username: currentAccount ? currentAccount.name : '',
|
||||
accessToken,
|
||||
};
|
||||
verifyPinCode(pinData)
|
||||
.then(() => {
|
||||
this._savePinCode(pin);
|
||||
getUserDataWithUsername(currentAccount.name).then(async (realmData) => {
|
||||
if (shouldUpdateRealm) {
|
||||
this._updatePinCodeRealm(pinData).then(() => {
|
||||
dispatch(closePinCodeModal());
|
||||
});
|
||||
} else {
|
||||
let _currentAccount = currentAccount;
|
||||
_currentAccount.username = _currentAccount.name;
|
||||
[_currentAccount.local] = realmData;
|
||||
let unlockPin = encUnlockPin ?
|
||||
decryptKey(encUnlockPin, Config.PIN_KEY) : decryptKey(applicationPinCode, Config.PIN_KEY);
|
||||
|
||||
try {
|
||||
const pinHash = encryptKey(pin, Config.PIN_KEY);
|
||||
//migration script for previously mast key based logged in user not having access token
|
||||
if (
|
||||
realmData[0].authType !== AUTH_TYPE.STEEM_CONNECT &&
|
||||
realmData[0].accessToken === ''
|
||||
) {
|
||||
_currentAccount = await migrateToMasterKeyWithAccessToken(
|
||||
_currentAccount,
|
||||
realmData[0],
|
||||
pinHash,
|
||||
);
|
||||
|
||||
//check if pins match
|
||||
if (unlockPin !== pin) {
|
||||
throw new Error(intl.formatMessage({
|
||||
id: 'alert.invalid_pincode',
|
||||
}));
|
||||
}
|
||||
|
||||
//refresh access token
|
||||
const encryptedAccessToken = await refreshSCToken(_currentAccount.local, pin);
|
||||
_currentAccount.local.accessToken = encryptedAccessToken;
|
||||
} catch (error) {
|
||||
this._onRefreshTokenFailed(error);
|
||||
//migrate data to default pin if encUnlockPin is not set.
|
||||
if (!encUnlockPin) {
|
||||
await MigrationHelpers.migrateUserEncryption(dispatch, currentAccount, applicationPinCode, this._onRefreshTokenFailed);
|
||||
}
|
||||
|
||||
//get unread notifications
|
||||
try {
|
||||
_currentAccount.unread_activity_count = await getUnreadNotificationCount();
|
||||
_currentAccount.pointsSummary = await getPointsSummary(_currentAccount.username);
|
||||
_currentAccount.mutes = await getMutes(_currentAccount.username);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'Optional user data fetch failed, account can still function without them',
|
||||
err,
|
||||
);
|
||||
}
|
||||
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
dispatch(fetchSubscribedCommunities(_currentAccount.username));
|
||||
dispatch(closePinCodeModal());
|
||||
}
|
||||
|
||||
//on successful code verification run requested operation passed as props
|
||||
if (callback) {
|
||||
callback(pin, oldPinCode);
|
||||
}
|
||||
|
||||
if (navigateTo) {
|
||||
navigate({
|
||||
routeName: navigateTo,
|
||||
params: navigateParams,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('code verification for login failed: ', err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
dispatch(closePinCodeModal());
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//encryptes and saved unlockPin
|
||||
_savePinCode = (pin) => {
|
||||
const { dispatch } = this.props;
|
||||
const encryptedPin = encryptKey(pin, Config.PIN_KEY);
|
||||
dispatch(savePinCode(encryptedPin));
|
||||
dispatch(setEncryptedUnlockPin(encryptedPin));
|
||||
};
|
||||
|
||||
|
||||
|
||||
_forgotPinCode = async () => {
|
||||
const { otherAccounts, dispatch } = this.props;
|
||||
|
||||
@ -416,7 +315,6 @@ class PinCodeContainer extends Component {
|
||||
dispatch(logoutDone());
|
||||
dispatch(closePinCodeModal());
|
||||
dispatch(isPinCodeOpen(false));
|
||||
setPinCodeOpen(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('Failed to remove user data', err);
|
||||
@ -478,51 +376,16 @@ class PinCodeContainer extends Component {
|
||||
};
|
||||
|
||||
_setPinCode = async (pin, isReset) => {
|
||||
const { intl, currentAccount, applicationPinCode } = this.props;
|
||||
const { isExistUser } = this.state;
|
||||
|
||||
try {
|
||||
const realmData = await getUserDataWithUsername(currentAccount.name);
|
||||
const userData = realmData[0];
|
||||
|
||||
// check if reset routine is triggered by user, reroute code to reset hanlder
|
||||
if (isReset) {
|
||||
await this._resetPinCode(pin);
|
||||
return true;
|
||||
}
|
||||
|
||||
//user is logged in and is not reset routine...
|
||||
if (isExistUser) {
|
||||
if (!userData.accessToken && !userData.masterKey && applicationPinCode) {
|
||||
const verifiedPin = decryptKey(applicationPinCode, Config.PIN_KEY, this._onDecryptFail);
|
||||
if (verifiedPin === undefined) {
|
||||
return true;
|
||||
}
|
||||
if (verifiedPin === pin) {
|
||||
await this._setFirstPinCode(pin);
|
||||
} else {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'alert.invalid_pincode',
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await this._verifyPinCode(pin);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//means this is not reset routine and user do not exist
|
||||
//only possible option left is user logging int,
|
||||
//verifyPinCode then and update realm as well.
|
||||
else {
|
||||
await this._verifyPinCode(pin, { shouldUpdateRealm: true });
|
||||
return true;
|
||||
}
|
||||
return true
|
||||
|
||||
} catch (error) {
|
||||
return this._handleFailedAttempt(error);
|
||||
}
|
||||
@ -551,7 +414,7 @@ class PinCodeContainer extends Component {
|
||||
intl,
|
||||
pinCodeParams: { isReset },
|
||||
} = this.props;
|
||||
const { informationText, isOldPinVerified, isExistUser } = this.state;
|
||||
const { informationText, isOldPinVerified } = this.state;
|
||||
|
||||
return (
|
||||
<PinCodeScreen
|
||||
@ -572,6 +435,7 @@ class PinCodeContainer extends Component {
|
||||
const mapStateToProps = (state) => ({
|
||||
currentAccount: state.account.currentAccount,
|
||||
applicationPinCode: state.application.pin,
|
||||
encUnlockPin: state.application.encUnlockPin,
|
||||
otherAccounts: state.account.otherAccounts,
|
||||
pinCodeParams: state.application.pinCodeNavigation,
|
||||
isBiometricEnabled: state.application.isBiometricEnabled,
|
@ -18,13 +18,10 @@ import {
|
||||
setLanguage as setLanguage2DB,
|
||||
setNsfw as setNsfw2DB,
|
||||
setTheme,
|
||||
setPinCodeOpen,
|
||||
removeUserData,
|
||||
removePinCode,
|
||||
setAuthStatus,
|
||||
setExistUser,
|
||||
removeAllUserData,
|
||||
getTheme,
|
||||
} from '../../../realm/realm';
|
||||
|
||||
// Services and Actions
|
||||
@ -38,17 +35,15 @@ import {
|
||||
openPinCodeModal,
|
||||
setNsfw,
|
||||
isPinCodeOpen,
|
||||
setPinCode as savePinCode,
|
||||
login,
|
||||
logoutDone,
|
||||
closePinCodeModal,
|
||||
setColorTheme,
|
||||
setIsBiometricEnabled,
|
||||
setEncryptedUnlockPin,
|
||||
} from '../../../redux/actions/applicationActions';
|
||||
import { toastNotification } from '../../../redux/actions/uiAction';
|
||||
import { setPushToken, getNodes } from '../../../providers/ecency/ecency';
|
||||
import { checkClient } from '../../../providers/hive/dhive';
|
||||
import { updatePinCode } from '../../../providers/hive/auth';
|
||||
import { removeOtherAccount, updateCurrentAccount } from '../../../redux/actions/accountAction';
|
||||
// Middleware
|
||||
|
||||
@ -226,7 +221,7 @@ class SettingsContainer extends Component {
|
||||
if (action) {
|
||||
dispatch(
|
||||
openPinCodeModal({
|
||||
callback: () => this._setDefaultPinCode(action),
|
||||
callback: () => this._enableDefaultUnlockPin(action),
|
||||
isReset: true,
|
||||
isOldPinVerified: true,
|
||||
oldPinCode: Config.DEFAULT_PIN,
|
||||
@ -235,7 +230,7 @@ class SettingsContainer extends Component {
|
||||
} else {
|
||||
dispatch(
|
||||
openPinCodeModal({
|
||||
callback: () => this._setDefaultPinCode(action),
|
||||
callback: () => this._enableDefaultUnlockPin(action),
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -403,7 +398,6 @@ class SettingsContainer extends Component {
|
||||
}
|
||||
dispatch(logoutDone());
|
||||
dispatch(isPinCodeOpen(false));
|
||||
setPinCodeOpen(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('Failed to remove user data', err);
|
||||
@ -428,45 +422,21 @@ class SettingsContainer extends Component {
|
||||
}, 500);
|
||||
};
|
||||
|
||||
_setDefaultPinCode = (action) => {
|
||||
const { dispatch, username, currentAccount, pinCode } = this.props;
|
||||
|
||||
if (!action) {
|
||||
const oldPinCode = decryptKey(pinCode, Config.PIN_KEY, this._onDecryptFail);
|
||||
_enableDefaultUnlockPin = (isEnabled) => {
|
||||
const { dispatch, encUnlockPin } = this.props;
|
||||
|
||||
dispatch(isPinCodeOpen(isEnabled));
|
||||
|
||||
if (!isEnabled) {
|
||||
const oldPinCode = decryptKey(encUnlockPin, Config.PIN_KEY, this._onDecryptFail);
|
||||
|
||||
if (oldPinCode === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pinData = {
|
||||
pinCode: Config.DEFAULT_PIN,
|
||||
username,
|
||||
oldPinCode,
|
||||
};
|
||||
updatePinCode(pinData)
|
||||
.then((response) => {
|
||||
const _currentAccount = currentAccount;
|
||||
_currentAccount.local = response;
|
||||
|
||||
dispatch(
|
||||
updateCurrentAccount({
|
||||
..._currentAccount,
|
||||
}),
|
||||
);
|
||||
|
||||
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
|
||||
dispatch(savePinCode(encryptedPin));
|
||||
|
||||
setPinCodeOpen(action);
|
||||
dispatch(isPinCodeOpen(action));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('pin update failure: ', err);
|
||||
this._onDecryptFail();
|
||||
});
|
||||
} else {
|
||||
setPinCodeOpen(action);
|
||||
dispatch(isPinCodeOpen(action));
|
||||
dispatch(setEncryptedUnlockPin(encryptedPin));
|
||||
}
|
||||
};
|
||||
|
||||
@ -492,8 +462,8 @@ const mapStateToProps = (state) => ({
|
||||
isDarkTheme: state.application.isDarkTheme,
|
||||
colorTheme: state.application.colorTheme,
|
||||
isPinCodeOpen: state.application.isPinCodeOpen,
|
||||
encUnlockPin: state.application.encUnlockPin,
|
||||
isBiometricEnabled: state.application.isBiometricEnabled,
|
||||
pinCode: state.application.pin,
|
||||
isDefaultFooter: state.application.isDefaultFooter,
|
||||
isLoggedIn: state.application.isLoggedIn,
|
||||
isNotificationSettingsOpen: state.application.isNotificationOpen,
|
@ -40,7 +40,7 @@ class HiveSigner extends PureComponent {
|
||||
if (!isLoading) {
|
||||
this.setState({ isLoading: true });
|
||||
handleOnModalClose();
|
||||
loginWithSC2(code[1], isPinCodeOpen)
|
||||
loginWithSC2(code[1])
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
const persistAccountData = persistAccountGenerator(result);
|
||||
|
179
src/utils/migrationHelpers.ts
Normal file
179
src/utils/migrationHelpers.ts
Normal file
@ -0,0 +1,179 @@
|
||||
import { Appearance } from 'react-native';
|
||||
import Config from 'react-native-config';
|
||||
|
||||
// Constants
|
||||
import THEME_OPTIONS from '../constants/options/theme';
|
||||
import { getUnreadNotificationCount } from '../providers/ecency/ecency';
|
||||
import { getPointsSummary } from '../providers/ecency/ePoint';
|
||||
import { migrateToMasterKeyWithAccessToken, refreshSCToken, updatePinCode } from '../providers/hive/auth';
|
||||
import { getMutes } from '../providers/hive/dhive';
|
||||
import AUTH_TYPE from '../constants/authType';
|
||||
|
||||
// Services
|
||||
import {
|
||||
getSettings, getUserDataWithUsername,
|
||||
} from '../realm/realm';
|
||||
import { updateCurrentAccount } from '../redux/actions/accountAction';
|
||||
|
||||
import {
|
||||
isDarkTheme,
|
||||
changeNotificationSettings,
|
||||
changeAllNotificationSettings,
|
||||
setApi,
|
||||
setCurrency,
|
||||
setLanguage,
|
||||
setUpvotePercent,
|
||||
setNsfw,
|
||||
isDefaultFooter,
|
||||
isPinCodeOpen,
|
||||
setColorTheme,
|
||||
setSettingsMigrated,
|
||||
setPinCode,
|
||||
setEncryptedUnlockPin,
|
||||
} from '../redux/actions/applicationActions';
|
||||
import { fetchSubscribedCommunities } from '../redux/actions/communitiesAction';
|
||||
import {
|
||||
hideActionModal,
|
||||
hideProfileModal,
|
||||
setRcOffer,
|
||||
toastNotification,
|
||||
} from '../redux/actions/uiAction';
|
||||
import { decryptKey, encryptKey } from './crypto';
|
||||
|
||||
|
||||
//migrates settings from realm to redux once and do no user realm for settings again;
|
||||
export const migrateSettings = async (dispatch: any, settingsMigratedV2: boolean) => {
|
||||
|
||||
if (settingsMigratedV2) {
|
||||
return;
|
||||
}
|
||||
|
||||
//reset certain properties
|
||||
dispatch(hideActionModal());
|
||||
dispatch(hideProfileModal());
|
||||
dispatch(toastNotification(''));
|
||||
dispatch(setRcOffer(false));
|
||||
|
||||
|
||||
const settings = await getSettings();
|
||||
|
||||
if (settings) {
|
||||
const isDarkMode = Appearance.getColorScheme() === 'dark';
|
||||
dispatch(isDarkTheme(settings.isDarkTheme !== null ? settings.isDarkTheme : isDarkMode));
|
||||
dispatch(setColorTheme(THEME_OPTIONS.findIndex(item => item.value === settings.isDarkTheme)));
|
||||
await dispatch(isPinCodeOpen(!!settings.isPinCodeOpen));
|
||||
if (settings.language !== '') dispatch(setLanguage(settings.language));
|
||||
if (settings.server !== '') dispatch(setApi(settings.server));
|
||||
if (settings.upvotePercent !== '') {
|
||||
dispatch(setUpvotePercent(Number(settings.upvotePercent)));
|
||||
}
|
||||
if (settings.isDefaultFooter !== '') dispatch(isDefaultFooter(settings.isDefaultFooter)); //TODO: remove as not being used
|
||||
|
||||
|
||||
if (settings.nsfw !== '') dispatch(setNsfw(settings.nsfw));
|
||||
|
||||
dispatch(setCurrency(settings.currency !== '' ? settings.currency : 'usd'));
|
||||
|
||||
if (settings.notification !== '') {
|
||||
dispatch(
|
||||
changeNotificationSettings({
|
||||
type: 'notification',
|
||||
action: settings.notification,
|
||||
}),
|
||||
);
|
||||
|
||||
dispatch(changeAllNotificationSettings(settings));
|
||||
}
|
||||
|
||||
await dispatch(setSettingsMigrated(true))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//migrates local user data to use default pin encruption instead of user pin encryption
|
||||
export const migrateUserEncryption = async (dispatch, currentAccount, encUserPin, onFailure) => {
|
||||
|
||||
const oldPinCode = decryptKey(encUserPin, Config.PIN_KEY);
|
||||
|
||||
if (oldPinCode === undefined || oldPinCode === Config.DEFAULT_PIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try{
|
||||
const pinData = {
|
||||
pinCode: Config.DEFAULT_PIN,
|
||||
username: currentAccount.username,
|
||||
oldPinCode,
|
||||
};
|
||||
|
||||
const response = updatePinCode(pinData)
|
||||
|
||||
const _currentAccount = currentAccount;
|
||||
_currentAccount.local = response;
|
||||
|
||||
dispatch(
|
||||
updateCurrentAccount({
|
||||
..._currentAccount,
|
||||
}),
|
||||
);
|
||||
|
||||
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
|
||||
dispatch(setPinCode(encryptedPin));
|
||||
|
||||
} catch(err){
|
||||
console.warn('pin update failure: ', err);
|
||||
}
|
||||
|
||||
|
||||
dispatch(setEncryptedUnlockPin(encUserPin))
|
||||
|
||||
const realmData = await getUserDataWithUsername(currentAccount.name)
|
||||
|
||||
let _currentAccount = currentAccount;
|
||||
_currentAccount.username = _currentAccount.name;
|
||||
_currentAccount.local = realmData[0];
|
||||
|
||||
try {
|
||||
const pinHash = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
|
||||
//migration script for previously mast key based logged in user not having access token
|
||||
if (
|
||||
realmData[0].authType !== AUTH_TYPE.STEEM_CONNECT &&
|
||||
realmData[0].accessToken === ''
|
||||
) {
|
||||
_currentAccount = await migrateToMasterKeyWithAccessToken(
|
||||
_currentAccount,
|
||||
realmData[0],
|
||||
pinHash,
|
||||
);
|
||||
}
|
||||
|
||||
//refresh access token
|
||||
const encryptedAccessToken = await refreshSCToken(_currentAccount.local, Config.DEFAULT_PIN);
|
||||
_currentAccount.local.accessToken = encryptedAccessToken;
|
||||
} catch (error) {
|
||||
onFailure(error)
|
||||
}
|
||||
|
||||
//get unread notifications
|
||||
try {
|
||||
_currentAccount.unread_activity_count = await getUnreadNotificationCount();
|
||||
_currentAccount.pointsSummary = await getPointsSummary(_currentAccount.username);
|
||||
_currentAccount.mutes = await getMutes(_currentAccount.username);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'Optional user data fetch failed, account can still function without them',
|
||||
err,
|
||||
);
|
||||
}
|
||||
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
dispatch(fetchSubscribedCommunities(_currentAccount.username));
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
migrateSettings,
|
||||
migrateUserEncryption
|
||||
}
|
Loading…
Reference in New Issue
Block a user