Page MenuHomePhorge

D10493.1767154342.diff
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

D10493.1767154342.diff

diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js
--- a/keyserver/src/endpoints.js
+++ b/keyserver/src/endpoints.js
@@ -155,6 +155,8 @@
roleModificationResultValidator,
} from './responders/thread-responders.js';
import {
+ keyserverAuthRequestInputValidator,
+ keyserverAuthResponder,
userSubscriptionUpdateResponder,
passwordUpdateResponder,
sendVerificationEmailResponder,
@@ -380,6 +382,12 @@
threadJoinResultValidator,
baseLegalPolicies,
),
+ keyserver_auth: createJSONResponder(
+ keyserverAuthResponder,
+ keyserverAuthRequestInputValidator,
+ logInResponseValidator,
+ [],
+ ),
leave_thread: createJSONResponder(
threadLeaveResponder,
leaveThreadRequestInputValidator,
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
@@ -2,6 +2,7 @@
import type { Utility as OlmUtility } from '@commapp/olm';
import invariant from 'invariant';
+import { getRustAPI } from 'rust-node-addon';
import { ErrorTypes, SiweMessage } from 'siwe';
import t, { type TInterface, type TUnion, type TEnums } from 'tcomb';
import bcrypt from 'twin-bcrypt';
@@ -14,6 +15,7 @@
import { rawThreadInfoValidator } from 'lib/permissions/minimally-encoded-thread-permissions-validators.js';
import { hasMinCodeVersion } from 'lib/shared/version-utils.js';
import type {
+ KeyserverAuthRequest,
ResetPasswordRequest,
LogOutResponse,
RegisterResponse,
@@ -24,7 +26,7 @@
UpdateUserSettingsRequest,
PolicyAcknowledgmentRequest,
ClaimUsernameResponse,
-} from 'lib/types/account-types.js';
+} from 'lib/types/account-types';
import {
userSettingsTypes,
notificationTypeValues,
@@ -98,10 +100,15 @@
verifyCalendarQueryThreadIDs,
} from './entry-responders.js';
import {
+ processOLMAccountCreation,
createAccount,
processSIWEAccountCreation,
} from '../creators/account-creator.js';
-import { createAndPersistOlmSession } from '../creators/olm-session-creator.js';
+import {
+ createOlmSession,
+ persistFreshOlmSession,
+ createAndPersistOlmSession,
+} from '../creators/olm-session-creator.js';
import { dbQuery, SQL } from '../database/database.js';
import { deleteAccount } from '../deleters/account-deleters.js';
import { deleteCookie } from '../deleters/cookie-deleters.js';
@@ -133,7 +140,8 @@
import { fetchOlmAccount } from '../updaters/olm-account-updater.js';
import { userSubscriptionUpdater } from '../updaters/user-subscription-updaters.js';
import { viewerAcknowledgmentUpdater } from '../updaters/viewer-acknowledgment-updater.js';
-import { getOlmUtility } from '../utils/olm-utils.js';
+import { fetchIdentityInfo } from '../user/identity.js';
+import { getOlmUtility, getContentSigningKey } from '../utils/olm-utils.js';
export const subscriptionUpdateRequestInputValidator: TInterface<SubscriptionUpdateRequest> =
tShape<SubscriptionUpdateRequest>({
@@ -293,6 +301,8 @@
+socialProof?: ?SIWESocialProof,
+signedIdentityKeysBlob?: ?SignedIdentityKeysBlob,
+initialNotificationsEncryptedMessage?: string,
+ +pickledContentOlmSession?: string,
+ +cookieHasBeenSet?: boolean,
};
async function processSuccessfulLogin(
@@ -306,6 +316,8 @@
socialProof,
signedIdentityKeysBlob,
initialNotificationsEncryptedMessage,
+ pickledContentOlmSession,
+ cookieHasBeenSet,
} = params;
const request: LogInRequest = input;
@@ -313,17 +325,25 @@
const deviceToken = request.deviceTokenUpdateRequest
? request.deviceTokenUpdateRequest.deviceToken
: viewer.deviceToken;
- const [userViewerData, notAcknowledgedPolicies] = await Promise.all([
- createNewUserCookie(userID, {
- platformDetails: request.platformDetails,
- deviceToken,
- socialProof,
- signedIdentityKeysBlob,
- }),
+ const setNewCookiePromise = (async () => {
+ if (cookieHasBeenSet) {
+ return;
+ }
+ const [userViewerData] = await Promise.all([
+ createNewUserCookie(userID, {
+ platformDetails: request.platformDetails,
+ deviceToken,
+ socialProof,
+ signedIdentityKeysBlob,
+ }),
+ deleteCookie(viewer.cookieID),
+ ]);
+ viewer.setNewCookie(userViewerData);
+ })();
+ const [notAcknowledgedPolicies] = await Promise.all([
fetchNotAcknowledgedPolicies(userID, baseLegalPolicies),
- deleteCookie(viewer.cookieID),
+ setNewCookiePromise,
]);
- viewer.setNewCookie(userViewerData);
if (
notAcknowledgedPolicies.length &&
@@ -348,16 +368,28 @@
if (calendarQuery) {
await setNewSession(viewer, calendarQuery, newServerTime);
}
- const olmSessionPromise = (async () => {
+ const olmNotifSessionPromise = (async () => {
if (
- userViewerData.cookieID &&
+ viewer.cookieID &&
initialNotificationsEncryptedMessage &&
signedIdentityKeysBlob
) {
await createAndPersistOlmSession(
initialNotificationsEncryptedMessage,
'notifications',
- userViewerData.cookieID,
+ viewer.cookieID,
+ );
+ }
+ })();
+ // `pickledContentOlmSession` is created in `keyserverAuthResponder(...)` in
+ // order to authenticate the user. Here, we simply persist the session if it
+ // exists.
+ const olmContentSessionPromise = (async () => {
+ if (viewer.cookieID && pickledContentOlmSession) {
+ await persistFreshOlmSession(
+ pickledContentOlmSession,
+ 'content',
+ viewer.cookieID,
);
}
})();
@@ -387,7 +419,8 @@
entriesPromise,
fetchKnownUserInfos(viewer),
fetchLoggedInUserInfo(viewer),
- olmSessionPromise,
+ olmNotifSessionPromise,
+ olmContentSessionPromise,
]);
const rawEntryInfos = entriesResult ? entriesResult.rawEntryInfos : null;
@@ -666,6 +699,127 @@
});
}
+export const keyserverAuthRequestInputValidator: TInterface<KeyserverAuthRequest> =
+ tShape<KeyserverAuthRequest>({
+ userID: t.String,
+ deviceID: t.String,
+ calendarQuery: entryQueryInputValidator,
+ deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator),
+ platformDetails: tPlatformDetails,
+ watchedIDs: t.list(tID),
+ initialContentEncryptedMessage: t.String,
+ initialNotificationsEncryptedMessage: t.String,
+ doNotRegister: t.Boolean,
+ });
+
+async function keyserverAuthResponder(
+ viewer: Viewer,
+ request: KeyserverAuthRequest,
+): Promise<LogInResponse> {
+ const {
+ userID,
+ deviceID,
+ deviceTokenUpdateRequest,
+ platformDetails,
+ initialContentEncryptedMessage,
+ initialNotificationsEncryptedMessage,
+ doNotRegister,
+ } = request;
+ const calendarQuery = normalizeCalendarQuery(request.calendarQuery);
+
+ // 1. Check if there's already a user for this userID. Simultaneously, get
+ // info for identity service auth.
+ const [existingUsername, authDeviceID, identityInfo, rustAPI] =
+ await Promise.all([
+ fetchUsername(userID),
+ getContentSigningKey(),
+ fetchIdentityInfo(),
+ getRustAPI(),
+ ]);
+ if (!existingUsername && doNotRegister) {
+ throw new ServerError('account_does_not_exist');
+ }
+ if (!identityInfo) {
+ throw new ServerError('account_not_registered_on_identity_service');
+ }
+
+ // 2. Get user's keys from identity service.
+ let inboundKeysForUser;
+ try {
+ inboundKeysForUser = await rustAPI.getInboundKeysForUserDevice(
+ identityInfo.userId,
+ authDeviceID,
+ identityInfo.accessToken,
+ userID,
+ deviceID,
+ );
+ } catch (e) {
+ throw new ServerError('failed_to_retrieve_inbound_keys');
+ }
+
+ const username = inboundKeysForUser.username
+ ? inboundKeysForUser.username
+ : inboundKeysForUser.walletAddress;
+
+ if (!username) {
+ throw new ServerError('user_identifier_missing');
+ }
+
+ const identityKeys: IdentityKeysBlob = JSON.parse(inboundKeysForUser.payload);
+ if (!identityKeysBlobValidator.is(identityKeys)) {
+ throw new ServerError('invalid_identity_keys_blob');
+ }
+
+ // 3. Create content olm session. (The notif session is not required for auth
+ // and will be created later in `processSuccessfulLogin(...)`.)
+ const pickledContentOlmSessionPromise = createOlmSession(
+ initialContentEncryptedMessage,
+ 'content',
+ identityKeys.primaryIdentityPublicKeys.curve25519,
+ );
+
+ // 4. Create account with call to `processOLMAccountCreation(...)`
+ // if username does not correspond to an existing user. If we successfully
+ // create a new account, we set `cookieHasBeenSet` to true to avoid
+ // creating a new cookie again in `processSuccessfulLogin`.
+ const signedIdentityKeysBlob: SignedIdentityKeysBlob = {
+ payload: inboundKeysForUser.payload,
+ signature: inboundKeysForUser.payloadSignature,
+ };
+ const olmAccountCreationPromise = (async () => {
+ if (existingUsername) {
+ return;
+ }
+ const olmAccountCreationRequest = {
+ userID,
+ username: username,
+ walletAddress: inboundKeysForUser.walletAddress,
+ signedIdentityKeysBlob,
+ calendarQuery,
+ deviceTokenUpdateRequest,
+ platformDetails,
+ };
+ await processOLMAccountCreation(viewer, olmAccountCreationRequest);
+ })();
+
+ const [pickledContentOlmSession] = await Promise.all([
+ pickledContentOlmSessionPromise,
+ olmAccountCreationPromise,
+ ]);
+
+ // 5. Complete login with call to `processSuccessfulLogin(...)`.
+ return await processSuccessfulLogin({
+ viewer,
+ input: request,
+ userID,
+ calendarQuery,
+ signedIdentityKeysBlob,
+ initialNotificationsEncryptedMessage,
+ pickledContentOlmSession,
+ cookieHasBeenSet: !existingUsername,
+ });
+}
+
export const updatePasswordRequestInputValidator: TInterface<UpdatePasswordRequest> =
tShape<UpdatePasswordRequest>({
code: t.String,
@@ -781,4 +935,5 @@
policyAcknowledgmentResponder,
updateUserAvatarResponder,
claimUsernameResponder,
+ keyserverAuthResponder,
};

File Metadata

Mime Type
text/plain
Expires
Wed, Dec 31, 4:12 AM (7 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5871935
Default Alt Text
D10493.1767154342.diff (9 KB)

Event Timeline