From 80efeb76b1801529766978af1c06e2d2c7de66c0 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Mon, 4 Dec 2023 17:03:44 +0530 Subject: [PATCH] refactor(users): Separate signup and signin (#2921) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/events/user.rs | 15 +- crates/api_models/src/user.rs | 40 ++++- crates/router/src/core/user.rs | 215 ++++++++++++++++++------- crates/router/src/routes/app.rs | 21 ++- crates/router/src/routes/lock_utils.rs | 5 +- crates/router/src/routes/user.rs | 59 +++++++ crates/router/src/types/domain/user.rs | 144 ++++++++++++----- crates/router/src/utils/user.rs | 57 ++++++- crates/router_env/src/logger/types.rs | 6 + 9 files changed, 453 insertions(+), 109 deletions(-) diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 8b7cd02c9350..3634b51e0cc0 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -6,11 +6,12 @@ use crate::user::{ dashboard_metadata::{ GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest, }, - ChangePasswordRequest, ConnectAccountRequest, ConnectAccountResponse, - CreateInternalUserRequest, GetUsersResponse, SwitchMerchantIdRequest, UserMerchantCreate, + AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest, + DashboardEntryResponse, GetUsersResponse, SignUpRequest, SignUpWithMerchantIdRequest, + SwitchMerchantIdRequest, UserMerchantCreate, }; -impl ApiEventMetric for ConnectAccountResponse { +impl ApiEventMetric for DashboardEntryResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::User { merchant_id: self.merchant_id.clone(), @@ -19,9 +20,9 @@ impl ApiEventMetric for ConnectAccountResponse { } } -impl ApiEventMetric for ConnectAccountRequest {} - common_utils::impl_misc_api_event_type!( + SignUpRequest, + SignUpWithMerchantIdRequest, ChangePasswordRequest, GetMultipleMetaDataPayload, GetMetaDataResponse, @@ -30,7 +31,9 @@ common_utils::impl_misc_api_event_type!( SwitchMerchantIdRequest, CreateInternalUserRequest, UserMerchantCreate, - GetUsersResponse + GetUsersResponse, + AuthorizeResponse, + ConnectAccountRequest ); #[cfg(feature = "dummy_connector")] diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 36d730f5118e..287c377eb46a 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -7,13 +7,25 @@ pub mod dashboard_metadata; pub mod sample_data; #[derive(serde::Deserialize, Debug, Clone, serde::Serialize)] -pub struct ConnectAccountRequest { +pub struct SignUpWithMerchantIdRequest { + pub name: Secret, + pub email: pii::Email, + pub password: Secret, + pub company_name: String, +} + +pub type SignUpWithMerchantIdResponse = AuthorizeResponse; + +#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)] +pub struct SignUpRequest { pub email: pii::Email, pub password: Secret, } +pub type SignUpResponse = DashboardEntryResponse; + #[derive(serde::Serialize, Debug, Clone)] -pub struct ConnectAccountResponse { +pub struct DashboardEntryResponse { pub token: Secret, pub merchant_id: String, pub name: Secret, @@ -25,6 +37,28 @@ pub struct ConnectAccountResponse { pub user_id: String, } +pub type SignInRequest = SignUpRequest; + +pub type SignInResponse = DashboardEntryResponse; + +#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)] +pub struct ConnectAccountRequest { + pub email: pii::Email, +} + +pub type ConnectAccountResponse = AuthorizeResponse; + +#[derive(serde::Serialize, Debug, Clone)] +pub struct AuthorizeResponse { + pub is_email_sent: bool, + //this field is added for audit/debug reasons + #[serde(skip_serializing)] + pub user_id: String, + //this field is added for audit/debug reasons + #[serde(skip_serializing)] + pub merchant_id: String, +} + #[derive(serde::Deserialize, Debug, serde::Serialize)] pub struct ChangePasswordRequest { pub new_password: Secret, @@ -36,6 +70,8 @@ pub struct SwitchMerchantIdRequest { pub merchant_id: String, } +pub type SwitchMerchantResponse = DashboardEntryResponse; + #[derive(serde::Deserialize, Debug, serde::Serialize)] pub struct CreateInternalUserRequest { pub name: Secret, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 7d0d599cc4ed..c868530f81af 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1,10 +1,17 @@ use api_models::user as user_api; use diesel_models::{enums::UserStatus, user as storage_user}; -use error_stack::{IntoReport, ResultExt}; -use masking::{ExposeInterface, Secret}; +#[cfg(feature = "email")] +use error_stack::IntoReport; +use error_stack::ResultExt; +use masking::ExposeInterface; +#[cfg(feature = "email")] use router_env::env; +#[cfg(feature = "email")] +use router_env::logger; use super::errors::{UserErrors, UserResponse}; +#[cfg(feature = "email")] +use crate::services::email::types as email_types; use crate::{ consts, db::user::UserInterface, @@ -13,11 +20,112 @@ use crate::{ types::domain, utils, }; +pub mod dashboard_metadata; #[cfg(feature = "dummy_connector")] pub mod sample_data; -pub mod dashboard_metadata; +#[cfg(feature = "email")] +pub async fn signup_with_merchant_id( + state: AppState, + request: user_api::SignUpWithMerchantIdRequest, +) -> UserResponse { + let new_user = domain::NewUser::try_from(request.clone())?; + new_user + .get_new_merchant() + .get_new_organization() + .insert_org_in_db(state.clone()) + .await?; + + let user_from_db = new_user + .insert_user_and_merchant_in_db(state.clone()) + .await?; + + let user_role = new_user + .insert_user_role_in_db( + state.clone(), + consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), + UserStatus::Active, + ) + .await?; + + let email_contents = email_types::ResetPassword { + recipient_email: user_from_db.get_email().try_into()?, + user_name: domain::UserName::new(user_from_db.get_name())?, + settings: state.conf.clone(), + subject: "Get back to Hyperswitch - Reset Your Password Now", + }; + + let send_email_result = state + .email_client + .compose_and_send_email( + Box::new(email_contents), + state.conf.proxy.https_url.as_ref(), + ) + .await; + + logger::info!(?send_email_result); + Ok(ApplicationResponse::Json(user_api::AuthorizeResponse { + is_email_sent: send_email_result.is_ok(), + user_id: user_from_db.get_user_id().to_string(), + merchant_id: user_role.merchant_id, + })) +} + +pub async fn signup( + state: AppState, + request: user_api::SignUpRequest, +) -> UserResponse { + let new_user = domain::NewUser::try_from(request)?; + new_user + .get_new_merchant() + .get_new_organization() + .insert_org_in_db(state.clone()) + .await?; + let user_from_db = new_user + .insert_user_and_merchant_in_db(state.clone()) + .await?; + let user_role = new_user + .insert_user_role_in_db( + state.clone(), + consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), + UserStatus::Active, + ) + .await?; + let token = utils::user::generate_jwt_auth_token(state, &user_from_db, &user_role).await?; + Ok(ApplicationResponse::Json( + utils::user::get_dashboard_entry_response(user_from_db, user_role, token), + )) +} + +pub async fn signin( + state: AppState, + request: user_api::SignInRequest, +) -> UserResponse { + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_email(request.email.clone().expose().expose().as_str()) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(UserErrors::InvalidCredentials) + } else { + e.change_context(UserErrors::InternalServerError) + } + })? + .into(); + + user_from_db.compare_password(request.password)?; + + let user_role = user_from_db.get_role_from_db(state.clone()).await?; + let token = utils::user::generate_jwt_auth_token(state, &user_from_db, &user_role).await?; + + Ok(ApplicationResponse::Json( + utils::user::get_dashboard_entry_response(user_from_db, user_role, token), + )) +} + +#[cfg(feature = "email")] pub async fn connect_account( state: AppState, request: user_api::ConnectAccountRequest, @@ -29,26 +137,34 @@ pub async fn connect_account( if let Ok(found_user) = find_user { let user_from_db: domain::UserFromStorage = found_user.into(); + let user_role = user_from_db.get_role_from_db(state.clone()).await?; - user_from_db.compare_password(request.password)?; + let email_contents = email_types::MagicLink { + recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?, + settings: state.conf.clone(), + user_name: domain::UserName::new(user_from_db.get_name())?, + subject: "Unlock Hyperswitch: Use Your Magic Link to Sign In", + }; + + let send_email_result = state + .email_client + .compose_and_send_email( + Box::new(email_contents), + state.conf.proxy.https_url.as_ref(), + ) + .await; - let user_role = user_from_db.get_role_from_db(state.clone()).await?; - let jwt_token = user_from_db - .get_jwt_auth_token(state.clone(), user_role.org_id) - .await?; + logger::info!(?send_email_result); return Ok(ApplicationResponse::Json( user_api::ConnectAccountResponse { - token: Secret::new(jwt_token), - merchant_id: user_role.merchant_id, - name: user_from_db.get_name(), - email: user_from_db.get_email(), - verification_days_left: None, - user_role: user_role.role_id, + is_email_sent: send_email_result.is_ok(), user_id: user_from_db.get_user_id().to_string(), + merchant_id: user_role.merchant_id, }, )); } else if find_user + .as_ref() .map_err(|e| e.current_context().is_db_not_found()) .err() .unwrap_or(false) @@ -73,46 +189,35 @@ pub async fn connect_account( UserStatus::Active, ) .await?; - let jwt_token = user_from_db - .get_jwt_auth_token(state.clone(), user_role.org_id) - .await?; - - #[cfg(feature = "email")] - { - use router_env::logger; - - use crate::services::email::types as email_types; - - let email_contents = email_types::VerifyEmail { - recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?, - settings: state.conf.clone(), - subject: "Welcome to the Hyperswitch community!", - }; - let send_email_result = state - .email_client - .compose_and_send_email( - Box::new(email_contents), - state.conf.proxy.https_url.as_ref(), - ) - .await; + let email_contents = email_types::VerifyEmail { + recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?, + settings: state.conf.clone(), + subject: "Welcome to the Hyperswitch community!", + }; + + let send_email_result = state + .email_client + .compose_and_send_email( + Box::new(email_contents), + state.conf.proxy.https_url.as_ref(), + ) + .await; - logger::info!(?send_email_result); - } + logger::info!(?send_email_result); return Ok(ApplicationResponse::Json( user_api::ConnectAccountResponse { - token: Secret::new(jwt_token), - merchant_id: user_role.merchant_id, - name: user_from_db.get_name(), - email: user_from_db.get_email(), - verification_days_left: None, - user_role: user_role.role_id, + is_email_sent: send_email_result.is_ok(), user_id: user_from_db.get_user_id().to_string(), + merchant_id: user_role.merchant_id, }, )); } else { - Err(UserErrors::InternalServerError.into()) + Err(find_user + .err() + .map(|e| e.change_context(UserErrors::InternalServerError)) + .unwrap_or(UserErrors::InternalServerError.into())) } } @@ -215,7 +320,7 @@ pub async fn switch_merchant_id( state: AppState, request: user_api::SwitchMerchantIdRequest, user_from_token: auth::UserFromToken, -) -> UserResponse { +) -> UserResponse { if !utils::user_role::is_internal_role(&user_from_token.role_id) { let merchant_list = utils::user_role::get_merchant_ids_for_user(state.clone(), &user_from_token.user_id) @@ -252,7 +357,7 @@ pub async fn switch_merchant_id( } })?; - let org_id = state + let _org_id = state .store .find_merchant_account_by_merchant_id(request.merchant_id.as_str(), &key_store) .await @@ -272,23 +377,23 @@ pub async fn switch_merchant_id( .await .change_context(UserErrors::InternalServerError)?; - let token = Box::pin(user.get_jwt_auth_token_with_custom_merchant_id( - state.clone(), + let token = utils::user::generate_jwt_auth_token_with_custom_merchant_id( + state, + &user, + &user_role, request.merchant_id.clone(), - org_id, - )) - .await? - .into(); + ) + .await?; Ok(ApplicationResponse::Json( - user_api::ConnectAccountResponse { - merchant_id: request.merchant_id, + user_api::SwitchMerchantResponse { token, name: user.get_name(), email: user.get_email(), user_id: user.get_user_id().to_string(), verification_days_left: None, user_role: user_role.role_id, + merchant_id: user_role.merchant_id, }, )) } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index a145f3e7e5d7..88806b565d3a 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -823,10 +823,7 @@ impl User { let mut route = web::scope("/user").app_data(web::Data::new(state)); route = route - .service(web::resource("/signin").route(web::post().to(user_connect_account))) - .service(web::resource("/signup").route(web::post().to(user_connect_account))) - .service(web::resource("/v2/signin").route(web::post().to(user_connect_account))) - .service(web::resource("/v2/signup").route(web::post().to(user_connect_account))) + .service(web::resource("/signin").route(web::post().to(user_signin))) .service(web::resource("/change_password").route(web::post().to(change_password))) .service( web::resource("/data/merchant") @@ -841,7 +838,6 @@ impl User { ) .service(web::resource("/switch/list").route(web::get().to(list_merchant_ids_for_user))) .service(web::resource("/user/list").route(web::get().to(get_user_details))) - // User Role APIs .service(web::resource("/permission_info").route(web::get().to(get_authorization_info))) .service(web::resource("/user/update_role").route(web::post().to(update_user_role))) .service(web::resource("/role/list").route(web::get().to(list_roles))) @@ -855,6 +851,21 @@ impl User { .route(web::delete().to(delete_sample_data)), ) } + #[cfg(feature = "email")] + { + route = route + .service( + web::resource("/connect_account").route(web::post().to(user_connect_account)), + ) + .service( + web::resource("/signup_with_merchant_id") + .route(web::post().to(user_signup_with_merchant_id)), + ); + } + #[cfg(not(feature = "email"))] + { + route = route.service(web::resource("/signup").route(web::post().to(user_signup))) + } route } } diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 6aa2bbad0b15..b32dbe3d4b6a 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -149,6 +149,8 @@ impl From for ApiIdentifier { | Flow::GsmRuleDelete => Self::Gsm, Flow::UserConnectAccount + | Flow::UserSignUp + | Flow::UserSignIn | Flow::ChangePassword | Flow::SetDashboardMetadata | Flow::GetMutltipleDashboardMetadata @@ -159,7 +161,8 @@ impl From for ApiIdentifier { | Flow::GenerateSampleData | Flow::DeleteSampleData | Flow::UserMerchantAccountList - | Flow::GetUserDetails => Self::User, + | Flow::GetUserDetails + | Flow::UserSignUpWithMerchantId => Self::User, Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => { Self::UserRole diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index 97bd7054da9e..45fa0ba35c59 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -19,6 +19,65 @@ use crate::{ utils::user::dashboard_metadata::{parse_string_to_enums, set_ip_address_if_required}, }; +#[cfg(feature = "email")] +pub async fn user_signup_with_merchant_id( + state: web::Data, + http_req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::UserSignUpWithMerchantId; + let req_payload = json_payload.into_inner(); + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + req_payload.clone(), + |state, _, req_body| user_core::signup_with_merchant_id(state, req_body), + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +pub async fn user_signup( + state: web::Data, + http_req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::UserSignUp; + let req_payload = json_payload.into_inner(); + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + req_payload.clone(), + |state, _, req_body| user_core::signup(state, req_body), + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +pub async fn user_signin( + state: web::Data, + http_req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::UserSignIn; + let req_payload = json_payload.into_inner(); + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + req_payload.clone(), + |state, _, req_body| user_core::signin(state, req_body), + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(feature = "email")] pub async fn user_connect_account( state: web::Data, http_req: HttpRequest, diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 082b29d80941..592195922493 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -26,7 +26,7 @@ use crate::{ db::StorageInterface, routes::AppState, services::{ - authentication::{AuthToken, UserFromToken}, + authentication::UserFromToken, authorization::{info, predefined_permissions}, }, types::transformers::ForeignFrom, @@ -215,6 +215,25 @@ impl NewUserOrganization { } } +impl TryFrom for NewUserOrganization { + type Error = error_stack::Report; + fn try_from(value: user_api::SignUpWithMerchantIdRequest) -> UserResult { + let new_organization = api_org::OrganizationNew::new(Some( + UserCompanyName::new(value.company_name)?.get_secret(), + )); + let db_organization = ForeignFrom::foreign_from(new_organization); + Ok(Self(db_organization)) + } +} + +impl From for NewUserOrganization { + fn from(_value: user_api::SignUpRequest) -> Self { + let new_organization = api_org::OrganizationNew::new(None); + let db_organization = ForeignFrom::foreign_from(new_organization); + Self(db_organization) + } +} + impl From for NewUserOrganization { fn from(_value: user_api::ConnectAccountRequest) -> Self { let new_organization = api_org::OrganizationNew::new(None); @@ -334,6 +353,24 @@ impl NewUserMerchant { } } +impl TryFrom for NewUserMerchant { + type Error = error_stack::Report; + + fn try_from(value: user_api::SignUpRequest) -> UserResult { + let merchant_id = MerchantId::new(format!( + "merchant_{}", + common_utils::date_time::now_unix_timestamp() + ))?; + let new_organization = NewUserOrganization::from(value); + + Ok(Self { + company_name: None, + merchant_id, + new_organization, + }) + } +} + impl TryFrom for NewUserMerchant { type Error = error_stack::Report; @@ -352,6 +389,21 @@ impl TryFrom for NewUserMerchant { } } +impl TryFrom for NewUserMerchant { + type Error = error_stack::Report; + fn try_from(value: user_api::SignUpWithMerchantIdRequest) -> UserResult { + let company_name = Some(UserCompanyName::new(value.company_name.clone())?); + let merchant_id = MerchantId::new(value.company_name.clone())?; + let new_organization = NewUserOrganization::try_from(value)?; + + Ok(Self { + company_name, + merchant_id, + new_organization, + }) + } +} + impl TryFrom for NewUserMerchant { type Error = error_stack::Report; @@ -434,10 +486,23 @@ impl NewUser { .attach_printable("Error while inserting user") } + pub async fn check_if_already_exists_in_db(&self, state: AppState) -> UserResult<()> { + if state + .store + .find_user_by_email(self.get_email().into_inner().expose().expose().as_str()) + .await + .is_ok() + { + return Err(UserErrors::UserExists).into_report(); + } + Ok(()) + } + pub async fn insert_user_and_merchant_in_db( &self, state: AppState, ) -> UserResult { + self.check_if_already_exists_in_db(state.clone()).await?; let db = state.store.as_ref(); let merchant_id = self.get_new_merchant().get_merchant_id(); self.new_merchant @@ -495,6 +560,46 @@ impl TryFrom for storage_user::UserNew { } } +impl TryFrom for NewUser { + type Error = error_stack::Report; + + fn try_from(value: user_api::SignUpWithMerchantIdRequest) -> UserResult { + let email = value.email.clone().try_into()?; + let name = UserName::new(value.name.clone())?; + let password = UserPassword::new(value.password.clone())?; + let user_id = uuid::Uuid::new_v4().to_string(); + let new_merchant = NewUserMerchant::try_from(value)?; + + Ok(Self { + name, + email, + password, + user_id, + new_merchant, + }) + } +} + +impl TryFrom for NewUser { + type Error = error_stack::Report; + + fn try_from(value: user_api::SignUpRequest) -> UserResult { + let user_id = uuid::Uuid::new_v4().to_string(); + let email = value.email.clone().try_into()?; + let name = UserName::try_from(value.email.clone())?; + let password = UserPassword::new(value.password.clone())?; + let new_merchant = NewUserMerchant::try_from(value)?; + + Ok(Self { + user_id, + name, + email, + password, + new_merchant, + }) + } +} + impl TryFrom for NewUser { type Error = error_stack::Report; @@ -502,7 +607,7 @@ impl TryFrom for NewUser { let user_id = uuid::Uuid::new_v4().to_string(); let email = value.email.clone().try_into()?; let name = UserName::try_from(value.email.clone())?; - let password = UserPassword::new(value.password.clone())?; + let password = UserPassword::new(uuid::Uuid::new_v4().to_string().into())?; let new_merchant = NewUserMerchant::try_from(value)?; Ok(Self { @@ -582,41 +687,6 @@ impl UserFromStorage { self.0.email.clone() } - pub async fn get_jwt_auth_token(&self, state: AppState, org_id: String) -> UserResult { - let role_id = self.get_role_from_db(state.clone()).await?.role_id; - let merchant_id = state - .store - .find_user_role_by_user_id(self.get_user_id()) - .await - .change_context(UserErrors::InternalServerError)? - .merchant_id; - AuthToken::new_token( - self.0.user_id.clone(), - merchant_id, - role_id, - &state.conf, - org_id, - ) - .await - } - - pub async fn get_jwt_auth_token_with_custom_merchant_id( - &self, - state: AppState, - merchant_id: String, - org_id: String, - ) -> UserResult { - let role_id = self.get_role_from_db(state.clone()).await?.role_id; - AuthToken::new_token( - self.0.user_id.clone(), - merchant_id, - role_id, - &state.conf, - org_id, - ) - .await - } - pub async fn get_role_from_db(&self, state: AppState) -> UserResult { state .store diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index 696aa4090044..0403d9b453d0 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -1,11 +1,13 @@ -use diesel_models::enums::UserStatus; +use api_models::user as user_api; +use diesel_models::{enums::UserStatus, user_role::UserRole}; use error_stack::ResultExt; +use masking::Secret; use crate::{ core::errors::{UserErrors, UserResult}, routes::AppState, - services::authentication::UserFromToken, - types::domain::MerchantAccount, + services::authentication::{AuthToken, UserFromToken}, + types::domain::{MerchantAccount, UserFromStorage}, }; pub mod dashboard_metadata; @@ -68,3 +70,52 @@ pub async fn get_merchant_ids_for_user(state: AppState, user_id: &str) -> UserRe }) .collect()) } + +pub async fn generate_jwt_auth_token( + state: AppState, + user: &UserFromStorage, + user_role: &UserRole, +) -> UserResult> { + let token = AuthToken::new_token( + user.get_user_id().to_string(), + user_role.merchant_id.clone(), + user_role.role_id.clone(), + &state.conf, + user_role.org_id.clone(), + ) + .await?; + Ok(Secret::new(token)) +} + +pub async fn generate_jwt_auth_token_with_custom_merchant_id( + state: AppState, + user: &UserFromStorage, + user_role: &UserRole, + merchant_id: String, +) -> UserResult> { + let token = AuthToken::new_token( + user.get_user_id().to_string(), + merchant_id, + user_role.role_id.clone(), + &state.conf, + user_role.org_id.to_owned(), + ) + .await?; + Ok(Secret::new(token)) +} + +pub fn get_dashboard_entry_response( + user: UserFromStorage, + user_role: UserRole, + token: Secret, +) -> user_api::DashboardEntryResponse { + user_api::DashboardEntryResponse { + merchant_id: user_role.merchant_id, + token, + name: user.get_name(), + email: user.get_email(), + user_id: user.get_user_id().to_string(), + verification_days_left: None, + user_role: user_role.role_id, + } +} diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index f54a5a82baaf..606ae1f2f169 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -249,6 +249,12 @@ pub enum Flow { GsmRuleUpdate, /// Gsm Rule Delete flow GsmRuleDelete, + /// User Sign Up + UserSignUp, + /// User Sign Up + UserSignUpWithMerchantId, + /// User Sign In + UserSignIn, /// User connect account UserConnectAccount, /// Upsert Decision Manager Config