diff --git a/keyserver/addons/rust-node-addon/src/identity_client/login.rs b/keyserver/addons/rust-node-addon/src/identity_client/login.rs --- a/keyserver/addons/rust-node-addon/src/identity_client/login.rs +++ b/keyserver/addons/rust-node-addon/src/identity_client/login.rs @@ -54,20 +54,37 @@ }; debug!("Starting login to identity service"); - let login_start_response = identity_client + let response = identity_client .login_password_user_start(login_start_request) .await - .map_err(handle_grpc_error)? - .into_inner(); - + .map_err(handle_grpc_error)?; debug!("Received login response from identity service"); + + // We need to get the load balancer cookie from from the response and send it + // in the subsequent request to ensure it is routed to the same identity + // service instance as the first request + let cookie = response + .metadata() + .get(RESPONSE_METADATA_COOKIE_KEY) + .cloned(); + + let login_start_response = response.into_inner(); + let opaque_login_upload = client_login .finish(&login_start_response.opaque_login_response) .map_err(|_| Error::from_reason("Failed to finish opaque login request"))?; - let login_finish_request = OpaqueLoginFinishRequest { + + let mut login_finish_request = Request::new(OpaqueLoginFinishRequest { session_id: login_start_response.session_id, opaque_login_upload, - }; + }); + + // Cookie won't be available in local dev environments + if let Some(cookie_metadata) = cookie { + login_finish_request + .metadata_mut() + .insert(REQUEST_METADATA_COOKIE_KEY, cookie_metadata); + } debug!("Attempting to finalize opaque login exchange with identity service"); let login_finish_response = identity_client diff --git a/keyserver/addons/rust-node-addon/src/identity_client/mod.rs b/keyserver/addons/rust-node-addon/src/identity_client/mod.rs --- a/keyserver/addons/rust-node-addon/src/identity_client/mod.rs +++ b/keyserver/addons/rust-node-addon/src/identity_client/mod.rs @@ -16,6 +16,9 @@ use grpc_clients::identity::protos::authenticated::UploadOneTimeKeysRequest; use grpc_clients::identity::protos::unauthenticated as client_proto; use grpc_clients::identity::shared::CodeVersionLayer; +use grpc_clients::identity::{ + REQUEST_METADATA_COOKIE_KEY, RESPONSE_METADATA_COOKIE_KEY, +}; use lazy_static::lazy_static; use napi::bindgen_prelude::*; use serde::{Deserialize, Serialize}; diff --git a/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs b/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs --- a/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs +++ b/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs @@ -50,13 +50,22 @@ }); // Finish OPAQUE registration and send final registration request - let registration_start_response = identity_client + let response = identity_client .register_password_user_start(registration_start_request) .await - .map_err(handle_grpc_error)? - .into_inner(); + .map_err(handle_grpc_error)?; debug!("Received registration start response"); + // We need to get the load balancer cookie from from the response and send it + // in the subsequent request to ensure it is routed to the same identity + // service instance as the first request + let cookie = response + .metadata() + .get(RESPONSE_METADATA_COOKIE_KEY) + .cloned(); + + let registration_start_response = response.into_inner(); + let opaque_registration_upload = opaque_registration .finish( &password, @@ -64,10 +73,18 @@ ) .map_err(|_| Error::from_status(Status::GenericFailure))?; - let registration_finish_request = Request::new(RegistrationFinishRequest { - session_id: registration_start_response.session_id, - opaque_registration_upload, - }); + let mut registration_finish_request = + Request::new(RegistrationFinishRequest { + session_id: registration_start_response.session_id, + opaque_registration_upload, + }); + + // Cookie won't be available in local dev environments + if let Some(cookie_metadata) = cookie { + registration_finish_request + .metadata_mut() + .insert(REQUEST_METADATA_COOKIE_KEY, cookie_metadata); + } let registration_response = identity_client .register_password_user_finish(registration_finish_request) diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -10,12 +10,15 @@ OutboundKeyInfo, OutboundKeysForUserRequest, PreKey, RegistrationFinishRequest, RegistrationStartRequest, WalletLoginRequest, }; -use grpc_clients::identity::{get_auth_client, get_unauthenticated_client}; +use grpc_clients::identity::{ + get_auth_client, get_unauthenticated_client, REQUEST_METADATA_COOKIE_KEY, + RESPONSE_METADATA_COOKIE_KEY, +}; use lazy_static::lazy_static; use serde::Serialize; use std::sync::Arc; use tokio::runtime::{Builder, Runtime}; -use tonic::Status; +use tonic::{Request, Status}; use tracing::instrument; mod argon2_tools; @@ -375,10 +378,19 @@ DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; - let registration_start_response = identity_client + let response = identity_client .register_password_user_start(registration_start_request) - .await? - .into_inner(); + .await?; + + // We need to get the load balancer cookie from from the response and send it + // in the subsequent request to ensure it is routed to the same identity + // service instance as the first request + let cookie = response + .metadata() + .get(RESPONSE_METADATA_COOKIE_KEY) + .cloned(); + + let registration_start_response = response.into_inner(); let opaque_registration_upload = client_registration .finish( @@ -386,13 +398,23 @@ ®istration_start_response.opaque_registration_response, ) .map_err(handle_error)?; + let registration_finish_request = RegistrationFinishRequest { session_id: registration_start_response.session_id, opaque_registration_upload, }; + let mut finish_request = Request::new(registration_finish_request); + + // Cookie won't be available in local dev environments + if let Some(cookie_metadata) = cookie { + finish_request + .metadata_mut() + .insert(REQUEST_METADATA_COOKIE_KEY, cookie_metadata); + } + let registration_finish_response = identity_client - .register_password_user_finish(registration_finish_request) + .register_password_user_finish(finish_request) .await? .into_inner(); let user_id_and_access_token = UserIDAndDeviceAccessToken { @@ -471,21 +493,40 @@ ) .await?; - let login_start_response = identity_client + let response = identity_client .login_password_user_start(login_start_request) - .await? - .into_inner(); + .await?; + + // We need to get the load balancer cookie from from the response and send it + // in the subsequent request to ensure it is routed to the same identity + // service instance as the first request + let cookie = response + .metadata() + .get(RESPONSE_METADATA_COOKIE_KEY) + .cloned(); + + let login_start_response = response.into_inner(); let opaque_login_upload = client_login .finish(&login_start_response.opaque_login_response) .map_err(handle_error)?; + let login_finish_request = OpaqueLoginFinishRequest { session_id: login_start_response.session_id, opaque_login_upload, }; + let mut finish_request = Request::new(login_finish_request); + + // Cookie won't be available in local dev environments + if let Some(cookie_metadata) = cookie { + finish_request + .metadata_mut() + .insert(REQUEST_METADATA_COOKIE_KEY, cookie_metadata); + } + let login_finish_response = identity_client - .login_password_user_finish(login_finish_request) + .login_password_user_finish(finish_request) .await? .into_inner(); let user_id_and_access_token = UserIDAndDeviceAccessToken { @@ -634,10 +675,19 @@ ) .await?; - let update_password_start_response = identity_client + let response = identity_client .update_user_password_start(update_password_start_request) - .await? - .into_inner(); + .await?; + + // We need to get the load balancer cookie from from the response and send it + // in the subsequent request to ensure it is routed to the same identity + // service instance as the first request + let cookie = response + .metadata() + .get(RESPONSE_METADATA_COOKIE_KEY) + .cloned(); + + let update_password_start_response = response.into_inner(); let opaque_registration_upload = client_registration .finish( @@ -645,13 +695,23 @@ &update_password_start_response.opaque_registration_response, ) .map_err(handle_error)?; + let update_password_finish_request = UpdateUserPasswordFinishRequest { session_id: update_password_start_response.session_id, opaque_registration_upload, }; + let mut finish_request = Request::new(update_password_finish_request); + + // Cookie won't be available in local dev environments + if let Some(cookie_metadata) = cookie { + finish_request + .metadata_mut() + .insert(REQUEST_METADATA_COOKIE_KEY, cookie_metadata); + } + identity_client - .update_user_password_finish(update_password_finish_request) + .update_user_password_finish(finish_request) .await?; Ok(()) diff --git a/shared/grpc_clients/src/identity/mod.rs b/shared/grpc_clients/src/identity/mod.rs --- a/shared/grpc_clients/src/identity/mod.rs +++ b/shared/grpc_clients/src/identity/mod.rs @@ -17,4 +17,5 @@ pub use authenticated::get_auth_client; pub use device::DeviceType; +pub use shared::{REQUEST_METADATA_COOKIE_KEY, RESPONSE_METADATA_COOKIE_KEY}; pub use unauthenticated::get_unauthenticated_client; diff --git a/shared/grpc_clients/src/identity/shared.rs b/shared/grpc_clients/src/identity/shared.rs --- a/shared/grpc_clients/src/identity/shared.rs +++ b/shared/grpc_clients/src/identity/shared.rs @@ -4,6 +4,9 @@ Request, Status, }; +pub const RESPONSE_METADATA_COOKIE_KEY: &str = "set-cookie"; +pub const REQUEST_METADATA_COOKIE_KEY: &str = "cookie"; + pub struct CodeVersionLayer { pub(crate) version: u64, pub(crate) device_type: String,