mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(user): add single purpose token and auth (#4470)
This commit is contained in:
@ -68,6 +68,8 @@ pub const LOCKER_REDIS_EXPIRY_SECONDS: u32 = 60 * 15; // 15 minutes
|
||||
|
||||
pub const JWT_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24 * 2; // 2 days
|
||||
|
||||
pub const SINGLE_PURPOSE_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24; // 1 day
|
||||
|
||||
pub const JWT_TOKEN_COOKIE_NAME: &str = "login_token";
|
||||
|
||||
pub const USER_BLACKLIST_PREFIX: &str = "BU_";
|
||||
|
||||
@ -64,6 +64,10 @@ pub enum AuthenticationType {
|
||||
UserJwt {
|
||||
user_id: String,
|
||||
},
|
||||
SinglePurposeJWT {
|
||||
user_id: String,
|
||||
purpose: Purpose,
|
||||
},
|
||||
MerchantId {
|
||||
merchant_id: String,
|
||||
},
|
||||
@ -101,11 +105,51 @@ impl AuthenticationType {
|
||||
user_id: _,
|
||||
}
|
||||
| Self::WebhookAuth { merchant_id } => Some(merchant_id.as_ref()),
|
||||
Self::AdminApiKey | Self::UserJwt { .. } | Self::NoAuth => None,
|
||||
Self::AdminApiKey
|
||||
| Self::UserJwt { .. }
|
||||
| Self::SinglePurposeJWT { .. }
|
||||
| Self::NoAuth => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserFromSinglePurposeToken {
|
||||
pub user_id: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct SinglePurposeToken {
|
||||
pub user_id: String,
|
||||
pub purpose: Purpose,
|
||||
pub exp: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, strum::Display, serde::Deserialize, serde::Serialize)]
|
||||
pub enum Purpose {
|
||||
AcceptInvite,
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
impl SinglePurposeToken {
|
||||
pub async fn new_token(
|
||||
user_id: String,
|
||||
purpose: Purpose,
|
||||
settings: &Settings,
|
||||
) -> UserResult<String> {
|
||||
let exp_duration =
|
||||
std::time::Duration::from_secs(consts::SINGLE_PURPOSE_TOKEN_TIME_IN_SECS);
|
||||
let exp = jwt::generate_exp(exp_duration)?.as_secs();
|
||||
let token_payload = Self {
|
||||
user_id,
|
||||
purpose,
|
||||
exp,
|
||||
};
|
||||
jwt::generate_jwt(&token_payload, settings).await
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This has to be removed once single purpose token is used as a intermediate token
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserWithoutMerchantFromToken {
|
||||
pub user_id: String,
|
||||
@ -316,6 +360,42 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SinglePurposeJWTAuth(pub Purpose);
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
#[async_trait]
|
||||
impl<A> AuthenticateAndFetch<UserFromSinglePurposeToken, A> for SinglePurposeJWTAuth
|
||||
where
|
||||
A: AppStateInfo + Sync,
|
||||
{
|
||||
async fn authenticate_and_fetch(
|
||||
&self,
|
||||
request_headers: &HeaderMap,
|
||||
state: &A,
|
||||
) -> RouterResult<(UserFromSinglePurposeToken, AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, SinglePurposeToken>(request_headers, state).await?;
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
if self.0 != payload.purpose {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
Ok((
|
||||
UserFromSinglePurposeToken {
|
||||
user_id: payload.user_id.clone(),
|
||||
},
|
||||
AuthenticationType::SinglePurposeJWT {
|
||||
user_id: payload.user_id,
|
||||
purpose: payload.purpose,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AdminApiAuth;
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ use common_utils::date_time;
|
||||
use error_stack::ResultExt;
|
||||
use redis_interface::RedisConnectionPool;
|
||||
|
||||
use super::{AuthToken, UserAuthToken};
|
||||
use super::{AuthToken, SinglePurposeToken, UserAuthToken};
|
||||
#[cfg(feature = "email")]
|
||||
use crate::consts::{EMAIL_TOKEN_BLACKLIST_PREFIX, EMAIL_TOKEN_TIME_IN_SECS};
|
||||
use crate::{
|
||||
@ -163,3 +163,13 @@ impl BlackList for UserAuthToken {
|
||||
check_user_in_blacklist(state, &self.user_id, self.exp).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BlackList for SinglePurposeToken {
|
||||
async fn check_in_blacklist<A>(&self, state: &A) -> RouterResult<bool>
|
||||
where
|
||||
A: AppStateInfo + Sync,
|
||||
{
|
||||
check_user_in_blacklist(state, &self.user_id, self.exp).await
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user