diff --git a/web/grpc/identity-service-client-wrapper.js b/web/grpc/identity-service-client-wrapper.js --- a/web/grpc/identity-service-client-wrapper.js +++ b/web/grpc/identity-service-client-wrapper.js @@ -1,13 +1,25 @@ // @flow +import { Login } from '@commapp/opaque-ke-wasm'; + import identityServiceConfig from 'lib/facts/identity-service.js'; import type { IdentityServiceAuthLayer } from 'lib/types/identity-service-types.js'; import { VersionInterceptor, AuthInterceptor } from './interceptor.js'; +import { initOpaque } from '../crypto/opaque-utils.js'; import * as IdentityAuthClient from '../protobufs/identity-auth-client.cjs'; -import { Empty } from '../protobufs/identity-unauth-structs.cjs'; +import { + DeviceKeyUpload, + Empty, + IdentityKeyInfo, + OpaqueLoginFinishRequest, + OpaqueLoginStartRequest, + Prekey, +} from '../protobufs/identity-unauth-structs.cjs'; import * as IdentityClient from '../protobufs/identity-unauth.cjs'; +const webDeviceType = Object.freeze(1); + class IdentityServiceClientWrapper { authClient: ?IdentityAuthClient.IdentityClientServicePromiseClient; unauthorizedClient: ?IdentityClient.IdentityClientServicePromiseClient; @@ -21,7 +33,9 @@ return process.env.IDENTITY_SOCKET_ADDR ?? identityServiceConfig.defaultURL; } - async initAuthClient(authLayer: IdentityServiceAuthLayer): Promise { + initAuthClient( + authLayer: IdentityServiceAuthLayer, + ): IdentityAuthClient.IdentityClientServicePromiseClient { const { userID, deviceID, commServicesAccessToken } = authLayer; const identitySocketAddr = this.determineSocketAddr(); @@ -42,9 +56,11 @@ null, authClientOpts, ); + + return this.authClient; } - async initUnauthorizedClient(): Promise { + initUnauthorizedClient(): IdentityClient.IdentityClientServicePromiseClient { const identitySocketAddr = this.determineSocketAddr(); const versionInterceptor = new VersionInterceptor(); @@ -59,6 +75,8 @@ null, unauthorizedClientOpts, ); + + return this.unauthorizedClient; } deleteUser: ( @@ -70,20 +88,100 @@ deviceID: string, accessToken: string, ): Promise => { - if (!this.authClient) { - const authLayer: IdentityServiceAuthLayer = { + const authClient = + this.authClient || + this.initAuthClient({ userID, deviceID, commServicesAccessToken: accessToken, - }; - await this.initAuthClient(authLayer); + }); + + await authClient.deleteUser(new Empty()); + }; + + loginPasswordUser: ( + username: string, + password: string, + keyPayload: string, + keyPayloadSignature: string, + contentPrekey: string, + contentPrekeySignature: string, + notifPrekey: string, + notifPrekeySignature: string, + contentOneTimeKeys: Array, + notifOneTimeKeys: Array, + ) => Promise = async ( + username: string, + password: string, + keyPayload: string, + keyPayloadSignature: string, + contentPrekey: string, + contentPrekeySignature: string, + notifPrekey: string, + notifPrekeySignature: string, + contentOneTimeKeys: Array, + notifOneTimeKeys: Array, + ): Promise => { + const client = this.unauthorizedClient || this.initUnauthorizedClient(); + + await initOpaque(); + const opaqueLogin = new Login(); + const startRequestBytes = opaqueLogin.start(password); + + const identityKeyInfo = new IdentityKeyInfo(); + identityKeyInfo.setPayload(keyPayload); + identityKeyInfo.setPayloadSignature(keyPayloadSignature); + + const contentPrekeyUpload = new Prekey(); + contentPrekeyUpload.setPrekey(contentPrekey); + contentPrekeyUpload.setPrekeySignature(contentPrekeySignature); + + const notifPrekeyUpload = new Prekey(); + notifPrekeyUpload.setPrekey(notifPrekey); + notifPrekeyUpload.setPrekeySignature(notifPrekeySignature); + + const deviceKeyUpload = new DeviceKeyUpload(); + deviceKeyUpload.setDeviceKeyInfo(identityKeyInfo); + deviceKeyUpload.setContentUpload(contentPrekeyUpload); + deviceKeyUpload.setNotifUpload(notifPrekeyUpload); + deviceKeyUpload.setOneTimeContentPrekeysList(contentOneTimeKeys); + deviceKeyUpload.setOneTimeNotifPrekeysList(notifOneTimeKeys); + deviceKeyUpload.setDeviceType(webDeviceType); + + const loginStartRequest = new OpaqueLoginStartRequest(); + loginStartRequest.setUsername(username); + loginStartRequest.setOpaqueLoginRequest(startRequestBytes); + loginStartRequest.setDeviceKeyUpload(deviceKeyUpload); + + let loginStartResponse; + try { + loginStartResponse = + await client.logInPasswordUserStart(loginStartRequest); + } catch (startError) { + startError.method = 'loginPasswordUserStart'; + throw startError; } + const finishRequestBytes = opaqueLogin.finish( + loginStartResponse.getOpaqueLoginResponse_asU8(), + ); - if (this.authClient) { - await this.authClient.deleteUser(new Empty()); - } else { - throw new Error('Identity service client is not initialized'); + const loginFinishRequest = new OpaqueLoginFinishRequest(); + loginFinishRequest.setSessionId(loginStartResponse.getSessionId()); + loginFinishRequest.setOpaqueLoginUpload(finishRequestBytes); + + let loginFinishResponse; + try { + loginFinishResponse = + await client.logInPasswordUserFinish(loginFinishRequest); + } catch (finishError) { + finishError.method = 'loginPasswordUserFinish'; + throw finishError; } + + const userID = loginFinishResponse.getUserId(); + const accessToken = loginFinishResponse.getAccessToken(); + + return JSON.stringify({ userID, accessToken }); }; }