feat(user): add single purpose token and auth (#4470)

This commit is contained in:
Apoorv Dixit
2024-04-29 17:55:01 +05:30
committed by GitHub
parent ac9d856add
commit c20ecb855a
3 changed files with 94 additions and 2 deletions

View File

@ -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_";

View File

@ -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;

View File

@ -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
}
}