mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 21:37:41 +08:00
refactor(authentication): authenticate merchant by API keys from API keys table (#712)
This commit is contained in:
@ -1,10 +1,8 @@
|
||||
use common_utils::{date_time, errors::CustomResult, fp_utils};
|
||||
use common_utils::date_time;
|
||||
use error_stack::{report, IntoReport, ResultExt};
|
||||
use masking::{PeekInterface, Secret, StrongSecret};
|
||||
use masking::{PeekInterface, StrongSecret};
|
||||
use router_env::{instrument, tracing};
|
||||
|
||||
#[cfg(feature = "kms")]
|
||||
use crate::services::kms;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
@ -14,6 +12,8 @@ use crate::{
|
||||
types::{api, storage, transformers::ForeignInto},
|
||||
utils,
|
||||
};
|
||||
#[cfg(feature = "kms")]
|
||||
use crate::{routes::metrics, services::kms};
|
||||
|
||||
pub static HASH_KEY: tokio::sync::OnceCell<StrongSecret<[u8; PlaintextApiKey::HASH_KEY_LEN]>> =
|
||||
tokio::sync::OnceCell::const_new();
|
||||
@ -28,6 +28,10 @@ pub async fn get_hash_key(
|
||||
api_key_config.kms_encrypted_hash_key.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
metrics::AWS_KMS_FAILURES.add(&metrics::CONTEXT, 1, &[]);
|
||||
error
|
||||
})
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to KMS decrypt API key hashing key")?;
|
||||
|
||||
@ -49,11 +53,13 @@ pub async fn get_hash_key(
|
||||
|
||||
// Defining new types `PlaintextApiKey` and `HashedApiKey` in the hopes of reducing the possibility
|
||||
// of plaintext API key being stored in the data store.
|
||||
pub struct PlaintextApiKey(Secret<String>);
|
||||
pub struct PlaintextApiKey(StrongSecret<String>);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct HashedApiKey(String);
|
||||
|
||||
impl PlaintextApiKey {
|
||||
pub const HASH_KEY_LEN: usize = 32;
|
||||
const HASH_KEY_LEN: usize = 32;
|
||||
|
||||
const PREFIX_LEN: usize = 12;
|
||||
|
||||
@ -107,22 +113,6 @@ impl PlaintextApiKey {
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn verify_hash(
|
||||
&self,
|
||||
key: &[u8; Self::HASH_KEY_LEN],
|
||||
stored_api_key: &HashedApiKey,
|
||||
) -> CustomResult<(), errors::ApiKeyError> {
|
||||
// Converting both hashes to `blake3::Hash` since it provides constant-time equality checks
|
||||
let provided_api_key_hash = blake3::keyed_hash(key, self.0.peek().as_bytes());
|
||||
let stored_api_key_hash = blake3::Hash::from_hex(&stored_api_key.0)
|
||||
.into_report()
|
||||
.change_context(errors::ApiKeyError::FailedToReadHashFromHex)?;
|
||||
|
||||
fp_utils::when(provided_api_key_hash != stored_api_key_hash, || {
|
||||
Err(errors::ApiKeyError::HashVerificationFailed).into_report()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -165,7 +155,7 @@ pub async fn retrieve_api_key(
|
||||
key_id: &str,
|
||||
) -> RouterResponse<api::RetrieveApiKeyResponse> {
|
||||
let api_key = store
|
||||
.find_api_key_optional(key_id)
|
||||
.find_api_key_by_key_id_optional(key_id)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError) // If retrieve failed
|
||||
.attach_printable("Failed to retrieve new API key")?
|
||||
@ -224,12 +214,30 @@ pub async fn list_api_keys(
|
||||
Ok(ApplicationResponse::Json(api_keys))
|
||||
}
|
||||
|
||||
impl From<&str> for PlaintextApiKey {
|
||||
fn from(s: &str) -> Self {
|
||||
Self(s.to_owned().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for PlaintextApiKey {
|
||||
fn from(s: String) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HashedApiKey> for storage::HashedApiKey {
|
||||
fn from(hashed_api_key: HashedApiKey) -> Self {
|
||||
hashed_api_key.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<storage::HashedApiKey> for HashedApiKey {
|
||||
fn from(hashed_api_key: storage::HashedApiKey) -> Self {
|
||||
Self(hashed_api_key.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::expect_used, clippy::unwrap_used)]
|
||||
@ -251,8 +259,7 @@ mod tests {
|
||||
hashed_api_key.0.as_bytes()
|
||||
);
|
||||
|
||||
plaintext_api_key
|
||||
.verify_hash(hash_key.peek(), &hashed_api_key)
|
||||
.unwrap();
|
||||
let new_hashed_api_key = plaintext_api_key.keyed_hash(hash_key.peek());
|
||||
assert_eq!(hashed_api_key, new_hashed_api_key)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user