Page MenuHomePhabricator

D5603.id18618.diff
No OneTemporary

D5603.id18618.diff

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([
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<LogInResponse> {
- 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<LogInResponse> {
+ 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<SIWEResponse> {
+ 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';
@@ -91,6 +93,33 @@
};
};
+const siweActionTypes = Object.freeze({
+ started: 'SIWE_STARTED',
+ success: 'SIWE_SUCCESS',
+ failed: 'SIWE_FAILED',
+});
+const siwe = (
+ callServerEndpoint: CallServerEndpoint,
+): ((siweInfo: SIWEServerCall) => Promise<SIWEResult>) => async siweInfo => {
+ const watchedIDs = threadWatcher.getWatchedIDs();
+ const response = await callServerEndpoint(
+ 'siwe',
+ {
+ ...siweInfo,
+ watchedIDs,
+ platformDetails: getConfig().platformDetails,
+ },
+ registerCallServerEndpointOptions,
+ );
+ return {
+ currentUserInfo: response.currentUserInfo,
+ rawMessageInfos: response.rawMessageInfos,
+ threadInfos: response.cookieChange.threadInfos,
+ userInfos: response.cookieChange.userInfos,
+ calendarQuery: siweInfo.calendarQuery,
+ };
+};
+
function mergeUserInfos(...userInfoArrays: UserInfo[][]): UserInfo[] {
const merged = {};
for (const userInfoArray of userInfoArrays) {
@@ -238,6 +267,8 @@
searchUsersActionTypes,
setUserSettings,
setUserSettingsActionTypes,
+ siwe,
+ siweActionTypes,
updateSubscription,
updateSubscriptionActionTypes,
};
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
@@ -5,6 +5,7 @@
deleteAccountActionTypes,
logInActionTypes,
registerActionTypes,
+ siweActionTypes,
} from '../actions/user-actions';
import type { BaseAction } from '../types/redux-types';
import { setNewSessionActionType } from '../utils/action-utils';
@@ -15,7 +16,8 @@
): boolean {
if (
action.type === logInActionTypes.success ||
- action.type === registerActionTypes.success
+ action.type === registerActionTypes.success ||
+ action.type === siweActionTypes.success
) {
return true;
} else if (
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,6 +50,7 @@
+calendarQuery?: ?CalendarQuery,
+deviceTokenUpdateRequest?: ?DeviceTokenUpdateRequest,
+platformDetails: PlatformDetails,
+ +address?: ?string,
};
export type RegisterResponse = {
@@ -140,6 +141,26 @@
+logInActionSource: LogInActionSource,
};
+export type SIWERequest = {
+ +address: string,
+ +message: string,
+ +signature: string,
+ +calendarQuery?: ?CalendarQuery,
+ +deviceTokenUpdateRequest?: ?DeviceTokenUpdateRequest,
+ +platformDetails: PlatformDetails,
+ +watchedIDs: $ReadOnlyArray<string>,
+};
+
+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<typeof sessionChangingEndpoints>;
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/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<RegisterResult>,
+ +siweAction: (siweInfo: SIWEServerCall) => Promise<SIWEResult>,
};
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 (
<SIWEPanel
{...props}
logInExtraInfo={logInExtraInfo}
dispatchActionPromise={dispatchActionPromise}
- registerAction={callRegister}
+ siweAction={callSiwe}
/>
);
},
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);

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 30, 4:18 AM (18 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2599543
Default Alt Text
D5603.id18618.diff (16 KB)

Event Timeline