Merge pull request #2398 from ecency/nt/pin-encryption

Nt/pin encryption
This commit is contained in:
Feruz M 2022-07-28 11:08:32 +03:00 committed by GitHub
commit a4d8edc311
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 387 additions and 441 deletions

View File

@ -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);

View File

@ -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
})

View File

@ -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';

View File

@ -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;
}

View File

@ -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
}

View File

@ -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,

View File

@ -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,
});

View File

@ -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,

View File

@ -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,

View File

@ -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);

View 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
}