import React from 'react'; import { StringUtils, ToastUtils, UserUtils } from '../../../session/utils'; import { ConversationController } from '../../../session/conversations'; import { removeAll } from '../../../data/data'; import { SignUpTab } from './SignUpTab'; import { SignInTab } from './SignInTab'; import { TabLabel, TabType } from './TabLabel'; import { PasswordUtil } from '../../../util'; import { trigger } from '../../../shims/events'; import { AccountManager, sessionGenerateKeyPair, } from '../../../util/accountManager'; import { fromHex, fromHexToArray } from '../../../session/utils/String'; export const MAX_USERNAME_LENGTH = 20; // tslint:disable: use-simple-attributes interface State { selectedTab: TabType; generatedRecoveryPhrase: string; hexGeneratedPubKey: string; } export function validatePassword(password: string, verifyPassword: string) { const trimmedPassword = password.trim(); const trimmedVerifyPassword = verifyPassword.trim(); // If user hasn't set a value then skip if (!trimmedPassword && !trimmedVerifyPassword) { return { passwordErrorString: '', passwordFieldsMatch: true, }; } const error = PasswordUtil.validatePassword(trimmedPassword, window.i18n); if (error) { return { passwordErrorString: error, passwordFieldsMatch: true, }; } if (trimmedPassword !== trimmedVerifyPassword) { return { passwordErrorString: '', passwordFieldsMatch: false, }; } return { passwordErrorString: '', passwordFieldsMatch: true, }; } export async function resetRegistration() { await removeAll(); await window.storage.reset(); await window.storage.fetch(); ConversationController.getInstance().reset(); await ConversationController.getInstance().load(); } export async function signUp(signUpDetails: { displayName: string; generatedRecoveryPhrase: string; password: string; verifyPassword: string; }) { const { displayName, password, verifyPassword, generatedRecoveryPhrase, } = signUpDetails; window.log.info('SIGNING UP'); const trimName = displayName.trim(); if (!trimName) { window.log.warn('invalid trimmed name for registration'); ToastUtils.pushToastError( 'invalidDisplayName', window.i18n('displayNameEmpty') ); return; } const passwordErrors = validatePassword(password, verifyPassword); if (passwordErrors.passwordErrorString) { window.log.warn('invalid password for registration'); ToastUtils.pushToastError( 'invalidPassword', window.i18n('invalidPassword') ); return; } if (!!password && !passwordErrors.passwordFieldsMatch) { window.log.warn('passwords does not match for registration'); ToastUtils.pushToastError( 'invalidPassword', window.i18n('passwordsDoNotMatch') ); return; } try { await resetRegistration(); await window.setPassword(password); UserUtils.setRestoringFromSeed(false); await AccountManager.registerSingleDevice( generatedRecoveryPhrase, 'english', trimName ); await UserUtils.setLastProfileUpdateTimestamp(Date.now()); trigger('openInbox'); } catch (e) { ToastUtils.pushToastError( 'registrationError', `Error: ${e.message || 'Something went wrong'}` ); window.log.warn('exception during registration:', e); } } /** * Sign in/restore from seed. * Ask for a display name, as we will drop incoming ConfigurationMessages if any are saved on the swarm. * We will handle a ConfigurationMessage */ export async function signInWithRecovery(signInDetails: { displayName: string; userRecoveryPhrase: string; password: string; verifyPassword: string; }) { const { displayName, password, verifyPassword, userRecoveryPhrase, } = signInDetails; window.log.info('RESTORING FROM SEED'); const trimName = displayName.trim(); if (!trimName) { window.log.warn('invalid trimmed name for registration'); ToastUtils.pushToastError( 'invalidDisplayName', window.i18n('displayNameEmpty') ); return; } const passwordErrors = validatePassword(password, verifyPassword); if (passwordErrors.passwordErrorString) { window.log.warn('invalid password for registration'); ToastUtils.pushToastError( 'invalidPassword', window.i18n('invalidPassword') ); return; } if (!!password && !passwordErrors.passwordFieldsMatch) { window.log.warn('passwords does not match for registration'); ToastUtils.pushToastError( 'invalidPassword', window.i18n('passwordsDoNotMatch') ); return; } try { await resetRegistration(); await window.setPassword(password); UserUtils.setRestoringFromSeed(false); await AccountManager.registerSingleDevice( userRecoveryPhrase, 'english', trimName ); await UserUtils.setLastProfileUpdateTimestamp(Date.now()); trigger('openInbox'); } catch (e) { ToastUtils.pushToastError( 'registrationError', `Error: ${e.message || 'Something went wrong'}` ); window.log.warn('exception during registration:', e); } } export class RegistrationTabs extends React.Component { constructor() { super({}); this.state = { selectedTab: TabType.SignUp, generatedRecoveryPhrase: '', hexGeneratedPubKey: '', }; } public componentDidMount() { void this.generateMnemonicAndKeyPair(); void resetRegistration(); } public render() { const { selectedTab } = this.state; return (
{this.renderSections()}
); } private async generateMnemonicAndKeyPair() { if (this.state.generatedRecoveryPhrase === '') { const language = 'english'; const mnemonic = await AccountManager.generateMnemonic(language); let seedHex = window.mnemonic.mn_decode(mnemonic, language); // handle shorter than 32 bytes seeds const privKeyHexLength = 32 * 2; if (seedHex.length !== privKeyHexLength) { seedHex = seedHex.concat('0'.repeat(32)); seedHex = seedHex.substring(0, privKeyHexLength); } const seed = fromHex(seedHex); const keyPair = await sessionGenerateKeyPair(seed); const hexGeneratedPubKey = StringUtils.decode(keyPair.pubKey, 'hex'); this.setState({ generatedRecoveryPhrase: mnemonic, hexGeneratedPubKey, // our 'frontend' sessionID }); } } private readonly handleTabSelect = (tabType: TabType): void => { this.setState({ selectedTab: tabType, }); }; private renderSections() { const { selectedTab, generatedRecoveryPhrase, hexGeneratedPubKey, } = this.state; if (selectedTab === TabType.SignUp) { return ( ); } return ; } }