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



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 -->
Hotfix PR
Original PR: [https://github.com/juspay/hyperswitch/pull/7483](https://github.com/juspay/hyperswitch/pull/7483)

### 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)?
-->


## 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:

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.