Skip to content

Commit 1d4c87a

Browse files
feat(auth): Add support for partial-auth, by facilitating injection of authentication parameters in headers (#4802)
Co-authored-by: Jagan <[email protected]>
1 parent 5e1eb4a commit 1d4c87a

31 files changed

+523
-103
lines changed

config/development.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ validity = 1
174174
[api_keys]
175175
hash_key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
176176

177+
checksum_auth_context = "TEST"
178+
checksum_auth_key = "54455354"
179+
180+
177181
[connectors]
178182
aci.base_url = "https://eu-test.oppwa.com/"
179183
adyen.base_url = "https://checkout-test.adyen.com/"

crates/common_utils/src/crypto.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,43 @@ impl VerifySignature for HmacSha512 {
238238
}
239239
}
240240

241+
///
242+
/// Blake3
243+
#[derive(Debug)]
244+
pub struct Blake3(String);
245+
246+
impl Blake3 {
247+
/// Create a new instance of Blake3 with a key
248+
pub fn new(key: impl Into<String>) -> Self {
249+
Self(key.into())
250+
}
251+
}
252+
253+
impl SignMessage for Blake3 {
254+
fn sign_message(
255+
&self,
256+
secret: &[u8],
257+
msg: &[u8],
258+
) -> CustomResult<Vec<u8>, errors::CryptoError> {
259+
let key = blake3::derive_key(&self.0, secret);
260+
let output = blake3::keyed_hash(&key, msg).as_bytes().to_vec();
261+
Ok(output)
262+
}
263+
}
264+
265+
impl VerifySignature for Blake3 {
266+
fn verify_signature(
267+
&self,
268+
secret: &[u8],
269+
signature: &[u8],
270+
msg: &[u8],
271+
) -> CustomResult<bool, errors::CryptoError> {
272+
let key = blake3::derive_key(&self.0, secret);
273+
let output = blake3::keyed_hash(&key, msg);
274+
Ok(output.as_bytes() == signature)
275+
}
276+
}
277+
241278
/// Represents the GCM-AES-256 algorithm
242279
#[derive(Debug)]
243280
pub struct GcmAes256;

crates/masking/src/secret.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use std::{fmt, marker::PhantomData};
66

7-
use crate::{strategy::Strategy, PeekInterface};
7+
use crate::{strategy::Strategy, PeekInterface, StrongSecret};
88

99
///
1010
/// Secret thing.
@@ -81,6 +81,14 @@ where
8181
{
8282
f(self.inner_secret).into()
8383
}
84+
85+
/// Convert to [`StrongSecret`]
86+
pub fn into_strong(self) -> StrongSecret<SecretValue, MaskingStrategy>
87+
where
88+
SecretValue: zeroize::DefaultIsZeroes,
89+
{
90+
StrongSecret::new(self.inner_secret)
91+
}
8492
}
8593

8694
impl<SecretValue, MaskingStrategy> PeekInterface<SecretValue>

crates/router/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ merchant_account_v2 = ["api_models/merchant_account_v2", "diesel_models/merchant
3838
payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2", "hyperswitch_domain_models/payment_v2"]
3939
merchant_connector_account_v2 = ["api_models/merchant_connector_account_v2", "kgraph_utils/merchant_connector_account_v2"]
4040

41+
# Partial Auth
42+
# The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request.
43+
# This is named as partial-auth because the router will still try to authenticate if the `x-merchant-id` header is not present.
44+
partial-auth = []
45+
4146
[dependencies]
4247
actix-cors = "0.6.5"
4348
actix-http = "3.6.0"

crates/router/src/compatibility/stripe/customers.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub async fn customer_create(
5353
|state, auth, req, _| {
5454
customers::create_customer(state, auth.merchant_account, auth.key_store, req)
5555
},
56-
&auth::ApiKeyAuth,
56+
&auth::HeaderAuth(auth::ApiKeyAuth),
5757
api_locking::LockAction::NotApplicable,
5858
))
5959
.await
@@ -87,7 +87,7 @@ pub async fn customer_retrieve(
8787
|state, auth, req, _| {
8888
customers::retrieve_customer(state, auth.merchant_account, auth.key_store, req)
8989
},
90-
&auth::ApiKeyAuth,
90+
&auth::HeaderAuth(auth::ApiKeyAuth),
9191
api_locking::LockAction::NotApplicable,
9292
))
9393
.await
@@ -132,7 +132,7 @@ pub async fn customer_update(
132132
|state, auth, req, _| {
133133
customers::update_customer(state, auth.merchant_account, req, auth.key_store)
134134
},
135-
&auth::ApiKeyAuth,
135+
&auth::HeaderAuth(auth::ApiKeyAuth),
136136
api_locking::LockAction::NotApplicable,
137137
))
138138
.await
@@ -166,7 +166,7 @@ pub async fn customer_delete(
166166
|state, auth: auth::AuthenticationData, req, _| {
167167
customers::delete_customer(state, auth.merchant_account, req, auth.key_store)
168168
},
169-
&auth::ApiKeyAuth,
169+
&auth::HeaderAuth(auth::ApiKeyAuth),
170170
api_locking::LockAction::NotApplicable,
171171
))
172172
.await
@@ -208,7 +208,7 @@ pub async fn list_customer_payment_method_api(
208208
None,
209209
)
210210
},
211-
&auth::ApiKeyAuth,
211+
&auth::HeaderAuth(auth::ApiKeyAuth),
212212
api_locking::LockAction::NotApplicable,
213213
))
214214
.await

