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 @@ -1,15 +1,17 @@ use crate::ffi::{bool_callback, string_callback, void_callback}; use comm_opaque2::client::{Login, Registration}; use comm_opaque2::grpc::opaque_error_to_grpc_status as handle_error; -use grpc_clients::identity::get_unauthenticated_client; +use grpc_clients::identity::protos::authenticated::{ + UpdateUserPasswordFinishRequest, UpdateUserPasswordStartRequest, +}; use grpc_clients::identity::protos::client::{ outbound_keys_for_user_request::Identifier, DeleteUserRequest, DeviceKeyUpload, DeviceType, Empty, IdentityKeyInfo, OpaqueLoginFinishRequest, OpaqueLoginStartRequest, OutboundKeyInfo, OutboundKeysForUserRequest, PreKey, RegistrationFinishRequest, - RegistrationStartRequest, UpdateUserPasswordFinishRequest, - UpdateUserPasswordStartRequest, WalletLoginRequest, + RegistrationStartRequest, WalletLoginRequest, }; +use grpc_clients::identity::{get_auth_client, get_unauthenticated_client}; use lazy_static::lazy_static; use serde::Serialize; use std::sync::Arc; @@ -622,12 +624,12 @@ .map_err(handle_error)?; let update_password_start_request = UpdateUserPasswordStartRequest { opaque_registration_request, - access_token: update_password_info.access_token, - user_id: update_password_info.user_id, - device_id_key: update_password_info.device_id, }; - let mut identity_client = get_unauthenticated_client( + let mut identity_client = get_auth_client( "http://127.0.0.1:50054", + update_password_info.user_id, + update_password_info.device_id, + update_password_info.access_token, CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) diff --git a/services/identity/src/client_service.rs b/services/identity/src/client_service.rs --- a/services/identity/src/client_service.rs +++ b/services/identity/src/client_service.rs @@ -22,10 +22,8 @@ RegistrationFinishRequest, RegistrationFinishResponse, RegistrationStartRequest, RegistrationStartResponse, RemoveReservedUsernameRequest, ReservedRegistrationStartRequest, - ReservedWalletLoginRequest, UpdateUserPasswordFinishRequest, - UpdateUserPasswordStartRequest, UpdateUserPasswordStartResponse, - VerifyUserAccessTokenRequest, VerifyUserAccessTokenResponse, - WalletLoginRequest, WalletLoginResponse, + ReservedWalletLoginRequest, VerifyUserAccessTokenRequest, + VerifyUserAccessTokenResponse, WalletLoginRequest, WalletLoginResponse, }; use crate::config::CONFIG; use crate::database::{ @@ -252,80 +250,6 @@ } } - async fn update_user_password_start( - &self, - request: tonic::Request, - ) -> Result, tonic::Status> - { - let message = request.into_inner(); - - let token_is_valid = self - .client - .verify_access_token( - message.user_id.clone(), - message.device_id_key, - message.access_token, - ) - .await - .map_err(handle_db_error)?; - - if !token_is_valid { - return Err(tonic::Status::permission_denied("bad token")); - } - - let server_registration = comm_opaque2::server::Registration::new(); - let server_message = server_registration - .start( - &CONFIG.server_setup, - &message.opaque_registration_request, - message.user_id.as_bytes(), - ) - .map_err(protocol_error_to_grpc_status)?; - - let update_state = UpdateState { - user_id: message.user_id, - }; - let session_id = self - .cache - .insert_with_uuid_key(WorkflowInProgress::Update(update_state)) - .await; - - let response = UpdateUserPasswordStartResponse { - session_id, - opaque_registration_response: server_message, - }; - Ok(Response::new(response)) - } - - async fn update_user_password_finish( - &self, - request: tonic::Request, - ) -> Result, tonic::Status> { - let message = request.into_inner(); - - if let Some(WorkflowInProgress::Update(state)) = - self.cache.get(&message.session_id) - { - self.cache.invalidate(&message.session_id).await; - - let server_registration = comm_opaque2::server::Registration::new(); - let password_file = server_registration - .finish(&message.opaque_registration_upload) - .map_err(protocol_error_to_grpc_status)?; - - self - .client - .update_user_password(state.user_id, password_file) - .await - .map_err(handle_db_error)?; - - let response = Empty {}; - Ok(Response::new(response)) - } else { - Err(tonic::Status::not_found("session not found")) - } - } - async fn login_password_user_start( &self, request: tonic::Request, diff --git a/services/identity/src/grpc_services/authenticated.rs b/services/identity/src/grpc_services/authenticated.rs --- a/services/identity/src/grpc_services/authenticated.rs +++ b/services/identity/src/grpc_services/authenticated.rs @@ -1,7 +1,15 @@ +use crate::config::CONFIG; use crate::{ - client_service::handle_db_error, constants::request_metadata, - database::DatabaseClient, grpc_services::shared::get_value, token::AuthType, + client_service::{ + handle_db_error, CacheExt, UpdateState, WorkflowInProgress, + }, + constants::request_metadata, + database::DatabaseClient, + grpc_services::shared::get_value, + token::AuthType, }; +use comm_opaque2::grpc::protocol_error_to_grpc_status; +use moka::future::Cache; use tonic::{Request, Response, Status}; // This must be named client, because generated code from the authenticated @@ -25,6 +33,7 @@ #[derive(derive_more::Constructor)] pub struct AuthenticatedService { db_client: DatabaseClient, + cache: Cache, } fn get_auth_info(req: &Request<()>) -> Option<(String, String, String)> { @@ -194,4 +203,65 @@ Ok(Response::new(FindUserIdResponse { user_id })) } + + async fn update_user_password_start( + &self, + request: tonic::Request, + ) -> Result< + tonic::Response, + tonic::Status, + > { + let (user_id, _) = get_user_and_device_id(&request)?; + let message = request.into_inner(); + + let server_registration = comm_opaque2::server::Registration::new(); + let server_message = server_registration + .start( + &CONFIG.server_setup, + &message.opaque_registration_request, + user_id.as_bytes(), + ) + .map_err(protocol_error_to_grpc_status)?; + + let update_state = UpdateState { user_id }; + let session_id = self + .cache + .insert_with_uuid_key(WorkflowInProgress::Update(update_state)) + .await; + + let response = auth_proto::UpdateUserPasswordStartResponse { + session_id, + opaque_registration_response: server_message, + }; + Ok(Response::new(response)) + } + + async fn update_user_password_finish( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let message = request.into_inner(); + + let Some(WorkflowInProgress::Update(state)) = + self.cache.get(&message.session_id) + else { + return Err(tonic::Status::not_found("session not found")); + }; + + self.cache.invalidate(&message.session_id).await; + + let server_registration = comm_opaque2::server::Registration::new(); + let password_file = server_registration + .finish(&message.opaque_registration_upload) + .map_err(protocol_error_to_grpc_status)?; + + self + .db_client + .update_user_password(state.user_id, password_file) + .await + .map_err(handle_db_error)?; + + let response = Empty {}; + Ok(Response::new(response)) + } } diff --git a/services/identity/src/main.rs b/services/identity/src/main.rs --- a/services/identity/src/main.rs +++ b/services/identity/src/main.rs @@ -80,13 +80,13 @@ .time_to_live(Duration::from_secs(10)) .build(); let inner_client_service = - ClientService::new(database_client.clone(), workflow_cache); + ClientService::new(database_client.clone(), workflow_cache.clone()); let client_service = IdentityClientServiceServer::with_interceptor( inner_client_service, grpc_services::shared::version_interceptor, ); let inner_auth_service = - AuthenticatedService::new(database_client.clone()); + AuthenticatedService::new(database_client.clone(), workflow_cache); let auth_service = AuthServer::with_interceptor(inner_auth_service, move |req| { grpc_services::authenticated::auth_interceptor(req, &database_client) diff --git a/shared/protos/identity_authenticated.proto b/shared/protos/identity_authenticated.proto --- a/shared/protos/identity_authenticated.proto +++ b/shared/protos/identity_authenticated.proto @@ -19,6 +19,12 @@ rpc RefreshUserPreKeys(RefreshUserPreKeysRequest) returns (identity.client.Empty) {} + // Called by user to update password and receive new access token + rpc UpdateUserPasswordStart(UpdateUserPasswordStartRequest) returns + (UpdateUserPasswordStartResponse) {} + rpc UpdateUserPasswordFinish(UpdateUserPasswordFinishRequest) returns + (identity.client.Empty) {} + // Called by clients to get required keys for opening a connection // to a user's keyserver rpc GetKeyserverKeys(OutboundKeysForUserRequest) returns @@ -79,3 +85,27 @@ // none if user not found optional string userID = 1; } + +// UpdateUserPassword + +// Request for updating a user, similar to registration but need a +// access token to validate user before updating password +message UpdateUserPasswordStartRequest { + // Message sent to initiate PAKE registration (step 1) + bytes opaqueRegistrationRequest = 1; +} + +// Do a user registration, but overwrite the existing credentials +// after validation of user +message UpdateUserPasswordFinishRequest { + // Identifier used to correlate start and finish request + string sessionID = 1; + // Opaque client registration upload (step 3) + bytes opaqueRegistrationUpload = 2; +} + +message UpdateUserPasswordStartResponse { + // Identifier used to correlate start request with finish request + string sessionID = 1; + bytes opaqueRegistrationResponse = 2; +} diff --git a/shared/protos/identity_client.proto b/shared/protos/identity_client.proto --- a/shared/protos/identity_client.proto +++ b/shared/protos/identity_client.proto @@ -17,11 +17,6 @@ returns (RegistrationStartResponse) {} rpc RegisterPasswordUserFinish(RegistrationFinishRequest) returns ( RegistrationFinishResponse) {} - // Called by user to update password and receive new access token - rpc UpdateUserPasswordStart(UpdateUserPasswordStartRequest) returns - (UpdateUserPasswordStartResponse) {} - rpc UpdateUserPasswordFinish(UpdateUserPasswordFinishRequest) returns - (Empty) {} // Called by user to register device and get an access token rpc LoginPasswordUserStart(OpaqueLoginStartRequest) returns (OpaqueLoginStartResponse) {} @@ -174,36 +169,6 @@ string accessToken = 2; } -// UpdateUserPassword - -// Request for updating a user, similar to registration but need a -// access token to validate user before updating password -message UpdateUserPasswordStartRequest { - // Message sent to initiate PAKE registration (step 1) - bytes opaqueRegistrationRequest = 1; - // Used to validate user, before attempting to update password - string accessToken = 2; - string userID = 3; - // Public ed25519 key used for signing. We need this to look up a device's - // access token - string deviceIDKey = 4; -} - -// Do a user registration, but overwrite the existing credentials -// after validation of user -message UpdateUserPasswordFinishRequest { - // Identifier used to correlate start and finish request - string sessionID = 1; - // Opaque client registration upload (step 3) - bytes opaqueRegistrationUpload = 2; -} - -message UpdateUserPasswordStartResponse { - // Identifier used to correlate start request with finish request - string sessionID = 1; - bytes opaqueRegistrationResponse = 2; -} - // LoginUser message OpaqueLoginStartRequest {