diff --git a/keyserver/src/creators/account-creator.js b/keyserver/src/creators/account-creator.js --- a/keyserver/src/creators/account-creator.js +++ b/keyserver/src/creators/account-creator.js @@ -94,9 +94,9 @@ ? request.deviceTokenUpdateRequest.deviceToken : viewer.deviceToken; const [id] = await createIDs('users', 1); - const newUserRow = [id, request.username, hash, time]; + const newUserRow = [id, request.username, hash, time, request.address]; const newUserQuery = SQL` - INSERT INTO users(id, username, hash, creation_time) + INSERT INTO users(id, username, hash, creation_time, ethereum_address) VALUES ${[newUserRow]} `; const [userViewerData] = await Promise.all([ @@ -178,8 +178,10 @@ return { id, + isNewAccount: true, rawMessageInfos, currentUserInfo, + serverTime: Date.now(), cookieChange: { threadInfos: threadsResult.threadInfos, userInfos: values(userInfos), diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -51,6 +51,7 @@ logInResponder, oldPasswordUpdateResponder, updateUserSettingsResponder, + siweResponder, } from './responders/user-responders'; import { codeVerificationResponder } from './responders/verification-responders'; import { uploadDeletionResponder } from './uploads/uploads'; @@ -84,6 +85,7 @@ send_password_reset_email: sendPasswordResetEmailResponder, send_verification_email: sendVerificationEmailResponder, set_thread_unread_status: threadSetUnreadStatusResponder, + siwe: siweResponder, update_account: passwordUpdateResponder, update_activity: updateActivityResponder, update_calendar_query: calendarQueryUpdateResponder, diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js --- a/keyserver/src/responders/user-responders.js +++ b/keyserver/src/responders/user-responders.js @@ -1,6 +1,7 @@ // @flow import invariant from 'invariant'; +import { SiweMessage, ErrorTypes } from 'siwe'; import t from 'tcomb'; import bcrypt from 'twin-bcrypt'; @@ -13,6 +14,8 @@ RegisterRequest, LogInResponse, LogInRequest, + SIWERequest, + SIWEResponse, UpdatePasswordRequest, UpdateUserSettingsRequest, } from 'lib/types/account-types'; @@ -193,55 +196,18 @@ source: t.maybe(t.enums.of(values(logInActionSources))), }); -async function logInResponder( - viewer: Viewer, - input: any, -): Promise { - await validateInput(viewer, logInRequestInputValidator, input); - const request: LogInRequest = input; - +async function logInQueries(viewer: Viewer, input: any, userId: string) { + const request: LogInRequest | SIWERequest = input; const calendarQuery = request.calendarQuery ? normalizeCalendarQuery(request.calendarQuery) : null; - const promises = {}; - if (calendarQuery) { - promises.verifyCalendarQueryThreadIDs = verifyCalendarQueryThreadIDs( - calendarQuery, - ); - } - const username = request.username ?? request.usernameOrEmail; - if (!username) { - throw new ServerError('invalid_parameters'); - } - const userQuery = SQL` - SELECT id, hash, username - FROM users - WHERE LCASE(username) = LCASE(${username}) - `; - promises.userQuery = dbQuery(userQuery); - const { - userQuery: [userResult], - } = await promiseAll(promises); - - if (userResult.length === 0) { - throw new ServerError('invalid_parameters'); - } - const userRow = userResult[0]; - if (!userRow.hash || !bcrypt.compareSync(request.password, userRow.hash)) { - if (hasMinCodeVersion(viewer.platformDetails, 99999)) { - throw new ServerError('invalid_parameters'); - } else { - throw new ServerError('invalid_credentials'); - } - } - const id = userRow.id.toString(); const newServerTime = Date.now(); const deviceToken = request.deviceTokenUpdateRequest ? request.deviceTokenUpdateRequest.deviceToken : viewer.deviceToken; const [userViewerData] = await Promise.all([ - createNewUserCookie(id, { + createNewUserCookie(userId, { platformDetails: request.platformDetails, deviceToken, }), @@ -290,6 +256,116 @@ return response; } +async function logInResponder( + viewer: Viewer, + input: any, +): Promise { + await validateInput(viewer, logInRequestInputValidator, input); + const request: LogInRequest = input; + + const calendarQuery = request.calendarQuery + ? normalizeCalendarQuery(request.calendarQuery) + : null; + const promises = {}; + if (calendarQuery) { + promises.verifyCalendarQueryThreadIDs = verifyCalendarQueryThreadIDs( + calendarQuery, + ); + } + const username = request.username ?? request.usernameOrEmail; + if (!username) { + throw new ServerError('invalid_parameters'); + } + const userQuery = SQL` + SELECT id, hash, username + FROM users + WHERE LCASE(username) = LCASE(${username}) + `; + promises.userQuery = dbQuery(userQuery); + const { + userQuery: [userResult], + } = await promiseAll(promises); + + if (userResult.length === 0) { + throw new ServerError('invalid_parameters'); + } + const userRow = userResult[0]; + if (!userRow.hash || !bcrypt.compareSync(request.password, userRow.hash)) { + if (hasMinCodeVersion(viewer.platformDetails, 99999)) { + throw new ServerError('invalid_parameters'); + } else { + throw new ServerError('invalid_credentials'); + } + } + const id = userRow.id.toString(); + + return await logInQueries(viewer, input, id); +} + +const siweRequestInputValidator = tShape({ + address: t.String, + signature: t.String, + message: t.String, + watchedIDs: t.list(t.String), + calendarQuery: t.maybe(entryQueryInputValidator), + deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), + platformDetails: tPlatformDetails, + source: t.maybe(t.enums.of(values(logInActionSources))), +}); + +async function siweResponder( + viewer: Viewer, + input: any, +): Promise { + await validateInput(viewer, siweRequestInputValidator, input); + const request: SIWERequest = input; + + const { address, message, signature } = request; + if (!address) { + throw new ServerError('invalid_parameters'); + } + + try { + const siweMessage = new SiweMessage(message); + await siweMessage.validate(signature); + } catch (error) { + switch (error) { + case ErrorTypes.EXPIRED_MESSAGE: + throw new ServerError('expired_signature', { status: 440 }); + case ErrorTypes.INVALID_SIGNATURE: + throw new ServerError('invalid_signature', { status: 422 }); + default: + throw new ServerError('oops', { status: 500 }); + } + } + // addresses are case insensitive to the network but not to sql queries + // lowercasing just in case + const userQuery = SQL` + SELECT id, hash, username + FROM users + WHERE LCASE(ethereum_address) = LCASE(${address}) + `; + const [userResult] = await dbQuery(userQuery); + if (userResult.length === 0) { + // broke out vars for flow's sake - no thread IDs to watch on a new account + const { + message: noop, + signature: noop2, + watchedIDs: noop3, + ...rest + } = request; + return await createAccount(viewer, { + username: address, + password: signature, + ...rest, + }); + } + const userRow = userResult[0]; + const id = userRow.id.toString(); + + return await logInQueries(viewer, input, id); +} + const updatePasswordRequestInputValidator = tShape({ code: t.String, password: tPassword, @@ -339,4 +415,5 @@ logInResponder, oldPasswordUpdateResponder, updateUserSettingsResponder, + siweResponder, }; diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -7,6 +7,8 @@ LogInResult, RegisterResult, RegisterInfo, + SIWEServerCall, + SIWEResult, UpdateUserSettingsRequest, } from '../types/account-types'; import type { GetSessionPublicKeysArgs } from '../types/request-types'; @@ -83,11 +85,42 @@ registerCallServerEndpointOptions, ); return { + isNewAccount: response.isNewAccount, currentUserInfo: response.currentUserInfo, rawMessageInfos: response.rawMessageInfos, threadInfos: response.cookieChange.threadInfos, userInfos: response.cookieChange.userInfos, calendarQuery: registerInfo.calendarQuery, + updatesCurrentAsOf: response.serverTime, + }; +}; + +const siweActionTypes = Object.freeze({ + started: 'SIWE_STARTED', + success: 'SIWE_SUCCESS', + failed: 'SIWE_FAILED', +}); +const siwe = ( + callServerEndpoint: CallServerEndpoint, +): ((siweInfo: SIWEServerCall) => Promise) => async siweInfo => { + const watchedIDs = threadWatcher.getWatchedIDs(); + const response = await callServerEndpoint( + 'siwe', + { + ...siweInfo, + watchedIDs, + platformDetails: getConfig().platformDetails, + }, + registerCallServerEndpointOptions, + ); + return { + isNewAccount: response.isNewAccount, + currentUserInfo: response.currentUserInfo, + rawMessageInfos: response.rawMessageInfos, + threadInfos: response.cookieChange.threadInfos, + userInfos: response.cookieChange.userInfos, + calendarQuery: siweInfo.calendarQuery, + updatesCurrentAsOf: response.serverTime, }; }; @@ -134,9 +167,9 @@ threadInfos: response.cookieChange.threadInfos, currentUserInfo: response.currentUserInfo, calendarResult: { - calendarQuery: logInInfo.calendarQuery, rawEntryInfos: response.rawEntryInfos, }, + calendarQuery: logInInfo.calendarQuery, messagesResult: { messageInfos: response.rawMessageInfos, truncationStatus: response.truncationStatuses, @@ -238,6 +271,8 @@ searchUsersActionTypes, setUserSettings, setUserSettingsActionTypes, + siwe, + siweActionTypes, updateSubscription, updateSubscriptionActionTypes, }; diff --git a/lib/reducers/calendar-filters-reducer.js b/lib/reducers/calendar-filters-reducer.js --- a/lib/reducers/calendar-filters-reducer.js +++ b/lib/reducers/calendar-filters-reducer.js @@ -9,8 +9,6 @@ import { logOutActionTypes, deleteAccountActionTypes, - logInActionTypes, - registerActionTypes, } from '../actions/user-actions'; import { filteredThreadIDs, @@ -37,7 +35,10 @@ type ClientUpdateInfo, processUpdatesActionType, } from '../types/update-types'; -import { setNewSessionActionType } from '../utils/action-utils'; +import { + setNewSessionActionType, + isSuccessfulAuthType, +} from '../utils/action-utils'; export default function reduceCalendarFilters( state: $ReadOnlyArray, @@ -46,8 +47,7 @@ if ( action.type === logOutActionTypes.success || action.type === deleteAccountActionTypes.success || - action.type === logInActionTypes.success || - action.type === registerActionTypes.success || + isSuccessfulAuthType(action) || (action.type === setNewSessionActionType && action.payload.sessionChange.cookieInvalidated) ) { diff --git a/lib/reducers/connection-reducer.js b/lib/reducers/connection-reducer.js --- a/lib/reducers/connection-reducer.js +++ b/lib/reducers/connection-reducer.js @@ -1,5 +1,4 @@ // @flow - import { updateActivityActionTypes } from '../actions/activity-actions'; import { updateCalendarQueryActionTypes } from '../actions/entry-actions'; import { @@ -7,6 +6,7 @@ deleteAccountActionTypes, logInActionTypes, registerActionTypes, + siweActionTypes, } from '../actions/user-actions'; import { queueActivityUpdatesActionType } from '../types/activity-types'; import { defaultCalendarQuery } from '../types/entry-types'; @@ -76,8 +76,10 @@ } else if (action.type === logInActionTypes.success) { return { ...state, - actualizedCalendarQuery: action.payload.calendarResult.calendarQuery, + actualizedCalendarQuery: action.payload.calendarQuery, }; + } else if (action.type === siweActionTypes.success) { + return { ...state, actualizedCalendarQuery: action.payload.calendarQuery }; } else if ( action.type === registerActionTypes.success || action.type === updateCalendarQueryActionTypes.success || diff --git a/lib/reducers/data-loaded-reducer.js b/lib/reducers/data-loaded-reducer.js --- a/lib/reducers/data-loaded-reducer.js +++ b/lib/reducers/data-loaded-reducer.js @@ -1,22 +1,19 @@ // @flow - import { logOutActionTypes, deleteAccountActionTypes, - logInActionTypes, - registerActionTypes, } from '../actions/user-actions'; import type { BaseAction } from '../types/redux-types'; -import { setNewSessionActionType } from '../utils/action-utils'; +import { + setNewSessionActionType, + isSuccessfulAuthType, +} from '../utils/action-utils'; export default function reduceDataLoaded( state: boolean, action: BaseAction, ): boolean { - if ( - action.type === logInActionTypes.success || - action.type === registerActionTypes.success - ) { + if (isSuccessfulAuthType(action)) { return true; } else if ( action.type === setNewSessionActionType && diff --git a/lib/reducers/entry-reducer.js b/lib/reducers/entry-reducer.js --- a/lib/reducers/entry-reducer.js +++ b/lib/reducers/entry-reducer.js @@ -1,5 +1,4 @@ // @flow - import invariant from 'invariant'; import _filter from 'lodash/fp/filter'; import _flow from 'lodash/fp/flow'; @@ -37,6 +36,7 @@ logOutActionTypes, deleteAccountActionTypes, logInActionTypes, + siweActionTypes, } from '../actions/user-actions'; import { entryID, @@ -487,7 +487,10 @@ }, [], ]; - } else if (action.type === logInActionTypes.success) { + } else if ( + action.type === logInActionTypes.success || + (action.type === siweActionTypes.success && !action.payload.isNewAccount) + ) { const { calendarResult } = action.payload; if (calendarResult) { const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos( diff --git a/lib/reducers/master-reducer.js b/lib/reducers/master-reducer.js --- a/lib/reducers/master-reducer.js +++ b/lib/reducers/master-reducer.js @@ -1,6 +1,4 @@ // @flow - -import { registerActionTypes, logInActionTypes } from '../actions/user-actions'; import type { BaseNavInfo } from '../types/nav-types'; import type { BaseAppState, BaseAction } from '../types/redux-types'; import { @@ -8,6 +6,7 @@ incrementalStateSyncActionType, } from '../types/socket-types'; import type { StoreOperations } from '../types/store-ops-types'; +import { isSuccessfulAuthType } from '../utils/action-utils'; import reduceCalendarFilters from './calendar-filters-reducer'; import reduceConnectionInfo from './connection-reducer'; import reduceDataLoaded from './data-loaded-reducer'; @@ -61,8 +60,7 @@ connection.status !== 'connected' && action.type !== incrementalStateSyncActionType && action.type !== fullStateSyncActionType && - action.type !== registerActionTypes.success && - action.type !== logInActionTypes.success + !isSuccessfulAuthType(action) ) { if (messageStore.currentAsOf !== state.messageStore.currentAsOf) { messageStore = { diff --git a/lib/reducers/message-reducer.js b/lib/reducers/message-reducer.js --- a/lib/reducers/message-reducer.js +++ b/lib/reducers/message-reducer.js @@ -1,5 +1,4 @@ // @flow - import invariant from 'invariant'; import _difference from 'lodash/fp/difference'; import _flow from 'lodash/fp/flow'; @@ -48,6 +47,7 @@ deleteAccountActionTypes, logInActionTypes, registerActionTypes, + siweActionTypes, } from '../actions/user-actions'; import { pendingToRealizedThreadIDsSelector } from '../selectors/thread-selectors'; import { @@ -689,7 +689,11 @@ action: BaseAction, newThreadInfos: { +[id: string]: RawThreadInfo }, ): ReduceMessageStoreResult { - if (action.type === logInActionTypes.success) { + if ( + action.type === logInActionTypes.success || + (action.type === siweActionTypes.success && !action.payload.isNewAccount) + ) { + // $FlowFixMe const messagesResult = action.payload.messagesResult; const { messageStoreOperations, @@ -845,6 +849,23 @@ truncationStatuses, newThreadInfos, ); + } else if ( + action.type === siweActionTypes.success && + action.payload.isNewAccount + ) { + const truncationStatuses = {}; + // $FlowFixMe + for (const messageInfo of action.payload.rawMessageInfos) { + truncationStatuses[messageInfo.threadID] = + messageTruncationStatus.EXHAUSTIVE; + } + return mergeNewMessages( + messageStore, + // $FlowFixMe + action.payload.rawMessageInfos, + truncationStatuses, + newThreadInfos, + ); } else if ( action.type === changeThreadSettingsActionTypes.success || action.type === removeUsersFromThreadActionTypes.success || diff --git a/lib/reducers/thread-reducer.js b/lib/reducers/thread-reducer.js --- a/lib/reducers/thread-reducer.js +++ b/lib/reducers/thread-reducer.js @@ -20,8 +20,6 @@ import { logOutActionTypes, deleteAccountActionTypes, - logInActionTypes, - registerActionTypes, updateSubscriptionActionTypes, } from '../actions/user-actions'; import type { BaseAction } from '../types/redux-types'; @@ -48,7 +46,10 @@ processUpdatesActionType, } from '../types/update-types'; import { actionLogger } from '../utils/action-logger'; -import { setNewSessionActionType } from '../utils/action-utils'; +import { + setNewSessionActionType, + isSuccessfulAuthType, +} from '../utils/action-utils'; import { getConfig } from '../utils/config'; import { sanitizeActionSecrets } from '../utils/sanitization'; @@ -156,11 +157,8 @@ newThreadInconsistencies: $ReadOnlyArray, threadStoreOperations: $ReadOnlyArray, } { - if ( - action.type === logInActionTypes.success || - action.type === registerActionTypes.success || - action.type === fullStateSyncActionType - ) { + if (isSuccessfulAuthType(action) || action.type === fullStateSyncActionType) { + // $FlowFixMe const newThreadInfos = action.payload.threadInfos; const threadStoreOperations = [ { diff --git a/lib/reducers/updates-reducer.js b/lib/reducers/updates-reducer.js --- a/lib/reducers/updates-reducer.js +++ b/lib/reducers/updates-reducer.js @@ -1,6 +1,5 @@ // @flow - -import { logInActionTypes } from '../actions/user-actions'; +import { logInActionTypes, siweActionTypes } from '../actions/user-actions'; import type { BaseAction } from '../types/redux-types'; import { fullStateSyncActionType, @@ -12,7 +11,10 @@ currentAsOf: number, action: BaseAction, ): number { - if (action.type === logInActionTypes.success) { + if ( + action.type === logInActionTypes.success || + action.type === siweActionTypes.success + ) { return action.payload.updatesCurrentAsOf; } else if (action.type === fullStateSyncActionType) { return action.payload.updatesCurrentAsOf; diff --git a/lib/reducers/user-reducer.js b/lib/reducers/user-reducer.js --- a/lib/reducers/user-reducer.js +++ b/lib/reducers/user-reducer.js @@ -13,6 +13,7 @@ logInActionTypes, registerActionTypes, setUserSettingsActionTypes, + siweActionTypes, } from '../actions/user-actions'; import type { BaseAction } from '../types/redux-types'; import { @@ -45,6 +46,7 @@ if ( action.type === logInActionTypes.success || action.type === registerActionTypes.success || + action.type === siweActionTypes.success || action.type === logOutActionTypes.success || action.type === deleteAccountActionTypes.success ) { diff --git a/lib/types/account-types.js b/lib/types/account-types.js --- a/lib/types/account-types.js +++ b/lib/types/account-types.js @@ -50,16 +50,19 @@ +calendarQuery?: ?CalendarQuery, +deviceTokenUpdateRequest?: ?DeviceTokenUpdateRequest, +platformDetails: PlatformDetails, + +address?: ?string, }; export type RegisterResponse = { id: string, + isNewAccount: boolean, rawMessageInfos: $ReadOnlyArray, currentUserInfo: OldLoggedInUserInfo | LoggedInUserInfo, cookieChange: { threadInfos: { +[id: string]: RawThreadInfo }, userInfos: $ReadOnlyArray, }, + serverTime: number, }; export type RegisterResult = { @@ -68,6 +71,9 @@ +threadInfos: { +[id: string]: RawThreadInfo }, +userInfos: $ReadOnlyArray, +calendarQuery: CalendarQuery, + +isNewAccount: boolean, + +calendarResult?: ?CalendarResult, + +updatesCurrentAsOf: number, }; export type DeleteAccountRequest = { @@ -136,10 +142,32 @@ +messagesResult: GenericMessagesResult, +userInfos: $ReadOnlyArray, +calendarResult: CalendarResult, + +calendarQuery: CalendarQuery, +updatesCurrentAsOf: number, +logInActionSource: LogInActionSource, + +isNewAccount?: boolean, }; +export type SIWERequest = { + +address: string, + +message: string, + +signature: string, + +calendarQuery?: ?CalendarQuery, + +deviceTokenUpdateRequest?: ?DeviceTokenUpdateRequest, + +platformDetails: PlatformDetails, + +watchedIDs: $ReadOnlyArray, +}; + +export type SIWEServerCall = { + +address: string, + +message: string, + +signature: string, + ...LogInExtraInfo, +}; + +export type SIWEResponse = RegisterResponse | LogInResponse; + +export type SIWEResult = RegisterResult | LogInResult; export type UpdatePasswordRequest = { code: string, password: string, diff --git a/lib/types/endpoints.js b/lib/types/endpoints.js --- a/lib/types/endpoints.js +++ b/lib/types/endpoints.js @@ -24,6 +24,7 @@ CREATE_ACCOUNT: 'create_account', LOG_IN: 'log_in', UPDATE_PASSWORD: 'update_password', + SIWE: 'siwe', }); type SessionChangingEndpoint = $Values; diff --git a/lib/types/entry-types.js b/lib/types/entry-types.js --- a/lib/types/entry-types.js +++ b/lib/types/entry-types.js @@ -196,7 +196,6 @@ export type CalendarResult = { +rawEntryInfos: $ReadOnlyArray, - +calendarQuery: CalendarQuery, }; export type CalendarQueryUpdateStartingPayload = { diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -7,6 +7,7 @@ LogInResult, RegisterResult, DefaultNotificationPayload, + SIWEResult, } from './account-types'; import type { ActivityUpdateSuccessPayload, @@ -818,6 +819,22 @@ +error: true, +payload: Error, +loadingInfo: LoadingInfo, + } + | { + +type: 'SIWE_STARTED', + +payload?: void, + +loadingInfo: LoadingInfo, + } + | { + +type: 'SIWE_SUCCESS', + +payload: SIWEResult, + +loadingInfo: LoadingInfo, + } + | { + +type: 'SIWE_FAILED', + +error: true, + +payload: Error, + +loadingInfo: LoadingInfo, }; export type ActionPayload = ?(Object | Array<*> | $ReadOnlyArray<*> | string); diff --git a/lib/utils/action-utils.js b/lib/utils/action-utils.js --- a/lib/utils/action-utils.js +++ b/lib/utils/action-utils.js @@ -5,6 +5,11 @@ import { useSelector, useDispatch } from 'react-redux'; import { createSelector } from 'reselect'; +import { + logInActionTypes, + registerActionTypes, + siweActionTypes, +} from '../actions/user-actions'; import { serverCallStateSelector } from '../selectors/server-calls'; import { logInActionSources, @@ -366,6 +371,29 @@ connectionStatus: ConnectionStatus, }; +function isSuccessfulAuthType(action: BaseAction): boolean { + const isAuthType: boolean = + action.type === logInActionTypes.success || + action.type === registerActionTypes.success || + action.type === siweActionTypes.success; + return isAuthType; +} + +function isSuccessfulLogin(action: BaseAction): boolean { + if ( + action.type !== logInActionTypes.success || + action.type !== siweActionTypes.success + ) { + return false; + } + let isNewAccount = false; + if (action.type === siweActionTypes.success) { + isNewAccount = action.payload.isNewAccount; + } + const isLogin: boolean = action.type === logInActionTypes.success; + return isLogin || !isNewAccount; +} + // All server calls needs to include some information from the Redux state // (namely, the cookie). This information is used deep in the server call, // at the point where callServerEndpoint is called. We don't want to bother @@ -437,4 +465,6 @@ createBoundServerCallsSelector, registerActiveSocket, useServerCall, + isSuccessfulAuthType, + isSuccessfulLogin, }; diff --git a/native/account/logged-out-modal.react.js b/native/account/logged-out-modal.react.js --- a/native/account/logged-out-modal.react.js +++ b/native/account/logged-out-modal.react.js @@ -320,12 +320,14 @@ const promptButtonsSize = Platform.OS === 'ios' ? 40 : 61; const logInContainerSize = 140; const registerPanelSize = Platform.OS === 'ios' ? 181 : 180; + const siwePanelSize = 250; const containerSize = add( headerHeight, cond(not(isPastPrompt(this.modeValue)), promptButtonsSize, 0), cond(eq(this.modeValue, modeNumbers['log-in']), logInContainerSize, 0), cond(eq(this.modeValue, modeNumbers['register']), registerPanelSize, 0), + cond(eq(this.modeValue, modeNumbers['siwe']), siwePanelSize, 0), ); const potentialPanelPaddingTop = divide( max(sub(this.contentHeight, this.keyboardHeightValue, containerSize), 0), diff --git a/native/account/siwe-panel.react.js b/native/account/siwe-panel.react.js --- a/native/account/siwe-panel.react.js +++ b/native/account/siwe-panel.react.js @@ -3,11 +3,11 @@ import Animated from 'react-native-reanimated'; import WebView from 'react-native-webview'; -import { registerActionTypes, register } from 'lib/actions/user-actions'; +import { siweActionTypes, siwe } from 'lib/actions/user-actions'; import type { - RegisterInfo, + SIWEServerCall, + SIWEResult, LogInExtraInfo, - RegisterResult, LogInStartingPayload, } from 'lib/types/account-types'; import { @@ -20,7 +20,6 @@ import { useSelector } from '../redux/redux-utils'; import { nativeLogInExtraInfoSelector } from '../selectors/account-selectors'; import { defaultLandingURLPrefix } from '../utils/url-utils'; -import { setNativeCredentials } from './native-credentials'; const commSIWE = `${defaultLandingURLPrefix}/siwe`; @@ -35,40 +34,40 @@ // Redux dispatch functions +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs - +registerAction: (registerInfo: RegisterInfo) => Promise, + +siweAction: (siweInfo: SIWEServerCall) => Promise, }; function SIWEPanel({ logInExtraInfo, dispatchActionPromise, - registerAction, + siweAction, }: Props) { const handleSIWE = React.useCallback( - ({ address, signature }) => { + ({ address, message, signature }) => { // this is all mocked from register-panel const extraInfo = logInExtraInfo(); dispatchActionPromise( - registerActionTypes, - registerAction({ - username: address, - password: signature, + siweActionTypes, + siweAction({ + address, + message, + signature, ...extraInfo, }), undefined, ({ calendarQuery: extraInfo.calendarQuery }: LogInStartingPayload), ); - setNativeCredentials({ username: address, password: signature }); }, - [logInExtraInfo, dispatchActionPromise, registerAction], + [logInExtraInfo, dispatchActionPromise, siweAction], ); const handleMessage = React.useCallback( event => { const { nativeEvent: { data }, } = event; - const { address, signature } = JSON.parse(data); + const { address, message, signature } = JSON.parse(data); if (address && signature) { - handleSIWE({ address, signature }); + handleSIWE({ address, message, signature }); } }, [handleSIWE], @@ -87,14 +86,14 @@ ); const dispatchActionPromise = useDispatchActionPromise(); - const callRegister = useServerCall(register); + const callSiwe = useServerCall(siwe); return ( ); }, diff --git a/native/utils/url-utils.js b/native/utils/url-utils.js --- a/native/utils/url-utils.js +++ b/native/utils/url-utils.js @@ -40,7 +40,13 @@ return getDevNodeServerURLFromHostname(hostname); } +// the wallet SIWE prompt hangs indefinitely if it doesn't originate from HTTPs +const canRainbowKitSignOverHTTPYet = false; + function getDevLandingURL(): string { + if (!canRainbowKitSignOverHTTPYet) { + return productionLandingURL; + } invariant(__DEV__, 'getDevLandingURL called from production'); const hostname = getDevServerHostname(); return getDevLandingURLFromHostname(hostname);