Issue:
Bug: feat(analytics): modify authentication queries and add generate report for authentications



Need to modify the authentication analytics queries to differentiate between **frictionless flow** and **challenge flow** through the authentication_type field.

Introduce support for **authentication report generation**, similar to the existing reports for payments, refunds, and disputes.  

Pull request description:
## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [x] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
This PR modifies authentication queries to improve the way we distinguish between authentication flows. Previously, `trans_status` was used for this purpose, but it has now been replaced with `authentication_type`, which more explicitly differentiates between **frictionless flow** and **challenge flow**.  

Additionally, this PR introduces support for **authentication report generation**, similar to the existing reports for payments, refunds, and disputes.  

 ## Key Changes  

### 1. **Updated Authentication Queries**  
- Replaced `trans_status` with `authentication_type` in authentication queries.  
- Ensures a clearer distinction between **frictionless flow** and **challenge flow** during authentication.  

### 2. **Added Authentication Report Generation**  
- Introduced report generation for **authentications**, aligning with the existing reporting structure for `payments`, 'refunds` and `disputes`.

### Routes for the reporting service:
- **Org level report**: `/analytics/v1/org/report/authentications`
- **Merchant level report**: `/analytics/v1/merchant/report/authentications`
- **Profile level report**: `/analytics/v1/profile/report/authentications`

