mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-20 11:51:52 +03:00
commit
d49d6918f9
@ -323,7 +323,10 @@
|
||||
"enter_text": "Enter PIN to unlock",
|
||||
"set_new": "Set new PIN",
|
||||
"write_again": "Write again",
|
||||
"forgot_text": "Oh, I forgot it..."
|
||||
"forgot_text": "Oh, I forgot it...",
|
||||
"pin_not_matched":"PIN do not match, Please try again.",
|
||||
"attempts_postfix":"failed attempt(s)",
|
||||
"message_reset_warning":"User data will be wiped on next failed attempt"
|
||||
},
|
||||
"alert": {
|
||||
"success": "Success!",
|
||||
@ -357,6 +360,7 @@
|
||||
"remove_alert": "Are you sure you want to remove?",
|
||||
"clear_alert": "Are you sure you want to clear?",
|
||||
"clear_user_alert": "Are you sure you want to clear all user data?",
|
||||
"decrypt_fail_alert":"Corrupt app state, please relogin to reset state.",
|
||||
"clear": "Clear",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
|
@ -97,6 +97,7 @@ class InAppPurchaseContainer extends Component {
|
||||
}),
|
||||
);
|
||||
} else if (get(error, 'responseCode') !== '2') {
|
||||
console.warn('failed puchase:', error);
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
|
@ -29,7 +29,11 @@ export const getCurrencyTokenRate = (currency, token) =>
|
||||
/**
|
||||
* @params username
|
||||
*/
|
||||
export const getDrafts = (username) => api.get(`/drafts/${username}`).then((resp) => resp.data);
|
||||
export const getDrafts = (username) =>
|
||||
api
|
||||
.get(`/drafts/${username}`)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
/*export const getDrafts = data =>
|
||||
new Promise((resolve, reject) => {
|
||||
@ -114,7 +118,8 @@ export const addBookmark = (username, author, permlink) =>
|
||||
permlink,
|
||||
chain: 'hive',
|
||||
})
|
||||
.then((resp) => resp.data);
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
export const addReport = (url) =>
|
||||
api
|
||||
@ -127,7 +132,10 @@ export const addReport = (url) =>
|
||||
* @params current username
|
||||
*/
|
||||
export const getBookmarks = (username) =>
|
||||
api.get(`/bookmarks/${username}`).then((resp) => resp.data);
|
||||
api
|
||||
.get(`/bookmarks/${username}`)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
/**
|
||||
* @params id
|
||||
@ -139,14 +147,20 @@ export const removeBookmark = (username, id) => api.delete(`/bookmarks/${usernam
|
||||
* @params current username
|
||||
*/
|
||||
export const getFavorites = (username) =>
|
||||
api.get(`/favorites/${username}`).then((resp) => resp.data);
|
||||
api
|
||||
.get(`/favorites/${username}`)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
/**
|
||||
* @params current username
|
||||
* @params target username
|
||||
*/
|
||||
export const getIsFavorite = (targetUsername, currentUsername) =>
|
||||
api.get(`/isfavorite/${currentUsername}/${targetUsername}`).then((resp) => resp.data);
|
||||
api
|
||||
.get(`/isfavorite/${currentUsername}/${targetUsername}`)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
/**
|
||||
* @params current username
|
||||
@ -158,7 +172,8 @@ export const addFavorite = (currentUsername, targetUsername) =>
|
||||
username: currentUsername,
|
||||
account: targetUsername,
|
||||
})
|
||||
.then((resp) => resp.data);
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
/**
|
||||
* @params current username
|
||||
@ -171,7 +186,10 @@ export const removeFavorite = (currentUsername, targetUsername) =>
|
||||
* @params current username
|
||||
*/
|
||||
export const getSnippets = (username) =>
|
||||
api.get(`/fragments/${username}`).then((resp) => resp.data);
|
||||
api
|
||||
.get(`/fragments/${username}`)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
/**
|
||||
* @params current username
|
||||
@ -185,7 +203,8 @@ export const addSnippet = (currentUsername, title, body) =>
|
||||
title,
|
||||
body,
|
||||
})
|
||||
.then((resp) => resp.data);
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
/**
|
||||
* @params current username
|
||||
@ -413,10 +432,14 @@ export const schedule = (
|
||||
options,
|
||||
reblog: 0,
|
||||
})
|
||||
.then((resp) => resp.data);
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
export const getSchedules = (username) =>
|
||||
api.get(`/schedules/${username}`).then((resp) => resp.data);
|
||||
api
|
||||
.get(`/schedules/${username}`)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
export const removeSchedule = (username, id) => api.delete(`/schedules/${username}/${id}`);
|
||||
|
||||
@ -425,7 +448,11 @@ export const moveSchedule = (id, username) => api.put(`/schedules/${username}/${
|
||||
// Old image service
|
||||
// Images
|
||||
|
||||
export const getImages = (username) => api.get(`api/images/${username}`).then((resp) => resp.data);
|
||||
export const getImages = (username) =>
|
||||
api
|
||||
.get(`api/images/${username}`)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
export const addMyImage = (user, url) =>
|
||||
api.post('/image', {
|
||||
@ -467,11 +494,14 @@ export const getNodes = () => serverList.get().then((resp) => resp.data.hived ||
|
||||
export const getSCAccessToken = (code) =>
|
||||
new Promise((resolve, reject) => {
|
||||
ecencyApi
|
||||
.post('/hs-token-refresh', {
|
||||
.post('/auth-api/hs-token-refresh', {
|
||||
code,
|
||||
})
|
||||
.then((resp) => resolve(resp.data))
|
||||
.catch((e) => reject(e));
|
||||
.catch((e) => {
|
||||
bugsnag.notify(e);
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
|
||||
export const getPromotePosts = () => {
|
||||
@ -479,11 +509,16 @@ export const getPromotePosts = () => {
|
||||
console.log('Fetching promoted posts');
|
||||
return api.get('/promoted-posts?limit=10').then((resp) => resp.data);
|
||||
} catch (error) {
|
||||
bugsnag.notify(error);
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
export const purchaseOrder = (data) => api.post('/purchase-order', data).then((resp) => resp.data);
|
||||
export const purchaseOrder = (data) =>
|
||||
api
|
||||
.post('/purchase-order', data)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
export const getPostReblogs = (data) =>
|
||||
api
|
||||
@ -492,4 +527,7 @@ export const getPostReblogs = (data) =>
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
||||
export const register = (data) =>
|
||||
api.post('/signup/account-create', data).then((resp) => resp.data);
|
||||
api
|
||||
.post('/signup/account-create', data)
|
||||
.then((resp) => resp.data)
|
||||
.catch((error) => bugsnag.notify(error));
|
||||
|
@ -203,6 +203,7 @@ export const setUserDataWithPinCode = async (data) => {
|
||||
|
||||
return updatedUserData;
|
||||
} catch (error) {
|
||||
console.warn('Failed to set user data with pin: ', data, error);
|
||||
return Promise.reject(new Error('auth.unknow_error'));
|
||||
}
|
||||
};
|
||||
@ -212,72 +213,98 @@ export const updatePinCode = (data) =>
|
||||
let currentUser = null;
|
||||
try {
|
||||
setPinCode(get(data, 'pinCode'));
|
||||
getUserData().then(async (users) => {
|
||||
if (users && users.length > 0) {
|
||||
await users.forEach((userData) => {
|
||||
if (
|
||||
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
|
||||
get(userData, 'authType', '') === AUTH_TYPE.ACTIVE_KEY ||
|
||||
get(userData, 'authType', '') === AUTH_TYPE.MEMO_KEY ||
|
||||
get(userData, 'authType', '') === AUTH_TYPE.POSTING_KEY
|
||||
) {
|
||||
const publicKey =
|
||||
get(userData, 'masterKey') ||
|
||||
get(userData, 'activeKey') ||
|
||||
get(userData, 'memoKey') ||
|
||||
get(userData, 'postingKey');
|
||||
getUserData()
|
||||
.then(async (users) => {
|
||||
const _onDecryptError = () => {
|
||||
throw new Error('Decryption failed');
|
||||
};
|
||||
if (users && users.length > 0) {
|
||||
users.forEach((userData) => {
|
||||
if (
|
||||
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
|
||||
get(userData, 'authType', '') === AUTH_TYPE.ACTIVE_KEY ||
|
||||
get(userData, 'authType', '') === AUTH_TYPE.MEMO_KEY ||
|
||||
get(userData, 'authType', '') === AUTH_TYPE.POSTING_KEY
|
||||
) {
|
||||
const publicKey =
|
||||
get(userData, 'masterKey') ||
|
||||
get(userData, 'activeKey') ||
|
||||
get(userData, 'memoKey') ||
|
||||
get(userData, 'postingKey');
|
||||
|
||||
data.password = decryptKey(publicKey, get(data, 'oldPinCode', ''));
|
||||
} else if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
|
||||
data.accessToken = decryptKey(
|
||||
get(userData, 'accessToken'),
|
||||
get(data, 'oldPinCode', ''),
|
||||
);
|
||||
}
|
||||
const updatedUserData = getUpdatedUserData(userData, data);
|
||||
updateUserData(updatedUserData);
|
||||
if (userData.username === data.username) {
|
||||
currentUser = updatedUserData;
|
||||
}
|
||||
});
|
||||
resolve(currentUser);
|
||||
}
|
||||
});
|
||||
const password = decryptKey(
|
||||
publicKey,
|
||||
get(data, 'oldPinCode', ''),
|
||||
_onDecryptError,
|
||||
);
|
||||
if (password === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.password = password;
|
||||
} else if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
|
||||
const accessToken = decryptKey(
|
||||
get(userData, 'accessToken'),
|
||||
get(data, 'oldPinCode', ''),
|
||||
_onDecryptError,
|
||||
);
|
||||
if (accessToken === undefined) {
|
||||
return;
|
||||
}
|
||||
data.accessToken = accessToken;
|
||||
}
|
||||
const updatedUserData = getUpdatedUserData(userData, data);
|
||||
updateUserData(updatedUserData);
|
||||
if (userData.username === data.username) {
|
||||
currentUser = updatedUserData;
|
||||
}
|
||||
});
|
||||
resolve(currentUser);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
export const verifyPinCode = async (data) => {
|
||||
const pinHash = await getPinCode();
|
||||
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'));
|
||||
// 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'));
|
||||
}
|
||||
await setPinCode(get(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 (result.length > 0) {
|
||||
if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
|
||||
await refreshSCToken(userData, get(data, 'pinCode'));
|
||||
if (sha256(get(data, 'pinCode')).toString() !== pinHash) {
|
||||
return Promise.reject(new Error('auth.invalid_pin'));
|
||||
}
|
||||
|
||||
if (result.length > 0) {
|
||||
if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
|
||||
await refreshSCToken(userData, get(data, 'pinCode'));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.warn('Failed to verify pin in auth: ', data, err);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const refreshSCToken = async (userData, pinCode) => {
|
||||
|
@ -47,6 +47,7 @@ export const getUserDataWithUsername = async (username) => {
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.warn('Failed to get user data: ', error);
|
||||
return error;
|
||||
}
|
||||
};
|
||||
@ -236,6 +237,7 @@ export const getPinCode = async () => {
|
||||
}
|
||||
return '';
|
||||
} catch (error) {
|
||||
console.warn('Failed get auth from storage: ', error);
|
||||
return error;
|
||||
}
|
||||
};
|
||||
@ -508,6 +510,7 @@ export const getSettings = async () => {
|
||||
mentionNotification: true,
|
||||
reblogNotification: true,
|
||||
transfersNotification: true,
|
||||
isPinCodeOpen: false,
|
||||
};
|
||||
await setItemToStorage(SETTINGS_SCHEMA, settingData);
|
||||
return settingData;
|
||||
|
@ -45,6 +45,7 @@ const Application = () => {
|
||||
isFullScreen
|
||||
swipeToClose={false}
|
||||
backButtonClose={false}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<PinCode />
|
||||
</Modal>
|
||||
@ -53,6 +54,7 @@ const Application = () => {
|
||||
isFullScreen
|
||||
swipeToClose={false}
|
||||
backButtonClose={false}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<WelcomeScreen handleButtonPress={handleWelcomeModalButtonPress} />
|
||||
</Modal>
|
||||
|
@ -4,6 +4,7 @@ import scalePx from '../../../utils/scalePx';
|
||||
export default EStyleSheet.create({
|
||||
safeAreaView: {
|
||||
flex: 1,
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
},
|
||||
container: {
|
||||
flex: 0.95,
|
||||
|
@ -10,6 +10,7 @@ import { navigate } from '../../../navigation/service';
|
||||
import { setUserDataWithPinCode, verifyPinCode, updatePinCode } from '../../../providers/hive/auth';
|
||||
import {
|
||||
closePinCodeModal,
|
||||
isPinCodeOpen,
|
||||
login,
|
||||
logoutDone,
|
||||
setPinCode as savePinCode,
|
||||
@ -21,6 +22,7 @@ import {
|
||||
removeAllUserData,
|
||||
removePinCode,
|
||||
setAuthStatus,
|
||||
setPinCodeOpen,
|
||||
} from '../../../realm/realm';
|
||||
import { updateCurrentAccount, removeOtherAccount } from '../../../redux/actions/accountAction';
|
||||
import { getUser } from '../../../providers/hive/dhive';
|
||||
@ -37,9 +39,10 @@ class PinCodeContainer extends Component {
|
||||
this.state = {
|
||||
isExistUser: null,
|
||||
informationText: '',
|
||||
pinCode: null,
|
||||
newPinCode: null,
|
||||
isOldPinVerified: get(props.pinCodeParams, 'isOldPinVerified', false),
|
||||
oldPinCode: get(props.pinCodeParams, 'oldPinCode', null),
|
||||
failedAttempts: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -48,9 +51,9 @@ class PinCodeContainer extends Component {
|
||||
componentDidMount() {
|
||||
this._getDataFromStorage().then(() => {
|
||||
const { intl } = this.props;
|
||||
const { isExistUser } = this.state;
|
||||
const { isOldPinVerified } = this.state;
|
||||
|
||||
if (isExistUser) {
|
||||
if (!isOldPinVerified) {
|
||||
this.setState({
|
||||
informationText: intl.formatMessage({
|
||||
id: 'pincode.enter_text',
|
||||
@ -78,6 +81,26 @@ class PinCodeContainer extends Component {
|
||||
});
|
||||
});
|
||||
|
||||
//this function updates realm with appropriate master key required for encyrption
|
||||
//this function is important: must run while chaning pin
|
||||
//and even logging in with existing pin code
|
||||
_updatePinCodeRealm = async (pinData) => {
|
||||
try {
|
||||
const { currentAccount, dispatch } = this.props;
|
||||
const response = await updatePinCode(pinData);
|
||||
if (!response) {
|
||||
return false;
|
||||
}
|
||||
const _currentAccount = currentAccount;
|
||||
_currentAccount.local = response;
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
return true;
|
||||
} catch (err) {
|
||||
this._onDecryptFail();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
_resetPinCode = (pin) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const {
|
||||
@ -86,7 +109,7 @@ class PinCodeContainer extends Component {
|
||||
pinCodeParams: { navigateTo, navigateParams, accessToken, callback },
|
||||
intl,
|
||||
} = this.props;
|
||||
const { isOldPinVerified, oldPinCode } = this.state;
|
||||
const { isOldPinVerified, oldPinCode, newPinCode } = this.state;
|
||||
|
||||
const pinData = {
|
||||
pinCode: pin,
|
||||
@ -96,27 +119,58 @@ class PinCodeContainer extends Component {
|
||||
oldPinCode,
|
||||
};
|
||||
|
||||
//if old pin already verified, check new pin setup conditions.
|
||||
if (isOldPinVerified) {
|
||||
updatePinCode(pinData).then((response) => {
|
||||
const _currentAccount = currentAccount;
|
||||
_currentAccount.local = response;
|
||||
//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,
|
||||
params: navigateParams,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
this._savePinCode(pin);
|
||||
|
||||
if (callback) {
|
||||
callback(pin, oldPinCode);
|
||||
}
|
||||
dispatch(closePinCodeModal());
|
||||
if (navigateTo) {
|
||||
navigate({
|
||||
routeName: navigateTo,
|
||||
params: navigateParams,
|
||||
});
|
||||
}
|
||||
// if newPin code exists and above case failed, that means pins did not match
|
||||
// warn user about it and do nothing
|
||||
else if (newPinCode) {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'pincode.pin_not_matched',
|
||||
}),
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
}
|
||||
|
||||
//if newPinCode do yet exist, save it in state and prompt user to reneter pin.
|
||||
else {
|
||||
this.setState({
|
||||
informationText: intl.formatMessage({
|
||||
id: 'pincode.write_again',
|
||||
}),
|
||||
newPinCode: pin,
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
// if old pin code is not yet verified attempt to verify code
|
||||
else {
|
||||
verifyPinCode(pinData)
|
||||
.then(() => {
|
||||
this.setState({ isOldPinVerified: true });
|
||||
@ -124,12 +178,13 @@ class PinCodeContainer extends Component {
|
||||
informationText: intl.formatMessage({
|
||||
id: 'pincode.set_new',
|
||||
}),
|
||||
pinCode: null,
|
||||
newPinCode: null,
|
||||
oldPinCode: pin,
|
||||
});
|
||||
resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('Failed to verify pin code', err);
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
@ -183,13 +238,12 @@ class PinCodeContainer extends Component {
|
||||
});
|
||||
});
|
||||
|
||||
_verifyPinCode = (pin) =>
|
||||
_verifyPinCode = (pin, { shouldUpdateRealm } = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const {
|
||||
currentAccount,
|
||||
dispatch,
|
||||
pinCodeParams: { navigateTo, navigateParams, accessToken, callback },
|
||||
intl,
|
||||
} = this.props;
|
||||
const { oldPinCode } = this.state;
|
||||
|
||||
@ -204,11 +258,20 @@ class PinCodeContainer extends Component {
|
||||
.then(() => {
|
||||
this._savePinCode(pin);
|
||||
getUserDataWithUsername(currentAccount.name).then((realmData) => {
|
||||
const _currentAccount = currentAccount;
|
||||
_currentAccount.username = _currentAccount.name;
|
||||
[_currentAccount.local] = realmData;
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
dispatch(closePinCodeModal());
|
||||
if (shouldUpdateRealm) {
|
||||
this._updatePinCodeRealm(pinData).then(() => {
|
||||
dispatch(closePinCodeModal());
|
||||
});
|
||||
} else {
|
||||
const _currentAccount = currentAccount;
|
||||
_currentAccount.username = _currentAccount.name;
|
||||
[_currentAccount.local] = realmData;
|
||||
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
dispatch(closePinCodeModal());
|
||||
}
|
||||
|
||||
//on successful code verification run requested operation passed as props
|
||||
if (callback) {
|
||||
callback(pin, oldPinCode);
|
||||
}
|
||||
@ -218,17 +281,11 @@ class PinCodeContainer extends Component {
|
||||
params: navigateParams,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: err.message,
|
||||
}),
|
||||
);
|
||||
console.warn('code verification for login failed: ', err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
@ -239,22 +296,104 @@ class PinCodeContainer extends Component {
|
||||
dispatch(savePinCode(encryptedPin));
|
||||
};
|
||||
|
||||
_forgotPinCode = async () => {
|
||||
const { otherAccounts, dispatch } = this.props;
|
||||
|
||||
await removeAllUserData()
|
||||
.then(async () => {
|
||||
dispatch(updateCurrentAccount({}));
|
||||
dispatch(login(false));
|
||||
removePinCode();
|
||||
setAuthStatus({ isLoggedIn: false });
|
||||
setExistUser(false);
|
||||
if (otherAccounts.length > 0) {
|
||||
otherAccounts.map((item) => dispatch(removeOtherAccount(item.username)));
|
||||
}
|
||||
dispatch(logoutDone());
|
||||
dispatch(closePinCodeModal());
|
||||
dispatch(isPinCodeOpen(false));
|
||||
setPinCodeOpen(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('Failed to remove user data', err);
|
||||
});
|
||||
};
|
||||
|
||||
_handleFailedAttempt = (error) => {
|
||||
console.warn('Failed to set pin: ', error);
|
||||
const { intl } = this.props;
|
||||
const { failedAttempts } = this.state;
|
||||
//increment failed attempt
|
||||
const totalAttempts = failedAttempts + 1;
|
||||
|
||||
if (totalAttempts < 3) {
|
||||
//shwo failure alert box
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: error.message,
|
||||
}),
|
||||
);
|
||||
|
||||
let infoMessage = intl.formatMessage({
|
||||
id: 'pincode.enter_text',
|
||||
});
|
||||
infoMessage += `, ${totalAttempts} ${intl.formatMessage({ id: 'pincode.attempts_postfix' })}`;
|
||||
if (totalAttempts > 1) {
|
||||
infoMessage += `\n${intl.formatMessage({ id: 'pincode.message_reset_warning' })}`;
|
||||
}
|
||||
this.setState({
|
||||
failedAttempts: totalAttempts,
|
||||
informationText: infoMessage,
|
||||
});
|
||||
|
||||
return false;
|
||||
} else {
|
||||
//wipe user data
|
||||
this._forgotPinCode();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
_onDecryptFail = () => {
|
||||
const { intl } = this.props;
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'alert.decrypt_fail_alert',
|
||||
}),
|
||||
[
|
||||
{ text: intl.formatMessage({ id: 'alert.clear' }), onPress: () => this._forgotPinCode() },
|
||||
{ text: intl.formatMessage({ id: 'alert.cancel' }), style: 'destructive' },
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
_setPinCode = async (pin, isReset) => {
|
||||
const { intl, currentAccount, applicationPinCode } = this.props;
|
||||
const { isExistUser, pinCode } = this.state;
|
||||
const { isExistUser } = this.state;
|
||||
|
||||
try {
|
||||
const realmData = await getUserDataWithUsername(currentAccount.name);
|
||||
const userData = realmData[0];
|
||||
|
||||
// For exist users
|
||||
// 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);
|
||||
const verifiedPin = decryptKey(applicationPinCode, Config.PIN_KEY, this._onDecryptFail);
|
||||
if (verifiedPin === undefined) {
|
||||
return true;
|
||||
}
|
||||
if (verifiedPin === pin) {
|
||||
await this._setFirstPinCode(pin);
|
||||
} else {
|
||||
@ -273,47 +412,16 @@ class PinCodeContainer extends Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For new users
|
||||
if (pinCode === pin) {
|
||||
await this._setFirstPinCode(pin);
|
||||
//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;
|
||||
}
|
||||
} catch (error) {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: error.message,
|
||||
}),
|
||||
);
|
||||
return this._handleFailedAttempt(error);
|
||||
}
|
||||
|
||||
if (!pinCode) {
|
||||
// If the user is logging in for the first time, the user should set to pin
|
||||
await this.setState({
|
||||
informationText: intl.formatMessage({
|
||||
id: 'pincode.write_again',
|
||||
}),
|
||||
pinCode: pin,
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
await this.setState({
|
||||
informationText: intl.formatMessage({
|
||||
id: 'pincode.write_again',
|
||||
}),
|
||||
});
|
||||
await setTimeout(() => {
|
||||
this.setState({
|
||||
informationText: intl.formatMessage({
|
||||
id: 'pincode.set_new',
|
||||
}),
|
||||
pinCode: null,
|
||||
});
|
||||
return Promise.resolve();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
_handleForgotButton = () => {
|
||||
@ -333,38 +441,19 @@ class PinCodeContainer extends Component {
|
||||
);
|
||||
};
|
||||
|
||||
_forgotPinCode = async () => {
|
||||
const { otherAccounts, dispatch } = this.props;
|
||||
|
||||
await removeAllUserData()
|
||||
.then(async () => {
|
||||
dispatch(updateCurrentAccount({}));
|
||||
dispatch(login(false));
|
||||
removePinCode();
|
||||
setAuthStatus({ isLoggedIn: false });
|
||||
setExistUser(false);
|
||||
if (otherAccounts.length > 0) {
|
||||
otherAccounts.map((item) => dispatch(removeOtherAccount(item.username)));
|
||||
}
|
||||
dispatch(logoutDone());
|
||||
dispatch(closePinCodeModal());
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
currentAccount,
|
||||
intl,
|
||||
pinCodeParams: { isReset },
|
||||
} = this.props;
|
||||
const { informationText, isExistUser } = this.state;
|
||||
const { informationText, isOldPinVerified, isExistUser } = this.state;
|
||||
|
||||
return (
|
||||
<PinCodeScreen
|
||||
informationText={informationText}
|
||||
setPinCode={(pin) => this._setPinCode(pin, isReset)}
|
||||
showForgotButton={isExistUser}
|
||||
showForgotButton={!isOldPinVerified}
|
||||
username={currentAccount.name}
|
||||
intl={intl}
|
||||
handleForgotButton={() => this._handleForgotButton()}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
@ -18,28 +18,37 @@ const PinCodeScreen = ({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const intl = useIntl();
|
||||
|
||||
const _handleKeyboardOnPress = async (value) => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
if (value === 'clear') {
|
||||
setPin('');
|
||||
return;
|
||||
}
|
||||
const newPin = `${pin}${value}`;
|
||||
|
||||
if (pin.length < 3) {
|
||||
setPin(newPin);
|
||||
} else if (pin.length === 3) {
|
||||
await setPin(newPin);
|
||||
await setLoading(true);
|
||||
|
||||
await setPinCode(`${pin}${value}`);
|
||||
useEffect(() => {
|
||||
_handlePinComplete();
|
||||
}, [pin]);
|
||||
|
||||
const _handlePinComplete = async () => {
|
||||
if (pin.length === 4) {
|
||||
setLoading(true);
|
||||
await setPinCode(pin);
|
||||
setPin('');
|
||||
setLoading(false);
|
||||
} else if (pin.length > 3) {
|
||||
setPin(`${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
const _handleKeyboardOnPress = async (value) => {
|
||||
try {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
if (value === 'clear') {
|
||||
setPin('');
|
||||
return;
|
||||
}
|
||||
const newPin = `${pin}${value}`;
|
||||
|
||||
if (pin.length < 4) {
|
||||
setPin(newPin);
|
||||
} else if (pin.length >= 4) {
|
||||
setPin(`${value}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Failed to handle keyboard press as expected', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -26,6 +26,7 @@ export default EStyleSheet.create({
|
||||
},
|
||||
informationText: {
|
||||
color: '$editorButtonColor',
|
||||
textAlign: 'center',
|
||||
},
|
||||
informationView: {
|
||||
flex: 1,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import { Platform, Alert } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { Client } from '@hiveio/dhive';
|
||||
import VersionNumber from 'react-native-version-number';
|
||||
@ -17,6 +17,11 @@ import {
|
||||
setNsfw as setNsfw2DB,
|
||||
setTheme,
|
||||
setPinCodeOpen,
|
||||
removeUserData,
|
||||
removePinCode,
|
||||
setAuthStatus,
|
||||
setExistUser,
|
||||
removeAllUserData,
|
||||
} from '../../../realm/realm';
|
||||
|
||||
// Services and Actions
|
||||
@ -31,12 +36,15 @@ import {
|
||||
setNsfw,
|
||||
isPinCodeOpen,
|
||||
setPinCode as savePinCode,
|
||||
login,
|
||||
logoutDone,
|
||||
closePinCodeModal,
|
||||
} 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 { updateCurrentAccount } from '../../../redux/actions/accountAction';
|
||||
import { removeOtherAccount, updateCurrentAccount } from '../../../redux/actions/accountAction';
|
||||
// Middleware
|
||||
|
||||
// Constants
|
||||
@ -361,32 +369,82 @@ class SettingsContainer extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_clearUserData = async () => {
|
||||
const { otherAccounts, dispatch } = this.props;
|
||||
|
||||
await removeAllUserData()
|
||||
.then(async () => {
|
||||
dispatch(updateCurrentAccount({}));
|
||||
dispatch(login(false));
|
||||
removePinCode();
|
||||
setAuthStatus({ isLoggedIn: false });
|
||||
setExistUser(false);
|
||||
if (otherAccounts.length > 0) {
|
||||
otherAccounts.map((item) => dispatch(removeOtherAccount(item.username)));
|
||||
}
|
||||
dispatch(logoutDone());
|
||||
dispatch(isPinCodeOpen(false));
|
||||
setPinCodeOpen(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('Failed to remove user data', err);
|
||||
});
|
||||
};
|
||||
|
||||
_onDecryptFail = () => {
|
||||
const { intl } = this.props;
|
||||
setTimeout(() => {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'alert.warning',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'alert.decrypt_fail_alert',
|
||||
}),
|
||||
[
|
||||
{ text: intl.formatMessage({ id: 'alert.clear' }), onPress: () => this._clearUserData() },
|
||||
{ text: intl.formatMessage({ id: 'alert.cancel' }), style: 'destructive' },
|
||||
],
|
||||
);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
_setDefaultPinCode = (action) => {
|
||||
const { dispatch, username, currentAccount, pinCode } = this.props;
|
||||
|
||||
if (!action) {
|
||||
const oldPinCode = decryptKey(pinCode, Config.PIN_KEY);
|
||||
const oldPinCode = decryptKey(pinCode, 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;
|
||||
updatePinCode(pinData)
|
||||
.then((response) => {
|
||||
const _currentAccount = currentAccount;
|
||||
_currentAccount.local = response;
|
||||
|
||||
dispatch(
|
||||
updateCurrentAccount({
|
||||
..._currentAccount,
|
||||
}),
|
||||
);
|
||||
dispatch(
|
||||
updateCurrentAccount({
|
||||
..._currentAccount,
|
||||
}),
|
||||
);
|
||||
|
||||
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
|
||||
dispatch(savePinCode(encryptedPin));
|
||||
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
|
||||
dispatch(savePinCode(encryptedPin));
|
||||
|
||||
setPinCodeOpen(action);
|
||||
dispatch(isPinCodeOpen(action));
|
||||
});
|
||||
setPinCodeOpen(action);
|
||||
dispatch(isPinCodeOpen(action));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('pin update failure: ', err);
|
||||
this._onDecryptFail();
|
||||
});
|
||||
} else {
|
||||
setPinCodeOpen(action);
|
||||
dispatch(isPinCodeOpen(action));
|
||||
@ -427,7 +485,6 @@ const mapStateToProps = (state) => ({
|
||||
selectedApi: state.application.api,
|
||||
selectedCurrency: state.application.currency,
|
||||
selectedLanguage: state.application.language,
|
||||
|
||||
username: state.account.currentAccount && state.account.currentAccount.name,
|
||||
currentAccount: state.account.currentAccount,
|
||||
otherAccounts: state.account.otherAccounts,
|
||||
|
@ -1,7 +1,64 @@
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
export const encryptKey = (key, data) => CryptoJS.AES.encrypt(key, data).toString();
|
||||
const STAMP = '995a06d5-ee54-407f-bb8e-e4af2ab2fe01';
|
||||
|
||||
export const decryptKey = (key, data) => {
|
||||
return CryptoJS.AES.decrypt(key, data).toString(CryptoJS.enc.Utf8);
|
||||
export const encryptKey = (data, key) => {
|
||||
console.log('encrypting: ', data, key);
|
||||
const stampedData = getStampedData(data);
|
||||
const encJson = CryptoJS.AES.encrypt(JSON.stringify(stampedData), key).toString();
|
||||
let encData = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encJson));
|
||||
console.log('returning: ', encData);
|
||||
return encData;
|
||||
};
|
||||
|
||||
export const decryptKey = (data, key, onError) => {
|
||||
const legacyDecrypt = () => decryptKeyLegacy(data, key, onError);
|
||||
|
||||
try {
|
||||
const response = decryptKeyNew(data, key);
|
||||
return response;
|
||||
} catch (err) {
|
||||
console.warn('decryption with new method failed, trying legacy', err);
|
||||
return legacyDecrypt();
|
||||
}
|
||||
};
|
||||
|
||||
const decryptKeyNew = (data, key) => {
|
||||
console.log('decrypting new: ', data, key);
|
||||
let decData = CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8);
|
||||
let bytes = CryptoJS.AES.decrypt(decData, key).toString(CryptoJS.enc.Utf8);
|
||||
const stampedData = JSON.parse(bytes);
|
||||
const ret = processStampedData(stampedData);
|
||||
console.log('returning: ', ret);
|
||||
return ret;
|
||||
};
|
||||
|
||||
const decryptKeyLegacy = (data, key, onError) => {
|
||||
try {
|
||||
console.log('decrypting legacy ', data, key);
|
||||
const ret = CryptoJS.AES.decrypt(data, key).toString(CryptoJS.enc.Utf8);
|
||||
console.log('returning: ', ret);
|
||||
return ret;
|
||||
} catch (err) {
|
||||
console.warn('decryption with legacy failed as well');
|
||||
if (onError) {
|
||||
onError(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// stamping mechanism will help distinguish old legacy data and new encrypted data
|
||||
// second purpose is to avoid necrypting empty strings
|
||||
const getStampedData = (data) => {
|
||||
return {
|
||||
data,
|
||||
stamp: STAMP,
|
||||
};
|
||||
};
|
||||
|
||||
const processStampedData = (stampedData) => {
|
||||
if (stampedData.hasOwnProperty('stamp') && stampedData.stamp == STAMP) {
|
||||
return stampedData.data;
|
||||
}
|
||||
throw new Error('Possibly un-stamped legacy data');
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user