mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 12:15:40 +08:00
feat(router): Add new JWT authentication variants and use them (#2835)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
@ -9,6 +9,10 @@ use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
||||
use masking::{PeekInterface, StrongSecret};
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
use super::jwt;
|
||||
#[cfg(feature = "olap")]
|
||||
use crate::consts;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
core::{
|
||||
@ -71,6 +75,37 @@ impl AuthenticationType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct AuthToken {
|
||||
pub user_id: String,
|
||||
pub merchant_id: String,
|
||||
pub role_id: String,
|
||||
pub exp: u64,
|
||||
pub org_id: String,
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
impl AuthToken {
|
||||
pub async fn new_token(
|
||||
user_id: String,
|
||||
merchant_id: String,
|
||||
role_id: String,
|
||||
settings: &settings::Settings,
|
||||
org_id: String,
|
||||
) -> errors::UserResult<String> {
|
||||
let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS);
|
||||
let exp = jwt::generate_exp(exp_duration)?.as_secs();
|
||||
let token_payload = Self {
|
||||
user_id,
|
||||
merchant_id,
|
||||
role_id,
|
||||
exp,
|
||||
org_id,
|
||||
};
|
||||
jwt::generate_jwt(&token_payload, settings).await
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AuthInfo {
|
||||
fn get_merchant_id(&self) -> Option<&str>;
|
||||
}
|
||||
@ -366,14 +401,58 @@ where
|
||||
request_headers: &HeaderMap,
|
||||
state: &A,
|
||||
) -> RouterResult<((), AuthenticationType)> {
|
||||
let mut token = get_jwt(request_headers)?;
|
||||
token = strip_jwt_token(token)?;
|
||||
decode_jwt::<JwtAuthPayloadFetchUnit>(token, state)
|
||||
.await
|
||||
.map(|_| ((), AuthenticationType::NoAuth))
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
Ok((
|
||||
(),
|
||||
AuthenticationType::MerchantJWT {
|
||||
merchant_id: payload.merchant_id,
|
||||
user_id: Some(payload.user_id),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JWTAuthMerchantFromRoute {
|
||||
pub merchant_id: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<A> AuthenticateAndFetch<(), A> for JWTAuthMerchantFromRoute
|
||||
where
|
||||
A: AppStateInfo + Sync,
|
||||
{
|
||||
async fn authenticate_and_fetch(
|
||||
&self,
|
||||
request_headers: &HeaderMap,
|
||||
state: &A,
|
||||
) -> RouterResult<((), AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
|
||||
// Check if token has access to merchantID that has been requested through query param
|
||||
if payload.merchant_id != self.merchant_id {
|
||||
return Err(report!(errors::ApiErrorResponse::InvalidJwtToken));
|
||||
}
|
||||
Ok((
|
||||
(),
|
||||
AuthenticationType::MerchantJWT {
|
||||
merchant_id: payload.merchant_id,
|
||||
user_id: Some(payload.user_id),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn parse_jwt_payload<A, T>(headers: &HeaderMap, state: &A) -> RouterResult<T>
|
||||
where
|
||||
T: serde::de::DeserializeOwned,
|
||||
A: AppStateInfo + Sync,
|
||||
{
|
||||
let token = get_jwt_from_authorization_header(headers)?;
|
||||
let payload = decode_jwt(token, state).await?;
|
||||
|
||||
Ok(payload)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct JwtAuthPayloadFetchMerchantAccount {
|
||||
merchant_id: String,
|
||||
@ -389,9 +468,9 @@ where
|
||||
request_headers: &HeaderMap,
|
||||
state: &A,
|
||||
) -> RouterResult<(AuthenticationData, AuthenticationType)> {
|
||||
let mut token = get_jwt(request_headers)?;
|
||||
token = strip_jwt_token(token)?;
|
||||
let payload = decode_jwt::<JwtAuthPayloadFetchMerchantAccount>(token, state).await?;
|
||||
let payload =
|
||||
parse_jwt_payload::<A, JwtAuthPayloadFetchMerchantAccount>(request_headers, state)
|
||||
.await?;
|
||||
let key_store = state
|
||||
.store()
|
||||
.get_merchant_key_store_by_merchant_id(
|
||||
@ -595,14 +674,16 @@ pub fn get_header_value_by_key(key: String, headers: &HeaderMap) -> RouterResult
|
||||
.transpose()
|
||||
}
|
||||
|
||||
pub fn get_jwt(headers: &HeaderMap) -> RouterResult<&str> {
|
||||
pub fn get_jwt_from_authorization_header(headers: &HeaderMap) -> RouterResult<&str> {
|
||||
headers
|
||||
.get(crate::headers::AUTHORIZATION)
|
||||
.get_required_value(crate::headers::AUTHORIZATION)?
|
||||
.to_str()
|
||||
.into_report()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to convert JWT token to string")
|
||||
.attach_printable("Failed to convert JWT token to string")?
|
||||
.strip_prefix("Bearer ")
|
||||
.ok_or(errors::ApiErrorResponse::InvalidJwtToken.into())
|
||||
}
|
||||
|
||||
pub fn strip_jwt_token(token: &str) -> RouterResult<&str> {
|
||||
|
||||
Reference in New Issue
Block a user