Bug: [FEATURE] Use Ephemeral auth for Customer PM list and PM delete APIs



### Feature Description

We want to enable Ephemeral auth for both customer's PM list and PM delete API for the SDK to be able to use these APIs in a non-payments context

### Possible Implementation

Implementation would involve changing the Ephemeral auth types and auth process for the said APIs

### Have you spent some time checking if this feature request has been raised before?

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/router/src/compatibility/stripe/customers.rs b/crates/router/src/compatibility/stripe/customers.rs
index 65abb012b9a..264f205c907 100644
--- a/crates/router/src/compatibility/stripe/customers.rs
+++ b/crates/router/src/compatibility/stripe/customers.rs
@@ -195,6 +195,7 @@ pub async fn list_customer_payment_method_api(
                 auth.key_store,
                 Some(req),
                 Some(&customer_id),
+                None,
             )
         },
         &auth::ApiKeyAuth,
diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs
index 0641c842390..62a0b65e7cf 100644
--- a/crates/router/src/core/payment_methods/cards.rs
+++ b/crates/router/src/core/payment_methods/cards.rs
@@ -64,6 +64,7 @@ use crate::{
     pii::prelude::*,
     routes::{
         self,
+        app::SessionStateInfo,
         metrics::{self, request},
         payment_methods::ParentPaymentMethodToken,
     },
@@ -3080,10 +3081,25 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed(
     key_store: domain::MerchantKeyStore,
     req: Option<api::PaymentMethodListRequest>,
     customer_id: Option<&id_type::CustomerId>,
+    ephemeral_api_key: Option<&str>,
 ) -> errors::RouterResponse<api::CustomerPaymentMethodsListResponse> {
     let db = state.store.as_ref();
     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 {
         Box::pin(list_customer_payment_method(
             &state,
diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs
index 87366cf5c6d..4684edbd18d 100644
--- a/crates/router/src/routes/customers.rs
+++ b/crates/router/src/routes/customers.rs
@@ -47,7 +47,7 @@ pub async fn customers_retrieve(
     let auth = if auth::is_jwt_auth(req.headers()) {
         Box::new(auth::JWTAuth(Permission::CustomerRead))
     } else {
-        match auth::is_ephemeral_auth(req.headers(), &payload.customer_id) {
+        match auth::is_ephemeral_auth(req.headers()) {
             Ok(auth) => auth,
             Err(err) => return api::log_and_return_error_response(err),
         }
diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs
index e36a8fc3ea5..09b5c625711 100644
--- a/crates/router/src/routes/payment_methods.rs
+++ b/crates/router/src/routes/payment_methods.rs
@@ -139,7 +139,7 @@ pub async fn list_customer_payment_method_api(
     let payload = query_payload.into_inner();
     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,
         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,
                 Some(req),
                 Some(&customer_id),
+                None,
             )
         },
         &*ephemeral_auth,
@@ -194,10 +195,12 @@ pub async fn list_customer_payment_method_api_client(
 ) -> HttpResponse {
     let flow = Flow::CustomerPaymentMethodsList;
     let payload = query_payload.into_inner();
-    let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
-        Ok((auth, _auth_flow)) => (auth, _auth_flow),
-        Err(e) => return api::log_and_return_error_response(e),
-    };
+    let api_key = auth::get_api_key(req.headers()).ok();
+    let (auth, _, is_ephemeral_auth) =
+        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(
         flow,
@@ -211,6 +214,7 @@ pub async fn list_customer_payment_method_api_client(
                 auth.key_store,
                 Some(req),
                 None,
+                is_ephemeral_auth.then_some(api_key).flatten(),
             )
         },
         &*auth,
@@ -291,6 +295,11 @@ pub async fn payment_method_delete_api(
     let pm = PaymentMethodId {
         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(
         flow,
         state,
@@ -299,7 +308,7 @@ pub async fn payment_method_delete_api(
         |state, auth, req, _| {
             cards::delete_payment_method(state, auth.merchant_account, req, auth.key_store)
         },
-        &auth::ApiKeyAuth,
+        &*ephemeral_auth,
         api_locking::LockAction::NotApplicable,
     ))
     .await
@@ -345,7 +354,7 @@ pub async fn default_payment_method_set_api(
     let pc = payload.clone();
     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,
         Err(err) => return api::log_and_return_error_response(err),
     };
diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs
index 72d0860eed9..e2224da29ea 100644
--- a/crates/router/src/services/authentication.rs
+++ b/crates/router/src/services/authentication.rs
@@ -5,7 +5,7 @@ use api_models::{
 };
 use async_trait::async_trait;
 use common_enums::TokenPurpose;
-use common_utils::{date_time, id_type};
+use common_utils::date_time;
 use error_stack::{report, ResultExt};
 use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
 use masking::PeekInterface;
@@ -440,7 +440,7 @@ where
 }
 
 #[derive(Debug)]
-pub struct EphemeralKeyAuth(pub id_type::CustomerId);
+pub struct EphemeralKeyAuth;
 
 #[async_trait]
 impl<A> AuthenticateAndFetch<AuthenticationData, A> for EphemeralKeyAuth
@@ -460,9 +460,6 @@ where
             .await
             .change_context(errors::ApiErrorResponse::Unauthorized)?;
 
-        if ephemeral_key.customer_id.ne(&self.0) {
-            return Err(report!(errors::ApiErrorResponse::InvalidEphemeralKey));
-        }
         MerchantIdAuth(ephemeral_key.merchant_id)
             .authenticate_and_fetch(request_headers, state)
             .await
@@ -1046,16 +1043,43 @@ where
     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>(
     headers: &HeaderMap,
-    customer_id: &id_type::CustomerId,
 ) -> RouterResult<Box<dyn AuthenticateAndFetch<AuthenticationData, A>>> {
     let api_key = get_api_key(headers)?;
 
     if !api_key.starts_with("epk") {
         Ok(Box::new(ApiKeyAuth))
     } else {
-        Ok(Box::new(EphemeralKeyAuth(customer_id.to_owned())))
+        Ok(Box::new(EphemeralKeyAuth))
     }
 }
 

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
This PR enables - 
- Ephemeral key auth for customer's pm list
- Ephemeral key auth for delete payment method



### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
1. Save a card for a customer
2. Create Ephemeral key for that customer
```
curl --location --request POST 'http://localhost:8080/ephemeral_keys' \
--header 'Content-Type: application/json' \
--header 'api-key: dev_cHN6hMdCFUUrNWjYSxnwhiuMsg83rXTX9jBmtHmCL6sq9a43PswY304MSt3a9blR' \
--data-raw '{
    "customer_id": "cus_q1KVr57JhvRlVHToTKkY"
}'
```
Response - 
```
{
    "id": "eki_DuTISISBl1O0MAyczMQ8",
    "merchant_id": "sarthak1",
    "customer_id": "cus_grCxh6TyR6uepXyc6p7q",
    "created_at": 1718270809,
    "expires": 1718274409,
    "secret": "epk_67d4e3b46c49402badcbff4901cfef67"
}
```
3. Use the Ephemeral key secret to call customer's pm list
```
curl --location --request GET 'http://localhost:8080/customers/payment_methods' \
--header 'Accept: application/json' \
--header 'api-key: epk_be243219006647c69cdd7111d490d79a'
```
Response - 
```
{
    "customer_payment_methods": [
        {
            "payment_token": "token_9OtU08A46TOZcmfislqy",
            "payment_method_id": "pm_RPkRCYXKIWZFj0Qr36E9",
            "customer_id": "Some_cust1",
            "payment_method": "card",
            "payment_method_type": null,
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "card": {
                "scheme": null,
                "issuer_country": null,
                "last4_digits": "4242",
                "expiry_month": "10",
                "expiry_year": "2025",
                "card_token": null,
                "card_holder_name": "joseph Doe",
                "card_fingerprint": null,
                "nick_name": null,
                "card_network": null,
                "card_isin": "424242",
                "card_issuer": null,
                "card_type": null,
                "saved_to_locker": true
            },
            "metadata": null,
            "created": "2024-06-13T09:59:16.717Z",
            "bank": null,
            "surcharge_details": null,
            "requires_cvv": true,
            "last_used_at": "2024-06-13T09:59:16.717Z",
            "default_payment_method_set": true,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "john",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": null
            }
        }
    ],
    "is_guest_customer": null
}
```
4. Separate flow, Delete a PM using Ephemeral key
```
curl --location --request DELETE 'http://localhost:8080/payment_methods/pm_RPkRCYXKIWZFj0Qr36E9' \
--header 'Accept: application/json' \
--header 'api-key: epk_be243219006647c69cdd7111d490d79a'
```
Response - 
```
{
    "payment_method_id": "pm_RPkRCYXKIWZFj0Qr36E9",
    "deleted": true
}
```


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
