Skip to content

Commit

Permalink
feat(users): add endpoint for terminate auth select (#5135)
Browse files Browse the repository at this point in the history
  • Loading branch information
apoorvdixit88 authored Jun 26, 2024
1 parent ce7d0d4 commit eb6afd6
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 23 deletions.
24 changes: 13 additions & 11 deletions crates/api_models/src/events/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ use crate::user::{
dashboard_metadata::{
GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest,
},
AcceptInviteFromEmailRequest, AuthorizeResponse, BeginTotpResponse, ChangePasswordRequest,
ConnectAccountRequest, CreateInternalUserRequest, CreateUserAuthenticationMethodRequest,
DashboardEntryResponse, ForgotPasswordRequest, GetSsoAuthUrlRequest,
GetUserAuthenticationMethodsRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest,
GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, ReInviteUserRequest,
RecoveryCodes, ResetPasswordRequest, RotatePasswordRequest, SendVerifyEmailRequest,
SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, SsoSignInRequest,
SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, TwoFactorAuthStatusResponse,
UpdateUserAccountDetailsRequest, UpdateUserAuthenticationMethodRequest, UserFromEmailRequest,
UserMerchantCreate, VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest,
AcceptInviteFromEmailRequest, AuthSelectRequest, AuthorizeResponse, BeginTotpResponse,
ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest,
CreateUserAuthenticationMethodRequest, DashboardEntryResponse, ForgotPasswordRequest,
GetSsoAuthUrlRequest, GetUserAuthenticationMethodsRequest, GetUserDetailsResponse,
GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse,
ReInviteUserRequest, RecoveryCodes, ResetPasswordRequest, RotatePasswordRequest,
SendVerifyEmailRequest, SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest,
SsoSignInRequest, SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse,
TwoFactorAuthStatusResponse, UpdateUserAccountDetailsRequest,
UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, UserMerchantCreate,
VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest,
};

impl ApiEventMetric for DashboardEntryResponse {
Expand Down Expand Up @@ -83,7 +84,8 @@ common_utils::impl_misc_api_event_type!(
CreateUserAuthenticationMethodRequest,
UpdateUserAuthenticationMethodRequest,
GetSsoAuthUrlRequest,
SsoSignInRequest
SsoSignInRequest,
AuthSelectRequest
);

#[cfg(feature = "dummy_connector")]
Expand Down
5 changes: 5 additions & 0 deletions crates/api_models/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,8 @@ pub struct SsoSignInRequest {
pub struct AuthIdQueryParam {
pub auth_id: Option<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AuthSelectRequest {
pub id: String,
}
38 changes: 38 additions & 0 deletions crates/router/src/core/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2312,3 +2312,41 @@ pub async fn sso_sign(

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,
)
}
3 changes: 2 additions & 1 deletion crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1417,7 +1417,8 @@ impl User {
.service(
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")]
Expand Down
3 changes: 2 additions & 1 deletion crates/router/src/routes/lock_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ impl From<Flow> for ApiIdentifier {
| Flow::UpdateUserAuthenticationMethod
| Flow::ListUserAuthenticationMethods
| Flow::GetSsoAuthUrl
| Flow::SignInWithSso => Self::User,
| Flow::SignInWithSso
| Flow::AuthSelect => Self::User,

Flow::ListRoles
| Flow::GetRole
Expand Down
19 changes: 19 additions & 0 deletions crates/router/src/routes/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,3 +876,22 @@ pub async fn list_user_authentication_methods(
))
.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
}
25 changes: 22 additions & 3 deletions crates/router/src/types/domain/user/decision_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@ impl SPTFlow {
) -> UserResult<bool> {
match self {
// Auth
// AuthSelect and SSO flow are not enabled, once the terminate SSO API is ready, we can enable these flows
Self::AuthSelect => Ok(false),
Self::SSO => Ok(false),
Self::AuthSelect => Ok(true),
Self::SSO => Ok(true),
// TOTP
Self::TOTP => Ok(!path.contains(&TokenPurpose::SSO)),
// Main email APIs
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 9 additions & 7 deletions crates/router_env/src/logger/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub enum Flow {
PaymentsStart,
/// Payments list flow.
PaymentsList,
// Payments filters flow
/// Payments filters flow
PaymentsFilters,
#[cfg(feature = "payouts")]
/// Payouts create flow
Expand Down Expand Up @@ -412,7 +412,7 @@ pub enum Flow {
UserFromEmail,
/// Begin TOTP
TotpBegin,
// Reset TOTP
/// Reset TOTP
TotpReset,
/// Verify TOTP
TotpVerify,
Expand All @@ -422,20 +422,22 @@ pub enum Flow {
RecoveryCodeVerify,
/// Generate or Regenerate recovery codes
RecoveryCodesGenerate,
// Terminate two factor authentication
/// Terminate two factor authentication
TerminateTwoFactorAuth,
// Check 2FA status
/// Check 2FA status
TwoFactorAuthStatus,
// Create user authentication method
/// Create user authentication method
CreateUserAuthenticationMethod,
// Update user authentication method
/// Update user authentication method
UpdateUserAuthenticationMethod,
// List user authentication methods
/// List user authentication methods
ListUserAuthenticationMethods,
/// Get sso auth url
GetSsoAuthUrl,
/// Signin with SSO
SignInWithSso,
/// Auth Select
AuthSelect,
/// List initial webhook delivery attempts
WebhookEventInitialDeliveryAttemptList,
/// List delivery attempts for a webhook event
Expand Down

0 comments on commit eb6afd6

Please sign in to comment.