feat(payment_methods): Use Ephemeral auth for pm list and pm delete (#4996)

This commit is contained in:
Sarthak Soni
2024-06-13 19:10:40 +05:30
committed by GitHub
parent 18493bd8f0
commit ad7886a6ff
5 changed files with 65 additions and 15 deletions

View File

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

View File

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

View File

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

View File

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

View File

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