mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	feat(users): add endpoint for terminate auth select (#5135)
This commit is contained in:
		| @ -10,16 +10,17 @@ use crate::user::{ | |||||||
|     dashboard_metadata::{ |     dashboard_metadata::{ | ||||||
|         GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest, |         GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest, | ||||||
|     }, |     }, | ||||||
|     AcceptInviteFromEmailRequest, AuthorizeResponse, BeginTotpResponse, ChangePasswordRequest, |     AcceptInviteFromEmailRequest, AuthSelectRequest, AuthorizeResponse, BeginTotpResponse, | ||||||
|     ConnectAccountRequest, CreateInternalUserRequest, CreateUserAuthenticationMethodRequest, |     ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest, | ||||||
|     DashboardEntryResponse, ForgotPasswordRequest, GetSsoAuthUrlRequest, |     CreateUserAuthenticationMethodRequest, DashboardEntryResponse, ForgotPasswordRequest, | ||||||
|     GetUserAuthenticationMethodsRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest, |     GetSsoAuthUrlRequest, GetUserAuthenticationMethodsRequest, GetUserDetailsResponse, | ||||||
|     GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, ReInviteUserRequest, |     GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, | ||||||
|     RecoveryCodes, ResetPasswordRequest, RotatePasswordRequest, SendVerifyEmailRequest, |     ReInviteUserRequest, RecoveryCodes, ResetPasswordRequest, RotatePasswordRequest, | ||||||
|     SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, SsoSignInRequest, |     SendVerifyEmailRequest, SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, | ||||||
|     SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, TwoFactorAuthStatusResponse, |     SsoSignInRequest, SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, | ||||||
|     UpdateUserAccountDetailsRequest, UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, |     TwoFactorAuthStatusResponse, UpdateUserAccountDetailsRequest, | ||||||
|     UserMerchantCreate, VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest, |     UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, UserMerchantCreate, | ||||||
|  |     VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| impl ApiEventMetric for DashboardEntryResponse { | impl ApiEventMetric for DashboardEntryResponse { | ||||||
| @ -83,7 +84,8 @@ common_utils::impl_misc_api_event_type!( | |||||||
|     CreateUserAuthenticationMethodRequest, |     CreateUserAuthenticationMethodRequest, | ||||||
|     UpdateUserAuthenticationMethodRequest, |     UpdateUserAuthenticationMethodRequest, | ||||||
|     GetSsoAuthUrlRequest, |     GetSsoAuthUrlRequest, | ||||||
|     SsoSignInRequest |     SsoSignInRequest, | ||||||
|  |     AuthSelectRequest | ||||||
| ); | ); | ||||||
|  |  | ||||||
| #[cfg(feature = "dummy_connector")] | #[cfg(feature = "dummy_connector")] | ||||||
|  | |||||||
| @ -372,3 +372,8 @@ pub struct SsoSignInRequest { | |||||||
| pub struct AuthIdQueryParam { | pub struct AuthIdQueryParam { | ||||||
|     pub auth_id: Option<String>, |     pub auth_id: Option<String>, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, serde::Deserialize, serde::Serialize)] | ||||||
|  | pub struct AuthSelectRequest { | ||||||
|  |     pub id: String, | ||||||
|  | } | ||||||
|  | |||||||
| @ -2312,3 +2312,41 @@ pub async fn sso_sign( | |||||||
|  |  | ||||||
|     auth::cookies::set_cookie_response(response, token) |     auth::cookies::set_cookie_response(response, token) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub async fn terminate_auth_select( | ||||||
|  |     state: SessionState, | ||||||
|  |     user_token: auth::UserFromSinglePurposeToken, | ||||||
|  |     req: user_api::AuthSelectRequest, | ||||||
|  | ) -> UserResponse<user_api::TokenResponse> { | ||||||
|  |     let user_from_db: domain::UserFromStorage = state | ||||||
|  |         .global_store | ||||||
|  |         .find_user_by_id(&user_token.user_id) | ||||||
|  |         .await | ||||||
|  |         .change_context(UserErrors::InternalServerError)? | ||||||
|  |         .into(); | ||||||
|  |  | ||||||
|  |     let user_authentication_method = state | ||||||
|  |         .store | ||||||
|  |         .get_user_authentication_method_by_id(&req.id) | ||||||
|  |         .await | ||||||
|  |         .change_context(UserErrors::InternalServerError)?; | ||||||
|  |  | ||||||
|  |     let current_flow = domain::CurrentFlow::new(user_token, domain::SPTFlow::AuthSelect.into())?; | ||||||
|  |     let mut next_flow = current_flow.next(user_from_db.clone(), &state).await?; | ||||||
|  |  | ||||||
|  |     // Skip SSO if continue with password(TOTP) | ||||||
|  |     if next_flow.get_flow() == domain::UserFlow::SPTFlow(domain::SPTFlow::SSO) | ||||||
|  |         && user_authentication_method.auth_type != common_enums::UserAuthType::OpenIdConnect | ||||||
|  |     { | ||||||
|  |         next_flow = next_flow.skip(user_from_db, &state).await?; | ||||||
|  |     } | ||||||
|  |     let token = next_flow.get_token(&state).await?; | ||||||
|  |  | ||||||
|  |     auth::cookies::set_cookie_response( | ||||||
|  |         user_api::TokenResponse { | ||||||
|  |             token: token.clone(), | ||||||
|  |             token_type: next_flow.get_flow().into(), | ||||||
|  |         }, | ||||||
|  |         token, | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | |||||||
| @ -1417,7 +1417,8 @@ impl User { | |||||||
|                 .service( |                 .service( | ||||||
|                     web::resource("/list").route(web::get().to(list_user_authentication_methods)), |                     web::resource("/list").route(web::get().to(list_user_authentication_methods)), | ||||||
|                 ) |                 ) | ||||||
|                 .service(web::resource("/url").route(web::get().to(get_sso_auth_url))), |                 .service(web::resource("/url").route(web::get().to(get_sso_auth_url))) | ||||||
|  |                 .service(web::resource("/select").route(web::post().to(terminate_auth_select))), | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         #[cfg(feature = "email")] |         #[cfg(feature = "email")] | ||||||
|  | |||||||
| @ -232,7 +232,8 @@ impl From<Flow> for ApiIdentifier { | |||||||
|             | Flow::UpdateUserAuthenticationMethod |             | Flow::UpdateUserAuthenticationMethod | ||||||
|             | Flow::ListUserAuthenticationMethods |             | Flow::ListUserAuthenticationMethods | ||||||
|             | Flow::GetSsoAuthUrl |             | Flow::GetSsoAuthUrl | ||||||
|             | Flow::SignInWithSso => Self::User, |             | Flow::SignInWithSso | ||||||
|  |             | Flow::AuthSelect => Self::User, | ||||||
|  |  | ||||||
|             Flow::ListRoles |             Flow::ListRoles | ||||||
|             | Flow::GetRole |             | Flow::GetRole | ||||||
|  | |||||||
| @ -876,3 +876,22 @@ pub async fn list_user_authentication_methods( | |||||||
|     )) |     )) | ||||||
|     .await |     .await | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub async fn terminate_auth_select( | ||||||
|  |     state: web::Data<AppState>, | ||||||
|  |     req: HttpRequest, | ||||||
|  |     json_payload: web::Json<user_api::AuthSelectRequest>, | ||||||
|  | ) -> HttpResponse { | ||||||
|  |     let flow = Flow::AuthSelect; | ||||||
|  |  | ||||||
|  |     Box::pin(api::server_wrap( | ||||||
|  |         flow, | ||||||
|  |         state.clone(), | ||||||
|  |         &req, | ||||||
|  |         json_payload.into_inner(), | ||||||
|  |         |state, user, req, _| user_core::terminate_auth_select(state, user, req), | ||||||
|  |         &auth::SinglePurposeJWTAuth(TokenPurpose::AuthSelect), | ||||||
|  |         api_locking::LockAction::NotApplicable, | ||||||
|  |     )) | ||||||
|  |     .await | ||||||
|  | } | ||||||
|  | |||||||
| @ -51,9 +51,8 @@ impl SPTFlow { | |||||||
|     ) -> UserResult<bool> { |     ) -> UserResult<bool> { | ||||||
|         match self { |         match self { | ||||||
|             // Auth |             // Auth | ||||||
|             // AuthSelect and SSO flow are not enabled, once the terminate SSO API is ready, we can enable these flows |             Self::AuthSelect => Ok(true), | ||||||
|             Self::AuthSelect => Ok(false), |             Self::SSO => Ok(true), | ||||||
|             Self::SSO => Ok(false), |  | ||||||
|             // TOTP |             // TOTP | ||||||
|             Self::TOTP => Ok(!path.contains(&TokenPurpose::SSO)), |             Self::TOTP => Ok(!path.contains(&TokenPurpose::SSO)), | ||||||
|             // Main email APIs |             // Main email APIs | ||||||
| @ -311,6 +310,26 @@ impl NextFlow { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub async fn skip(self, user: UserFromStorage, state: &SessionState) -> UserResult<Self> { | ||||||
|  |         let flows = self.origin.get_flows(); | ||||||
|  |         let index = flows | ||||||
|  |             .iter() | ||||||
|  |             .position(|flow| flow == &self.get_flow()) | ||||||
|  |             .ok_or(UserErrors::InternalServerError)?; | ||||||
|  |         let remaining_flows = flows.iter().skip(index + 1); | ||||||
|  |         for flow in remaining_flows { | ||||||
|  |             if flow.is_required(&user, &self.path, state).await? { | ||||||
|  |                 return Ok(Self { | ||||||
|  |                     origin: self.origin.clone(), | ||||||
|  |                     next_flow: *flow, | ||||||
|  |                     user, | ||||||
|  |                     path: self.path, | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Err(UserErrors::InternalServerError.into()) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<UserFlow> for TokenPurpose { | impl From<UserFlow> for TokenPurpose { | ||||||
|  | |||||||
| @ -155,7 +155,7 @@ pub enum Flow { | |||||||
|     PaymentsStart, |     PaymentsStart, | ||||||
|     /// Payments list flow. |     /// Payments list flow. | ||||||
|     PaymentsList, |     PaymentsList, | ||||||
|     // Payments filters flow |     /// Payments filters flow | ||||||
|     PaymentsFilters, |     PaymentsFilters, | ||||||
|     #[cfg(feature = "payouts")] |     #[cfg(feature = "payouts")] | ||||||
|     /// Payouts create flow |     /// Payouts create flow | ||||||
| @ -412,7 +412,7 @@ pub enum Flow { | |||||||
|     UserFromEmail, |     UserFromEmail, | ||||||
|     /// Begin TOTP |     /// Begin TOTP | ||||||
|     TotpBegin, |     TotpBegin, | ||||||
|     // Reset TOTP |     /// Reset TOTP | ||||||
|     TotpReset, |     TotpReset, | ||||||
|     /// Verify TOTP |     /// Verify TOTP | ||||||
|     TotpVerify, |     TotpVerify, | ||||||
| @ -422,20 +422,22 @@ pub enum Flow { | |||||||
|     RecoveryCodeVerify, |     RecoveryCodeVerify, | ||||||
|     /// Generate or Regenerate recovery codes |     /// Generate or Regenerate recovery codes | ||||||
|     RecoveryCodesGenerate, |     RecoveryCodesGenerate, | ||||||
|     // Terminate two factor authentication |     /// Terminate two factor authentication | ||||||
|     TerminateTwoFactorAuth, |     TerminateTwoFactorAuth, | ||||||
|     // Check 2FA status |     /// Check 2FA status | ||||||
|     TwoFactorAuthStatus, |     TwoFactorAuthStatus, | ||||||
|     // Create user authentication method |     /// Create user authentication method | ||||||
|     CreateUserAuthenticationMethod, |     CreateUserAuthenticationMethod, | ||||||
|     // Update user authentication method |     /// Update user authentication method | ||||||
|     UpdateUserAuthenticationMethod, |     UpdateUserAuthenticationMethod, | ||||||
|     // List user authentication methods |     /// List user authentication methods | ||||||
|     ListUserAuthenticationMethods, |     ListUserAuthenticationMethods, | ||||||
|     /// Get sso auth url |     /// Get sso auth url | ||||||
|     GetSsoAuthUrl, |     GetSsoAuthUrl, | ||||||
|     /// Signin with SSO |     /// Signin with SSO | ||||||
|     SignInWithSso, |     SignInWithSso, | ||||||
|  |     /// Auth Select | ||||||
|  |     AuthSelect, | ||||||
|     /// List initial webhook delivery attempts |     /// List initial webhook delivery attempts | ||||||
|     WebhookEventInitialDeliveryAttemptList, |     WebhookEventInitialDeliveryAttemptList, | ||||||
|     /// List delivery attempts for a webhook event |     /// List delivery attempts for a webhook event | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Apoorv Dixit
					Apoorv Dixit