crates/router/src/compatibility/stripe/payment_intents.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod types;
2+
23
use actix_web::{web, HttpRequest, HttpResponse};
34
use api_models::payments as payment_types;
45
use error_stack::report;
@@ -71,7 +72,7 @@ pub async fn payment_intents_create(
7172
api_types::HeaderPayload::default(),
7273
)
7374
},
74-
&auth::ApiKeyAuth,
75+
&auth::HeaderAuth(auth::ApiKeyAuth),
7576
locking_action,
7677
))
7778
.await
@@ -400,7 +401,7 @@ pub async fn payment_intents_capture(
400401
api_types::HeaderPayload::default(),
401402
)
402403
},
403-
&auth::ApiKeyAuth,
404+
&auth::HeaderAuth(auth::ApiKeyAuth),
404405
locking_action,
405406
))
406407
.await
@@ -500,7 +501,7 @@ pub async fn payment_intent_list(
500501
|state, auth, req, _| {
501502
payments::list_payments(state, auth.merchant_account, auth.key_store, req)
502503
},
503-
&auth::ApiKeyAuth,
504+
&auth::HeaderAuth(auth::ApiKeyAuth),
504505
api_locking::LockAction::NotApplicable,
505506
))
506507
.await

crates/router/src/compatibility/stripe/refunds.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod types;
2+
23
use actix_web::{web, HttpRequest, HttpResponse};
34
use error_stack::report;
45
use router_env::{instrument, tracing, Flow, Tag};
@@ -51,7 +52,7 @@ pub async fn refund_create(
5152
|state, auth, req, _| {
5253
refunds::refund_create_core(state, auth.merchant_account, auth.key_store, req)
5354
},
54-
&auth::ApiKeyAuth,
55+
&auth::HeaderAuth(auth::ApiKeyAuth),
5556
api_locking::LockAction::NotApplicable,
5657
))
5758
.await
@@ -101,7 +102,7 @@ pub async fn refund_retrieve_with_gateway_creds(
101102
refunds::refund_retrieve_core,
102103
)
103104
},
104-
&auth::ApiKeyAuth,
105+
&auth::HeaderAuth(auth::ApiKeyAuth),
105106
api_locking::LockAction::NotApplicable,
106107
))
107108
.await
@@ -143,7 +144,7 @@ pub async fn refund_retrieve(
143144
refunds::refund_retrieve_core,
144145
)
145146
},
146-
&auth::ApiKeyAuth,
147+
&auth::HeaderAuth(auth::ApiKeyAuth),
147148
api_locking::LockAction::NotApplicable,
148149
))
149150
.await
@@ -175,7 +176,7 @@ pub async fn refund_update(
175176
&req,
176177
create_refund_update_req,
177178
|state, auth, req, _| refunds::refund_update_core(state, auth.merchant_account, req),
178-
&auth::ApiKeyAuth,
179+
&auth::HeaderAuth(auth::ApiKeyAuth),
179180
api_locking::LockAction::NotApplicable,
180181
))
181182
.await

crates/router/src/compatibility/stripe/setup_intents.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod types;
2+
23
use actix_web::{web, HttpRequest, HttpResponse};
34
use api_models::payments as payment_types;
45
use error_stack::report;
@@ -72,7 +73,7 @@ pub async fn setup_intents_create(
7273
api_types::HeaderPayload::default(),
7374
)
7475
},
75-
&auth::ApiKeyAuth,
76+
&auth::HeaderAuth(auth::ApiKeyAuth),
7677
api_locking::LockAction::NotApplicable,
7778
))
7879
.await

crates/router/src/configs/defaults.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8929,6 +8929,13 @@ impl Default for super::settings::ApiKeys {
89298929
// Specifies the number of days before API key expiry when email reminders should be sent
89308930
#[cfg(feature = "email")]
89318931
expiry_reminder_days: vec![7, 3, 1],
8932+
8933+
// Hex-encoded key used for calculating checksum for partial auth
8934+
#[cfg(feature = "partial-auth")]
8935+
checksum_auth_key: String::new().into(),
8936+
// context used for blake3
8937+
#[cfg(feature = "partial-auth")]
8938+
checksum_auth_context: String::new().into(),
89328939
}
89338940
}
89348941
}

crates/router/src/configs/secrets_transformers.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,24 @@ impl SecretsHandler for settings::ApiKeys {
114114
#[cfg(feature = "email")]
115115
let expiry_reminder_days = api_keys.expiry_reminder_days.clone();
116116

117+
#[cfg(feature = "partial-auth")]
118+
let checksum_auth_context = secret_management_client
119+
.get_secret(api_keys.checksum_auth_context.clone())
120+
.await?;
121+
#[cfg(feature = "partial-auth")]
122+
let checksum_auth_key = secret_management_client
123+
.get_secret(api_keys.checksum_auth_key.clone())
124+
.await?;
125+
117126
Ok(value.transition_state(|_| Self {
118127
hash_key,
119128
#[cfg(feature = "email")]
120129
expiry_reminder_days,
130+
131+
#[cfg(feature = "partial-auth")]
132+
checksum_auth_key,
133+
#[cfg(feature = "partial-auth")]
134+
checksum_auth_context,
121135
}))
122136
}
123137
}

0 commit comments

Comments
 (0)