This PR resolves the data correctness issue raised here: [https://github.com/juspay/hyperswitch-cloud/issues/8727](https://github.com/juspay/hyperswitch-cloud/issues/8727)
### 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).
-->
- Using `authentication_type` instead of `trans_status` improves accuracy in flow differentiation.  
- Authentication reporting enables better tracking and analytics for user authentication flows.  

## 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)?
-->
Can check the test cases from the `How did you test it?` section from these 2 PRs:
- [https://github.com/juspay/hyperswitch/pull/7451](https://github.com/juspay/hyperswitch/pull/7451)
- [https://github.com/juspay/hyperswitch/pull/7433](https://github.com/juspay/hyperswitch/pull/7433) 

**Note**: The Response body should be validated from this PR for all the metrics: [https://github.com/juspay/hyperswitch/pull/7451](https://github.com/juspay/hyperswitch/pull/7451)

Curl for Authentication Report:
```bash
curl --location 'http://localhost:8080/analytics/v1/org/report/authentications' \
--header 'Accept: */*' \
--header 'Accept-Language: en-US,en;q=0.9' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:9000' \
--header 'Referer: http://localhost:9000/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: same-site' \
--header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' \
--header 'api-key: test_admin' \
--header 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNmNkZTA0NzYtMTFlMi00NGE5LTlkMjUtOTA5M2QzNDQwZjhlIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzM1MDQxMjkzIiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTczNjYwNDY3OCwib3JnX2lkIjoib3JnX2pwanI5TkFEWlhqSENNYTU5MmF3IiwicHJvZmlsZV9pZCI6InByb19QRHUydVA3aWNuM2lXY0I3V0VVSSIsInRlbmFudF9pZCI6InB1YmxpYyJ9.7_cuWH5XygVrtIDsbh7nusrjGcQ3jbcaPKhIOo3EbM8' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMDU1MmY3YzctMmNjZC00ZmU3LThkM2ItY2U1M2ExMTJhZTIwIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNjk5OTg4Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTg3MjgwMiwib3JnX2lkIjoib3JnX2hQamlOektmbmV6NG8wNjdVVlFwIiwicHJvZmlsZV9pZCI6InByb19kWWJpUm9NVkFkNDAwWEh6R3hYTiIsInRlbmFudF9pZCI6InB1YmxpYyJ9.1bsDkYwwk8FtWrDEYdMIsjmH7DqWOVejX2GVseu68ZQ' \
--data-raw '{
    "timeRange": {
        "startTime": "2025-01-08T13:56:15Z",
        "endTime": "2025-01-09T13:56:15.277Z"
    },
    "emails": [
        "sandeep.kumar@juspay.in"
    ]
}'
```
Queries: 
#### Authentication Attempt Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_status IN ('success', 'failed') AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Authentication Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Authentication Success Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_status = 'success' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Challenge Attempt Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'challenge' AND authentication_status in ('success', 'failed') AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Challenge Flow Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'challenge' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Challenge Success Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_status = 'success' AND authentication_type = 'challenge' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Frictionless Flow Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'frictionless' AND created_at >= '1738402351' AND created_at <= '1740216751' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Frictionless Success Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'frictionless' AND authentication_status = 'success' AND created_at >= '1738402351' AND created_at <= '1740216751' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Authentication Funnel: (2nd step):
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND trans_status IS NOT NULL AND created_at >= '1738434600' AND created_at <= '1740734520' )
```

#### Authentication Funnel: (3rd step):
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND trans_status IS NOT NULL AND authentication_status IN ('success', 'failed') AND created_at >= '1738434600' AND created_at <= '1740734520' )
```
#### Authentication Error Message:
```bash
SELECT error_message, sum(sign_flag) AS count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1741712401' AND authentication_status = 'failed' AND error_message IS NOT NULL AND created_at >= '1738434600' AND created_at <= '1740734520' ) GROUP BY error_message HAVING sum(sign_flag) >= '1' ORDER BY count desc
```
Sample Output:
You should be able to receive an email with the authentication report to the email specified in the request.

Similarly, just replace `org` with `merchant` and `profile` in the URL for the corresponding authentication report at the merchant and profile level.
## 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`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible


Testing:

Can check the test cases from the `How did you test it?` section from these 2 PRs:
- [https://github.com/juspay/hyperswitch/pull/7451](https://github.com/juspay/hyperswitch/pull/7451)
- [https://github.com/juspay/hyperswitch/pull/7433](https://github.com/juspay/hyperswitch/pull/7433) 

**Note**: The Response body should be validated from this PR for all the metrics: [https://github.com/juspay/hyperswitch/pull/7451](https://github.com/juspay/hyperswitch/pull/7451)

Curl for Authentication Report:
```bash
curl --location 'http://localhost:8080/analytics/v1/org/report/authentications' \
--header 'Accept: */*' \
--header 'Accept-Language: en-US,en;q=0.9' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:9000' \
--header 'Referer: http://localhost:9000/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: same-site' \
--header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' \
--header 'api-key: test_admin' \
--header 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNmNkZTA0NzYtMTFlMi00NGE5LTlkMjUtOTA5M2QzNDQwZjhlIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzM1MDQxMjkzIiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTczNjYwNDY3OCwib3JnX2lkIjoib3JnX2pwanI5TkFEWlhqSENNYTU5MmF3IiwicHJvZmlsZV9pZCI6InByb19QRHUydVA3aWNuM2lXY0I3V0VVSSIsInRlbmFudF9pZCI6InB1YmxpYyJ9.7_cuWH5XygVrtIDsbh7nusrjGcQ3jbcaPKhIOo3EbM8' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMDU1MmY3YzctMmNjZC00ZmU3LThkM2ItY2U1M2ExMTJhZTIwIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNjk5OTg4Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTg3MjgwMiwib3JnX2lkIjoib3JnX2hQamlOektmbmV6NG8wNjdVVlFwIiwicHJvZmlsZV9pZCI6InByb19kWWJpUm9NVkFkNDAwWEh6R3hYTiIsInRlbmFudF9pZCI6InB1YmxpYyJ9.1bsDkYwwk8FtWrDEYdMIsjmH7DqWOVejX2GVseu68ZQ' \
--data-raw '{
    "timeRange": {
        "startTime": "2025-01-08T13:56:15Z",
        "endTime": "2025-01-09T13:56:15.277Z"
    },
    "emails": [
        "sandeep.kumar@juspay.in"
    ]
}'
```
Queries: 
#### Authentication Attempt Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_status IN ('success', 'failed') AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Authentication Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Authentication Success Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_status = 'success' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Challenge Attempt Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'challenge' AND authentication_status in ('success', 'failed') AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Challenge Flow Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'challenge' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Challenge Success Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_status = 'success' AND authentication_type = 'challenge' AND created_at >= '1726079400' AND created_at <= '1727256120' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Frictionless Flow Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'frictionless' AND created_at >= '1738402351' AND created_at <= '1740216751' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Frictionless Success Count:
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND authentication_type = 'frictionless' AND authentication_status = 'success' AND created_at >= '1738402351' AND created_at <= '1740216751' ) GROUP BY toStartOfDay(created_at) HAVING sum(sign_flag) >= '1'
```

#### Authentication Funnel: (2nd step):
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND trans_status IS NOT NULL AND created_at >= '1738434600' AND created_at <= '1740734520' )
```

#### Authentication Funnel: (3rd step):
```bash
SELECT sum(sign_flag) as count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1740414909' AND trans_status IS NOT NULL AND authentication_status IN ('success', 'failed') AND created_at >= '1738434600' AND created_at <= '1740734520' )
```
#### Authentication Error Message:
```bash
SELECT error_message, sum(sign_flag) AS count, min(created_at) as start_bucket, max(created_at) as end_bucket FROM authentications WHERE ( merchant_id = 'merchant_1741712401' AND authentication_status = 'failed' AND error_message IS NOT NULL AND created_at >= '1738434600' AND created_at <= '1740734520' ) GROUP BY error_message HAVING sum(sign_flag) >= '1' ORDER BY count desc
```
Sample Output:
You should be able to receive an email with the authentication report to the email specified in the request.

Similarly, just replace `org` with `merchant` and `profile` in the URL for the corresponding authentication report at the merchant and profile level.

Patch:
diff --git a/crates/analytics/src/auth_events/core.rs b/crates/analytics/src/auth_events/core.rs
index a2640be6ead..bdcbdd86a94 100644
--- a/crates/analytics/src/auth_events/core.rs
+++ b/crates/analytics/src/auth_events/core.rs
@@ -165,6 +165,7 @@ pub async fn get_filters(
         .filter_map(|fil: AuthEventFilterRow| match dim {
             AuthEventDimensions::AuthenticationStatus => fil.authentication_status.map(|i| i.as_ref().to_string()),
             AuthEventDimensions::TransactionStatus => fil.trans_status.map(|i| i.as_ref().to_string()),
+            AuthEventDimensions::AuthenticationType => fil.authentication_type.map(|i| i.as_ref().to_string()),
             AuthEventDimensions::ErrorMessage => fil.error_message,
             AuthEventDimensions::AuthenticationConnector => fil.authentication_connector.map(|i| i.as_ref().to_string()),
             AuthEventDimensions::MessageVersion => fil.message_version,
diff --git a/crates/analytics/src/auth_events/filters.rs b/crates/analytics/src/auth_events/filters.rs
index da8e0b7cfa3..5f65eeaa279 100644
--- a/crates/analytics/src/auth_events/filters.rs
+++ b/crates/analytics/src/auth_events/filters.rs
@@ -1,4 +1,5 @@
 use api_models::analytics::{auth_events::AuthEventDimensions, Granularity, TimeRange};
+use common_enums::DecoupledAuthenticationType;
 use common_utils::errors::ReportSwitchExt;
 use diesel_models::enums::{AuthenticationConnectors, AuthenticationStatus, TransactionStatus};
 use error_stack::ResultExt;
@@ -54,6 +55,7 @@ where
 pub struct AuthEventFilterRow {
     pub authentication_status: Option<DBEnumWrapper<AuthenticationStatus>>,
     pub trans_status: Option<DBEnumWrapper<TransactionStatus>>,
+    pub authentication_type: Option<DBEnumWrapper<DecoupledAuthenticationType>>,
     pub error_message: Option<String>,
     pub authentication_connector: Option<DBEnumWrapper<AuthenticationConnectors>>,
     pub message_version: Option<String>,
diff --git a/crates/analytics/src/auth_events/metrics.rs b/crates/analytics/src/auth_events/metrics.rs
index fd94aac614c..a3d0fe7b683 100644
--- a/crates/analytics/src/auth_events/metrics.rs
+++ b/crates/analytics/src/auth_events/metrics.rs
@@ -41,6 +41,7 @@ pub struct AuthEventMetricRow {
     pub count: Option<i64>,
     pub authentication_status: Option<DBEnumWrapper<storage_enums::AuthenticationStatus>>,
     pub trans_status: Option<DBEnumWrapper<storage_enums::TransactionStatus>>,
+    pub authentication_type: Option<DBEnumWrapper<storage_enums::DecoupledAuthenticationType>>,
     pub error_message: Option<String>,
     pub authentication_connector: Option<DBEnumWrapper<storage_enums::AuthenticationConnectors>>,
     pub message_version: Option<String>,
diff --git a/crates/analytics/src/auth_events/metrics/authentication_attempt_count.rs b/crates/analytics/src/auth_events/metrics/authentication_attempt_count.rs
index 32c76385409..b82d1ef4c35 100644
--- a/crates/analytics/src/auth_events/metrics/authentication_attempt_count.rs
+++ b/crates/analytics/src/auth_events/metrics/authentication_attempt_count.rs
@@ -70,7 +70,10 @@ where
             .switch()?;
 
         query_builder
-            .add_negative_filter_clause("authentication_status", AuthenticationStatus::Pending)
+            .add_filter_in_range_clause(
+                "authentication_status",
+                &[AuthenticationStatus::Success, AuthenticationStatus::Failed],
+            )
             .switch()?;
         filters.set_filter_clause(&mut query_builder).switch()?;
         time_range
@@ -103,6 +106,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/authentication_count.rs b/crates/analytics/src/auth_events/metrics/authentication_count.rs
index 39df41f53aa..682e8a07f6b 100644
--- a/crates/analytics/src/auth_events/metrics/authentication_count.rs
+++ b/crates/analytics/src/auth_events/metrics/authentication_count.rs
@@ -96,6 +96,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/authentication_error_message.rs b/crates/analytics/src/auth_events/metrics/authentication_error_message.rs
index cdb89b796dd..047511d477c 100644
--- a/crates/analytics/src/auth_events/metrics/authentication_error_message.rs
+++ b/crates/analytics/src/auth_events/metrics/authentication_error_message.rs
@@ -12,8 +12,8 @@ use time::PrimitiveDateTime;
 use super::AuthEventMetricRow;
 use crate::{
     query::{
-        Aggregate, FilterTypes, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql,
-        Window,
+        Aggregate, FilterTypes, GroupByClause, Order, QueryBuilder, QueryFilter, SeriesBucket,
+        ToSql, Window,
     },
     types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
 };
@@ -45,11 +45,9 @@ where
         for dim in dimensions.iter() {
             query_builder.add_select_column(dim).switch()?;
         }
+
         query_builder
-            .add_select_column(Aggregate::Count {
-                field: None,
-                alias: Some("count"),
-            })
+            .add_select_column("sum(sign_flag) AS count")
             .switch()?;
 
         query_builder
@@ -94,6 +92,11 @@ where
                 .switch()?;
         }
 
+        query_builder
+            .add_order_by_clause("count", Order::Descending)
+            .attach_printable("Error adding order by clause")
+            .switch()?;
+
         if let Some(granularity) = granularity {
             granularity
                 .set_group_by_clause(&mut query_builder)
@@ -112,6 +115,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/authentication_funnel.rs b/crates/analytics/src/auth_events/metrics/authentication_funnel.rs
index 37f9dfd1c1f..9342e9047e0 100644
--- a/crates/analytics/src/auth_events/metrics/authentication_funnel.rs
+++ b/crates/analytics/src/auth_events/metrics/authentication_funnel.rs
@@ -107,6 +107,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/authentication_success_count.rs b/crates/analytics/src/auth_events/metrics/authentication_success_count.rs
index 039ef00dd6e..984350efe6b 100644
--- a/crates/analytics/src/auth_events/metrics/authentication_success_count.rs
+++ b/crates/analytics/src/auth_events/metrics/authentication_success_count.rs
@@ -101,6 +101,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs b/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs
index beccc093b19..5a2a9400b20 100644
--- a/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs
+++ b/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs
@@ -4,6 +4,7 @@ use api_models::analytics::{
     auth_events::{AuthEventDimensions, AuthEventFilters, AuthEventMetricsBucketIdentifier},
     Granularity, TimeRange,
 };
+use common_enums::{AuthenticationStatus, DecoupledAuthenticationType};
 use common_utils::errors::ReportSwitchExt;
 use error_stack::ResultExt;
 use time::PrimitiveDateTime;
@@ -67,11 +68,17 @@ where
             .switch()?;
 
         query_builder
-            .add_filter_clause("trans_status", "C".to_string())
+            .add_filter_clause(
+                "authentication_type",
+                DecoupledAuthenticationType::Challenge,
+            )
             .switch()?;
 
         query_builder
-            .add_negative_filter_clause("authentication_status", "pending")
+            .add_filter_in_range_clause(
+                "authentication_status",
+                &[AuthenticationStatus::Success, AuthenticationStatus::Failed],
+            )
             .switch()?;
         filters.set_filter_clause(&mut query_builder).switch()?;
         time_range
@@ -104,6 +111,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/challenge_flow_count.rs b/crates/analytics/src/auth_events/metrics/challenge_flow_count.rs
index 4d07cffba94..279844388f2 100644
--- a/crates/analytics/src/auth_events/metrics/challenge_flow_count.rs
+++ b/crates/analytics/src/auth_events/metrics/challenge_flow_count.rs
@@ -4,6 +4,7 @@ use api_models::analytics::{
     auth_events::{AuthEventDimensions, AuthEventFilters, AuthEventMetricsBucketIdentifier},
     Granularity, TimeRange,
 };
+use common_enums::DecoupledAuthenticationType;
 use common_utils::errors::ReportSwitchExt;
 use error_stack::ResultExt;
 use time::PrimitiveDateTime;
@@ -67,7 +68,10 @@ where
             .switch()?;
 
         query_builder
-            .add_filter_clause("trans_status", "C".to_string())
+            .add_filter_clause(
+                "authentication_type",
+                DecoupledAuthenticationType::Challenge,
+            )
             .switch()?;
         filters.set_filter_clause(&mut query_builder).switch()?;
         time_range
@@ -99,6 +103,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/challenge_success_count.rs b/crates/analytics/src/auth_events/metrics/challenge_success_count.rs
index 310a45f0530..91e8cc54289 100644
--- a/crates/analytics/src/auth_events/metrics/challenge_success_count.rs
+++ b/crates/analytics/src/auth_events/metrics/challenge_success_count.rs
@@ -4,7 +4,7 @@ use api_models::analytics::{
     auth_events::{AuthEventDimensions, AuthEventFilters, AuthEventMetricsBucketIdentifier},
     Granularity, TimeRange,
 };
-use common_enums::AuthenticationStatus;
+use common_enums::{AuthenticationStatus, DecoupledAuthenticationType};
 use common_utils::errors::ReportSwitchExt;
 use error_stack::ResultExt;
 use time::PrimitiveDateTime;
@@ -72,7 +72,10 @@ where
             .switch()?;
 
         query_builder
-            .add_filter_clause("trans_status", "C".to_string())
+            .add_filter_clause(
+                "authentication_type",
+                DecoupledAuthenticationType::Challenge,
+            )
             .switch()?;
         filters.set_filter_clause(&mut query_builder).switch()?;
         time_range
@@ -105,6 +108,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/frictionless_flow_count.rs b/crates/analytics/src/auth_events/metrics/frictionless_flow_count.rs
index 24857bfb840..b08edb10113 100644
--- a/crates/analytics/src/auth_events/metrics/frictionless_flow_count.rs
+++ b/crates/analytics/src/auth_events/metrics/frictionless_flow_count.rs
@@ -4,6 +4,7 @@ use api_models::analytics::{
     auth_events::{AuthEventDimensions, AuthEventFilters, AuthEventMetricsBucketIdentifier},
     Granularity, TimeRange,
 };
+use common_enums::DecoupledAuthenticationType;
 use common_utils::errors::ReportSwitchExt;
 use error_stack::ResultExt;
 use time::PrimitiveDateTime;
@@ -67,7 +68,10 @@ where
             .switch()?;
 
         query_builder
-            .add_filter_clause("trans_status", "Y".to_string())
+            .add_filter_clause(
+                "authentication_type",
+                DecoupledAuthenticationType::Frictionless,
+            )
             .switch()?;
         filters.set_filter_clause(&mut query_builder).switch()?;
         time_range
@@ -100,6 +104,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/auth_events/metrics/frictionless_success_count.rs b/crates/analytics/src/auth_events/metrics/frictionless_success_count.rs
index 79fef8a16d0..80417ac64bf 100644
--- a/crates/analytics/src/auth_events/metrics/frictionless_success_count.rs
+++ b/crates/analytics/src/auth_events/metrics/frictionless_success_count.rs
@@ -4,7 +4,7 @@ use api_models::analytics::{
     auth_events::{AuthEventDimensions, AuthEventFilters, AuthEventMetricsBucketIdentifier},
     Granularity, TimeRange,
 };
-use common_enums::AuthenticationStatus;
+use common_enums::{AuthenticationStatus, DecoupledAuthenticationType};
 use common_utils::errors::ReportSwitchExt;
 use error_stack::ResultExt;
 use time::PrimitiveDateTime;
@@ -68,7 +68,10 @@ where
             .switch()?;
 
         query_builder
-            .add_filter_clause("trans_status", "Y".to_string())
+            .add_filter_clause(
+                "authentication_type",
+                DecoupledAuthenticationType::Frictionless,
+            )
             .switch()?;
 
         query_builder
@@ -105,6 +108,7 @@ where
                     AuthEventMetricsBucketIdentifier::new(
                         i.authentication_status.as_ref().map(|i| i.0),
                         i.trans_status.as_ref().map(|i| i.0.clone()),
+                        i.authentication_type.as_ref().map(|i| i.0),
                         i.error_message.clone(),
                         i.authentication_connector.as_ref().map(|i| i.0),
                         i.message_version.clone(),
diff --git a/crates/analytics/src/lib.rs b/crates/analytics/src/lib.rs
index f698b1161a0..89f5cbd5b9c 100644
--- a/crates/analytics/src/lib.rs
+++ b/crates/analytics/src/lib.rs
@@ -1123,6 +1123,7 @@ pub struct ReportConfig {
     pub payment_function: String,
     pub refund_function: String,
     pub dispute_function: String,
+    pub authentication_function: String,
     pub region: String,
 }
 
@@ -1151,6 +1152,7 @@ pub enum AnalyticsFlow {
     GeneratePaymentReport,
     GenerateDisputeReport,
     GenerateRefundReport,
+    GenerateAuthenticationReport,
     GetApiEventMetrics,
     GetApiEventFilters,
     GetConnectorEvents,
diff --git a/crates/analytics/src/query.rs b/crates/analytics/src/query.rs
index d483ce43605..5effd9e70a7 100644
--- a/crates/analytics/src/query.rs
+++ b/crates/analytics/src/query.rs
@@ -19,7 +19,9 @@ use api_models::{
     },
     refunds::RefundStatus,
 };
-use common_enums::{AuthenticationConnectors, AuthenticationStatus, TransactionStatus};
+use common_enums::{
+    AuthenticationConnectors, AuthenticationStatus, DecoupledAuthenticationType, TransactionStatus,
+};
 use common_utils::{
     errors::{CustomResult, ParsingError},
     id_type::{MerchantId, OrganizationId, ProfileId},
@@ -506,6 +508,7 @@ impl_to_sql_for_to_string!(
     TransactionStatus,
     AuthenticationStatus,
     AuthenticationConnectors,
+    DecoupledAuthenticationType,
     Flow,
     &String,
     &bool,
diff --git a/crates/analytics/src/sqlx.rs b/crates/analytics/src/sqlx.rs
index a6db92e753f..6a6237e6783 100644
--- a/crates/analytics/src/sqlx.rs
+++ b/crates/analytics/src/sqlx.rs
@@ -4,7 +4,9 @@ use api_models::{
     analytics::{frm::FrmTransactionType, refunds::RefundType},
     enums::{DisputeStage, DisputeStatus},
 };
-use common_enums::{AuthenticationConnectors, AuthenticationStatus, TransactionStatus};
+use common_enums::{
+    AuthenticationConnectors, AuthenticationStatus, DecoupledAuthenticationType, TransactionStatus,
+};
 use common_utils::{
     errors::{CustomResult, ParsingError},
     DbConnectionParams,
@@ -100,6 +102,7 @@ db_type!(DisputeStatus);
 db_type!(AuthenticationStatus);
 db_type!(TransactionStatus);
 db_type!(AuthenticationConnectors);
+db_type!(DecoupledAuthenticationType);
 
 impl<'q, Type> Encode<'q, Postgres> for DBEnumWrapper<Type>
 where
@@ -208,6 +211,11 @@ impl<'a> FromRow<'a, PgRow> for super::auth_events::metrics::AuthEventMetricRow
                 ColumnNotFound(_) => Ok(Default::default()),
                 e => Err(e),
             })?;
+        let authentication_type: Option<DBEnumWrapper<DecoupledAuthenticationType>> =
+            row.try_get("authentication_type").or_else(|e| match e {
+                ColumnNotFound(_) => Ok(Default::default()),
+                e => Err(e),
+            })?;
         let error_message: Option<String> = row.try_get("error_message").or_else(|e| match e {
             ColumnNotFound(_) => Ok(Default::default()),
             e => Err(e),
@@ -237,6 +245,7 @@ impl<'a> FromRow<'a, PgRow> for super::auth_events::metrics::AuthEventMetricRow
         Ok(Self {
             authentication_status,
             trans_status,
+            authentication_type,
             error_message,
             authentication_connector,
             message_version,
@@ -259,6 +268,11 @@ impl<'a> FromRow<'a, PgRow> for super::auth_events::filters::AuthEventFilterRow
                 ColumnNotFound(_) => Ok(Default::default()),
                 e => Err(e),
             })?;
+        let authentication_type: Option<DBEnumWrapper<DecoupledAuthenticationType>> =
+            row.try_get("authentication_type").or_else(|e| match e {
+                ColumnNotFound(_) => Ok(Default::default()),
+                e => Err(e),
+            })?;
         let error_message: Option<String> = row.try_get("error_message").or_else(|e| match e {
             ColumnNotFound(_) => Ok(Default::default()),
             e => Err(e),
@@ -277,6 +291,7 @@ impl<'a> FromRow<'a, PgRow> for super::auth_events::filters::AuthEventFilterRow
         Ok(Self {
             authentication_status,
             trans_status,
+            authentication_type,
             error_message,
             authentication_connector,
             message_version,
diff --git a/crates/api_models/src/analytics/auth_events.rs b/crates/api_models/src/analytics/auth_events.rs
index 5c2d4ed2064..2360e564642 100644
--- a/crates/api_models/src/analytics/auth_events.rs
+++ b/crates/api_models/src/analytics/auth_events.rs
@@ -3,7 +3,9 @@ use std::{
     hash::{Hash, Hasher},
 };
 
-use common_enums::{AuthenticationConnectors, AuthenticationStatus, TransactionStatus};
+use common_enums::{
+    AuthenticationConnectors, AuthenticationStatus, DecoupledAuthenticationType, TransactionStatus,
+};
 
 use super::{NameDescription, TimeRange};
 
@@ -14,6 +16,8 @@ pub struct AuthEventFilters {
     #[serde(default)]
     pub trans_status: Vec<TransactionStatus>,
     #[serde(default)]
+    pub authentication_type: Vec<DecoupledAuthenticationType>,
+    #[serde(default)]
     pub error_message: Vec<String>,
     #[serde(default)]
     pub authentication_connector: Vec<AuthenticationConnectors>,
@@ -42,6 +46,7 @@ pub enum AuthEventDimensions {
     #[strum(serialize = "trans_status")]
     #[serde(rename = "trans_status")]
     TransactionStatus,
+    AuthenticationType,
     ErrorMessage,
     AuthenticationConnector,
     MessageVersion,
@@ -101,6 +106,7 @@ pub mod metric_behaviour {
     pub struct ChallengeAttemptCount;
     pub struct ChallengeSuccessCount;
     pub struct AuthenticationErrorMessage;
+    pub struct AuthenticationFunnel;
 }
 
 impl From<AuthEventMetrics> for NameDescription {
@@ -125,6 +131,7 @@ impl From<AuthEventDimensions> for NameDescription {
 pub struct AuthEventMetricsBucketIdentifier {
     pub authentication_status: Option<AuthenticationStatus>,
     pub trans_status: Option<TransactionStatus>,
+    pub authentication_type: Option<DecoupledAuthenticationType>,
     pub error_message: Option<String>,
     pub authentication_connector: Option<AuthenticationConnectors>,
     pub message_version: Option<String>,
@@ -140,6 +147,7 @@ impl AuthEventMetricsBucketIdentifier {
     pub fn new(
         authentication_status: Option<AuthenticationStatus>,
         trans_status: Option<TransactionStatus>,
+        authentication_type: Option<DecoupledAuthenticationType>,
         error_message: Option<String>,
         authentication_connector: Option<AuthenticationConnectors>,
         message_version: Option<String>,
@@ -148,6 +156,7 @@ impl AuthEventMetricsBucketIdentifier {
         Self {
             authentication_status,
             trans_status,
+            authentication_type,
             error_message,
             authentication_connector,
             message_version,
@@ -161,6 +170,7 @@ impl Hash for AuthEventMetricsBucketIdentifier {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.authentication_status.hash(state);
         self.trans_status.hash(state);
+        self.authentication_type.hash(state);
         self.authentication_connector.hash(state);
         self.message_version.hash(state);
         self.error_message.hash(state);
diff --git a/crates/api_models/src/analytics/sdk_events.rs b/crates/api_models/src/analytics/sdk_events.rs
index b2abbbe00e1..9a749f1144e 100644
--- a/crates/api_models/src/analytics/sdk_events.rs
+++ b/crates/api_models/src/analytics/sdk_events.rs
@@ -114,6 +114,10 @@ pub enum SdkEventNames {
     ThreeDsMethod,
     LoaderChanged,
     DisplayThreeDsSdk,
+    ThreeDsSdkInit,
+    AreqParamsGeneration,
+    ChallengePresented,
+    ChallengeComplete,
 }
 
 pub mod metric_behaviour {
diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs
index 858c16d052f..cc242f86a5b 100644
--- a/crates/router/src/analytics.rs
+++ b/crates/router/src/analytics.rs
@@ -90,6 +90,10 @@ pub mod routes {
                             web::resource("report/payments")
                                 .route(web::post().to(generate_merchant_payment_report)),
                         )
+                        .service(
+                            web::resource("report/authentications")
+                                .route(web::post().to(generate_merchant_authentication_report)),
+                        )
                         .service(
                             web::resource("metrics/sdk_events")
                                 .route(web::post().to(get_sdk_event_metrics)),
@@ -190,6 +194,11 @@ pub mod routes {
                                     web::resource("report/payments")
                                         .route(web::post().to(generate_merchant_payment_report)),
                                 )
+                                .service(
+                                    web::resource("report/authentications").route(
+                                        web::post().to(generate_merchant_authentication_report),
+                                    ),
+                                )
                                 .service(
                                     web::resource("metrics/api_events")
                                         .route(web::post().to(get_merchant_api_events_metrics)),
@@ -252,6 +261,10 @@ pub mod routes {
                                     web::resource("report/payments")
                                         .route(web::post().to(generate_org_payment_report)),
                                 )
+                                .service(
+                                    web::resource("report/authentications")
+                                        .route(web::post().to(generate_org_authentication_report)),
+                                )
                                 .service(
                                     web::resource("metrics/sankey")
                                         .route(web::post().to(get_org_sankey)),
@@ -306,6 +319,11 @@ pub mod routes {
                                     web::resource("report/payments")
                                         .route(web::post().to(generate_profile_payment_report)),
                                 )
+                                .service(
+                                    web::resource("report/authentications").route(
+                                        web::post().to(generate_profile_authentication_report),
+                                    ),
+                                )
                                 .service(
                                     web::resource("api_event_logs")
                                         .route(web::get().to(get_profile_api_events)),
@@ -1746,6 +1764,7 @@ pub mod routes {
         ))
         .await
     }
+
     #[cfg(feature = "v1")]
     pub async fn generate_org_payment_report(
         state: web::Data<AppState>,
@@ -1850,6 +1869,161 @@ pub mod routes {
         .await
     }
 
+    #[cfg(feature = "v1")]
+    pub async fn generate_merchant_authentication_report(
+        state: web::Data<AppState>,
+        req: actix_web::HttpRequest,
+        json_payload: web::Json<ReportRequest>,
+    ) -> impl Responder {
+        let flow = AnalyticsFlow::GenerateAuthenticationReport;
+        Box::pin(api::server_wrap(
+            flow,
+            state.clone(),
+            &req,
+            json_payload.into_inner(),
+            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
+                let user = UserInterface::find_user_by_id(&*state.global_store, &user_id)
+                    .await
+                    .change_context(AnalyticsError::UnknownError)?;
+
+                let user_email = UserEmail::from_pii_email(user.email)
+                    .change_context(AnalyticsError::UnknownError)?
+                    .get_secret();
+
+                let org_id = auth.merchant_account.get_org_id();
+                let merchant_id = auth.merchant_account.get_id();
+                let lambda_req = GenerateReportRequest {
+                    request: payload,
+                    merchant_id: Some(merchant_id.clone()),
+                    auth: AuthInfo::MerchantLevel {
+                        org_id: org_id.clone(),
+                        merchant_ids: vec![merchant_id.clone()],
+                    },
+                    email: user_email,
+                };
+
+                let json_bytes =
+                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
+                invoke_lambda(
+                    &state.conf.report_download_config.authentication_function,
+                    &state.conf.report_download_config.region,
+                    &json_bytes,
+                )
+                .await
+                .map(ApplicationResponse::Json)
+            },
+            &auth::JWTAuth {
+                permission: Permission::MerchantReportRead,
+            },
+            api_locking::LockAction::NotApplicable,
+        ))
+        .await
+    }
+
+    #[cfg(feature = "v1")]
+    pub async fn generate_org_authentication_report(
+        state: web::Data<AppState>,
+        req: actix_web::HttpRequest,
+        json_payload: web::Json<ReportRequest>,
+    ) -> impl Responder {
+        let flow = AnalyticsFlow::GenerateAuthenticationReport;
+        Box::pin(api::server_wrap(
+            flow,
+            state.clone(),
+            &req,
+            json_payload.into_inner(),
+            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
+                let user = UserInterface::find_user_by_id(&*state.global_store, &user_id)
+                    .await
+                    .change_context(AnalyticsError::UnknownError)?;
+
+                let user_email = UserEmail::from_pii_email(user.email)
+                    .change_context(AnalyticsError::UnknownError)?
+                    .get_secret();
+
+                let org_id = auth.merchant_account.get_org_id();
+                let lambda_req = GenerateReportRequest {
+                    request: payload,
+                    merchant_id: None,
+                    auth: AuthInfo::OrgLevel {
+                        org_id: org_id.clone(),
+                    },
+                    email: user_email,
+                };
+
+                let json_bytes =
+                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
+                invoke_lambda(
+                    &state.conf.report_download_config.authentication_function,
+                    &state.conf.report_download_config.region,
+                    &json_bytes,
+                )
+                .await
+                .map(ApplicationResponse::Json)
+            },
+            &auth::JWTAuth {
+                permission: Permission::OrganizationReportRead,
+            },
+            api_locking::LockAction::NotApplicable,
+        ))
+        .await
+    }
+
+    #[cfg(feature = "v1")]
+    pub async fn generate_profile_authentication_report(
+        state: web::Data<AppState>,
+        req: actix_web::HttpRequest,
+        json_payload: web::Json<ReportRequest>,
+    ) -> impl Responder {
+        let flow = AnalyticsFlow::GenerateAuthenticationReport;
+        Box::pin(api::server_wrap(
+            flow,
+            state.clone(),
+            &req,
+            json_payload.into_inner(),
+            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
+                let user = UserInterface::find_user_by_id(&*state.global_store, &user_id)
+                    .await
+                    .change_context(AnalyticsError::UnknownError)?;
+
+                let user_email = UserEmail::from_pii_email(user.email)
+                    .change_context(AnalyticsError::UnknownError)?
+                    .get_secret();
+                let org_id = auth.merchant_account.get_org_id();
+                let merchant_id = auth.merchant_account.get_id();
+                let profile_id = auth
+                    .profile_id
+                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
+                    .change_context(AnalyticsError::AccessForbiddenError)?;
+                let lambda_req = GenerateReportRequest {
+                    request: payload,
+                    merchant_id: Some(merchant_id.clone()),
+                    auth: AuthInfo::ProfileLevel {
+                        org_id: org_id.clone(),
+                        merchant_id: merchant_id.clone(),
+                        profile_ids: vec![profile_id.clone()],
+                    },
+                    email: user_email,
+                };
+
+                let json_bytes =
+                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
+                invoke_lambda(
+                    &state.conf.report_download_config.authentication_function,
+                    &state.conf.report_download_config.region,
+                    &json_bytes,
+                )
+                .await
+                .map(ApplicationResponse::Json)
+            },
+            &auth::JWTAuth {
+                permission: Permission::ProfileReportRead,
+            },
+            api_locking::LockAction::NotApplicable,
+        ))
+        .await
+    }
+
     /// # Panics
     ///
     /// Panics if `json_payload` array does not contain one `GetApiEventMetricRequest` element.