mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(payment_methods): Use Ephemeral auth for pm list and pm delete (#4996)
This commit is contained in:
@ -195,6 +195,7 @@ pub async fn list_customer_payment_method_api(
|
|||||||
auth.key_store,
|
auth.key_store,
|
||||||
Some(req),
|
Some(req),
|
||||||
Some(&customer_id),
|
Some(&customer_id),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
&auth::ApiKeyAuth,
|
&auth::ApiKeyAuth,
|
||||||
|
|||||||
@ -64,6 +64,7 @@ use crate::{
|
|||||||
pii::prelude::*,
|
pii::prelude::*,
|
||||||
routes::{
|
routes::{
|
||||||
self,
|
self,
|
||||||
|
app::SessionStateInfo,
|
||||||
metrics::{self, request},
|
metrics::{self, request},
|
||||||
payment_methods::ParentPaymentMethodToken,
|
payment_methods::ParentPaymentMethodToken,
|
||||||
},
|
},
|
||||||
@ -3107,10 +3108,25 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed(
|
|||||||
key_store: domain::MerchantKeyStore,
|
key_store: domain::MerchantKeyStore,
|
||||||
req: Option<api::PaymentMethodListRequest>,
|
req: Option<api::PaymentMethodListRequest>,
|
||||||
customer_id: Option<&id_type::CustomerId>,
|
customer_id: Option<&id_type::CustomerId>,
|
||||||
|
ephemeral_api_key: Option<&str>,
|
||||||
) -> errors::RouterResponse<api::CustomerPaymentMethodsListResponse> {
|
) -> errors::RouterResponse<api::CustomerPaymentMethodsListResponse> {
|
||||||
let db = state.store.as_ref();
|
let db = state.store.as_ref();
|
||||||
let limit = req.clone().and_then(|pml_req| pml_req.limit);
|
let limit = req.clone().and_then(|pml_req| pml_req.limit);
|
||||||
|
|
||||||
|
let auth_cust = if let Some(key) = ephemeral_api_key {
|
||||||
|
let key = state
|
||||||
|
.store()
|
||||||
|
.get_ephemeral_key(key)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::Unauthorized)?;
|
||||||
|
|
||||||
|
Some(key.customer_id.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let customer_id = customer_id.or(auth_cust.as_ref());
|
||||||
|
|
||||||
if let Some(customer_id) = customer_id {
|
if let Some(customer_id) = customer_id {
|
||||||
Box::pin(list_customer_payment_method(
|
Box::pin(list_customer_payment_method(
|
||||||
&state,
|
&state,
|
||||||
|
|||||||
@ -47,7 +47,7 @@ pub async fn customers_retrieve(
|
|||||||
let auth = if auth::is_jwt_auth(req.headers()) {
|
let auth = if auth::is_jwt_auth(req.headers()) {
|
||||||
Box::new(auth::JWTAuth(Permission::CustomerRead))
|
Box::new(auth::JWTAuth(Permission::CustomerRead))
|
||||||
} else {
|
} else {
|
||||||
match auth::is_ephemeral_auth(req.headers(), &payload.customer_id) {
|
match auth::is_ephemeral_auth(req.headers()) {
|
||||||
Ok(auth) => auth,
|
Ok(auth) => auth,
|
||||||
Err(err) => return api::log_and_return_error_response(err),
|
Err(err) => return api::log_and_return_error_response(err),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,7 +139,7 @@ pub async fn list_customer_payment_method_api(
|
|||||||
let payload = query_payload.into_inner();
|
let payload = query_payload.into_inner();
|
||||||
let customer_id = customer_id.into_inner().0;
|
let customer_id = customer_id.into_inner().0;
|
||||||
|
|
||||||
let ephemeral_auth = match auth::is_ephemeral_auth(req.headers(), &customer_id) {
|
let ephemeral_auth = match auth::is_ephemeral_auth(req.headers()) {
|
||||||
Ok(auth) => auth,
|
Ok(auth) => auth,
|
||||||
Err(err) => return api::log_and_return_error_response(err),
|
Err(err) => return api::log_and_return_error_response(err),
|
||||||
};
|
};
|
||||||
@ -155,6 +155,7 @@ pub async fn list_customer_payment_method_api(
|
|||||||
auth.key_store,
|
auth.key_store,
|
||||||
Some(req),
|
Some(req),
|
||||||
Some(&customer_id),
|
Some(&customer_id),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
&*ephemeral_auth,
|
&*ephemeral_auth,
|
||||||
@ -194,10 +195,12 @@ pub async fn list_customer_payment_method_api_client(
|
|||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
let flow = Flow::CustomerPaymentMethodsList;
|
let flow = Flow::CustomerPaymentMethodsList;
|
||||||
let payload = query_payload.into_inner();
|
let payload = query_payload.into_inner();
|
||||||
let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
|
let api_key = auth::get_api_key(req.headers()).ok();
|
||||||
Ok((auth, _auth_flow)) => (auth, _auth_flow),
|
let (auth, _, is_ephemeral_auth) =
|
||||||
Err(e) => return api::log_and_return_error_response(e),
|
match auth::get_ephemeral_or_other_auth(req.headers(), false, Some(&payload)).await {
|
||||||
};
|
Ok((auth, _auth_flow, is_ephemeral_auth)) => (auth, _auth_flow, is_ephemeral_auth),
|
||||||
|
Err(e) => return api::log_and_return_error_response(e),
|
||||||
|
};
|
||||||
|
|
||||||
Box::pin(api::server_wrap(
|
Box::pin(api::server_wrap(
|
||||||
flow,
|
flow,
|
||||||
@ -211,6 +214,7 @@ pub async fn list_customer_payment_method_api_client(
|
|||||||
auth.key_store,
|
auth.key_store,
|
||||||
Some(req),
|
Some(req),
|
||||||
None,
|
None,
|
||||||
|
is_ephemeral_auth.then_some(api_key).flatten(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
&*auth,
|
&*auth,
|
||||||
@ -291,6 +295,11 @@ pub async fn payment_method_delete_api(
|
|||||||
let pm = PaymentMethodId {
|
let pm = PaymentMethodId {
|
||||||
payment_method_id: payment_method_id.into_inner().0,
|
payment_method_id: payment_method_id.into_inner().0,
|
||||||
};
|
};
|
||||||
|
let ephemeral_auth = match auth::is_ephemeral_auth(req.headers()) {
|
||||||
|
Ok(auth) => auth,
|
||||||
|
Err(err) => return api::log_and_return_error_response(err),
|
||||||
|
};
|
||||||
|
|
||||||
Box::pin(api::server_wrap(
|
Box::pin(api::server_wrap(
|
||||||
flow,
|
flow,
|
||||||
state,
|
state,
|
||||||
@ -299,7 +308,7 @@ pub async fn payment_method_delete_api(
|
|||||||
|state, auth, req, _| {
|
|state, auth, req, _| {
|
||||||
cards::delete_payment_method(state, auth.merchant_account, req, auth.key_store)
|
cards::delete_payment_method(state, auth.merchant_account, req, auth.key_store)
|
||||||
},
|
},
|
||||||
&auth::ApiKeyAuth,
|
&*ephemeral_auth,
|
||||||
api_locking::LockAction::NotApplicable,
|
api_locking::LockAction::NotApplicable,
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
@ -345,7 +354,7 @@ pub async fn default_payment_method_set_api(
|
|||||||
let pc = payload.clone();
|
let pc = payload.clone();
|
||||||
let customer_id = &pc.customer_id;
|
let customer_id = &pc.customer_id;
|
||||||
|
|
||||||
let ephemeral_auth = match auth::is_ephemeral_auth(req.headers(), customer_id) {
|
let ephemeral_auth = match auth::is_ephemeral_auth(req.headers()) {
|
||||||
Ok(auth) => auth,
|
Ok(auth) => auth,
|
||||||
Err(err) => return api::log_and_return_error_response(err),
|
Err(err) => return api::log_and_return_error_response(err),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use api_models::{
|
|||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use common_enums::TokenPurpose;
|
use common_enums::TokenPurpose;
|
||||||
use common_utils::{date_time, id_type};
|
use common_utils::date_time;
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
||||||
use masking::PeekInterface;
|
use masking::PeekInterface;
|
||||||
@ -440,7 +440,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EphemeralKeyAuth(pub id_type::CustomerId);
|
pub struct EphemeralKeyAuth;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<A> AuthenticateAndFetch<AuthenticationData, A> for EphemeralKeyAuth
|
impl<A> AuthenticateAndFetch<AuthenticationData, A> for EphemeralKeyAuth
|
||||||
@ -460,9 +460,6 @@ where
|
|||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::Unauthorized)?;
|
.change_context(errors::ApiErrorResponse::Unauthorized)?;
|
||||||
|
|
||||||
if ephemeral_key.customer_id.ne(&self.0) {
|
|
||||||
return Err(report!(errors::ApiErrorResponse::InvalidEphemeralKey));
|
|
||||||
}
|
|
||||||
MerchantIdAuth(ephemeral_key.merchant_id)
|
MerchantIdAuth(ephemeral_key.merchant_id)
|
||||||
.authenticate_and_fetch(request_headers, state)
|
.authenticate_and_fetch(request_headers, state)
|
||||||
.await
|
.await
|
||||||
@ -1046,16 +1043,43 @@ where
|
|||||||
Ok((Box::new(ApiKeyAuth), api::AuthFlow::Merchant))
|
Ok((Box::new(ApiKeyAuth), api::AuthFlow::Merchant))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_ephemeral_or_other_auth<T>(
|
||||||
|
headers: &HeaderMap,
|
||||||
|
is_merchant_flow: bool,
|
||||||
|
payload: Option<&impl ClientSecretFetch>,
|
||||||
|
) -> RouterResult<(
|
||||||
|
Box<dyn AuthenticateAndFetch<AuthenticationData, T>>,
|
||||||
|
api::AuthFlow,
|
||||||
|
bool,
|
||||||
|
)>
|
||||||
|
where
|
||||||
|
T: SessionStateInfo,
|
||||||
|
ApiKeyAuth: AuthenticateAndFetch<AuthenticationData, T>,
|
||||||
|
PublishableKeyAuth: AuthenticateAndFetch<AuthenticationData, T>,
|
||||||
|
EphemeralKeyAuth: AuthenticateAndFetch<AuthenticationData, T>,
|
||||||
|
{
|
||||||
|
let api_key = get_api_key(headers)?;
|
||||||
|
|
||||||
|
if api_key.starts_with("epk") {
|
||||||
|
Ok((Box::new(EphemeralKeyAuth), api::AuthFlow::Client, true))
|
||||||
|
} else if is_merchant_flow {
|
||||||
|
Ok((Box::new(ApiKeyAuth), api::AuthFlow::Merchant, false))
|
||||||
|
} else {
|
||||||
|
let payload = payload.get_required_value("ClientSecretFetch")?;
|
||||||
|
let (auth, auth_flow) = check_client_secret_and_get_auth(headers, payload)?;
|
||||||
|
Ok((auth, auth_flow, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_ephemeral_auth<A: SessionStateInfo + Sync>(
|
pub fn is_ephemeral_auth<A: SessionStateInfo + Sync>(
|
||||||
headers: &HeaderMap,
|
headers: &HeaderMap,
|
||||||
customer_id: &id_type::CustomerId,
|
|
||||||
) -> RouterResult<Box<dyn AuthenticateAndFetch<AuthenticationData, A>>> {
|
) -> RouterResult<Box<dyn AuthenticateAndFetch<AuthenticationData, A>>> {
|
||||||
let api_key = get_api_key(headers)?;
|
let api_key = get_api_key(headers)?;
|
||||||
|
|
||||||
if !api_key.starts_with("epk") {
|
if !api_key.starts_with("epk") {
|
||||||
Ok(Box::new(ApiKeyAuth))
|
Ok(Box::new(ApiKeyAuth))
|
||||||
} else {
|
} else {
|
||||||
Ok(Box::new(EphemeralKeyAuth(customer_id.to_owned())))
|
Ok(Box::new(EphemeralKeyAuth))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user