feat(users): add endpoint for terminate auth select (#5135)

This commit is contained in:
Apoorv Dixit
2024-06-26 21:51:40 +05:30
committed by GitHub
parent ce7d0d427d
commit eb6afd66f2
8 changed files with 110 additions and 23 deletions

View File

@ -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")]

View File

@ -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,
}

View File

@ -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,
)
}

View File

@ -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")]

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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