mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(users): Implemented cookie parsing for auth (#4298)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -427,6 +427,7 @@ impl From<errors::ApiErrorResponse> for StripeErrorCode {
|
|||||||
| errors::ApiErrorResponse::InvalidJwtToken
|
| errors::ApiErrorResponse::InvalidJwtToken
|
||||||
| errors::ApiErrorResponse::GenericUnauthorized { .. }
|
| errors::ApiErrorResponse::GenericUnauthorized { .. }
|
||||||
| errors::ApiErrorResponse::AccessForbidden { .. }
|
| errors::ApiErrorResponse::AccessForbidden { .. }
|
||||||
|
| errors::ApiErrorResponse::InvalidCookie
|
||||||
| errors::ApiErrorResponse::InvalidEphemeralKey => Self::Unauthorized,
|
| errors::ApiErrorResponse::InvalidEphemeralKey => Self::Unauthorized,
|
||||||
errors::ApiErrorResponse::InvalidRequestUrl
|
errors::ApiErrorResponse::InvalidRequestUrl
|
||||||
| errors::ApiErrorResponse::InvalidHttpMethod
|
| errors::ApiErrorResponse::InvalidHttpMethod
|
||||||
|
|||||||
@ -260,6 +260,11 @@ pub enum ApiErrorResponse {
|
|||||||
CurrencyConversionFailed,
|
CurrencyConversionFailed,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_25", message = "Cannot delete the default payment method")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_25", message = "Cannot delete the default payment method")]
|
||||||
PaymentMethodDeleteFailed,
|
PaymentMethodDeleteFailed,
|
||||||
|
#[error(
|
||||||
|
error_type = ErrorType::InvalidRequestError, code = "IR_26",
|
||||||
|
message = "Invalid Cookie"
|
||||||
|
)]
|
||||||
|
InvalidCookie,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PTError for ApiErrorResponse {
|
impl PTError for ApiErrorResponse {
|
||||||
|
|||||||
@ -292,6 +292,9 @@ impl ErrorSwitch<api_models::errors::types::ApiErrorResponse> for ApiErrorRespon
|
|||||||
Self::PaymentMethodDeleteFailed => {
|
Self::PaymentMethodDeleteFailed => {
|
||||||
AER::BadRequest(ApiError::new("IR", 25, "Cannot delete the default payment method", None))
|
AER::BadRequest(ApiError::new("IR", 25, "Cannot delete the default payment method", None))
|
||||||
}
|
}
|
||||||
|
Self::InvalidCookie => {
|
||||||
|
AER::BadRequest(ApiError::new("IR", 26, "Invalid Cookie", None))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use common_utils::date_time;
|
|||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
||||||
use masking::PeekInterface;
|
use masking::PeekInterface;
|
||||||
|
use router_env::logger;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use self::blacklist::BlackList;
|
use self::blacklist::BlackList;
|
||||||
@ -33,7 +34,6 @@ use crate::{
|
|||||||
utils::OptionExt,
|
utils::OptionExt,
|
||||||
};
|
};
|
||||||
pub mod blacklist;
|
pub mod blacklist;
|
||||||
#[cfg(feature = "olap")]
|
|
||||||
pub mod cookies;
|
pub mod cookies;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -598,6 +598,15 @@ where
|
|||||||
A: AppStateInfo + Sync,
|
A: AppStateInfo + Sync,
|
||||||
{
|
{
|
||||||
let token = get_jwt_from_authorization_header(headers)?;
|
let token = get_jwt_from_authorization_header(headers)?;
|
||||||
|
if let Some(token_from_cookies) = get_cookie_from_header(headers)
|
||||||
|
.ok()
|
||||||
|
.and_then(|cookies| cookies::parse_cookie(cookies).ok())
|
||||||
|
{
|
||||||
|
logger::info!(
|
||||||
|
"Cookie header and authorization header JWT comparison result: {}",
|
||||||
|
token == token_from_cookies
|
||||||
|
);
|
||||||
|
}
|
||||||
let payload = decode_jwt(token, state).await?;
|
let payload = decode_jwt(token, state).await?;
|
||||||
|
|
||||||
Ok(payload)
|
Ok(payload)
|
||||||
@ -959,6 +968,13 @@ pub fn get_jwt_from_authorization_header(headers: &HeaderMap) -> RouterResult<&s
|
|||||||
.ok_or(errors::ApiErrorResponse::InvalidJwtToken.into())
|
.ok_or(errors::ApiErrorResponse::InvalidJwtToken.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_cookie_from_header(headers: &HeaderMap) -> RouterResult<&str> {
|
||||||
|
headers
|
||||||
|
.get(cookies::get_cookie_header())
|
||||||
|
.and_then(|header_value| header_value.to_str().ok())
|
||||||
|
.ok_or(errors::ApiErrorResponse::InvalidCookie.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn strip_jwt_token(token: &str) -> RouterResult<&str> {
|
pub fn strip_jwt_token(token: &str) -> RouterResult<&str> {
|
||||||
token
|
token
|
||||||
.strip_prefix("Bearer ")
|
.strip_prefix("Bearer ")
|
||||||
|
|||||||
@ -1,15 +1,27 @@
|
|||||||
|
use cookie::Cookie;
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
use cookie::{
|
use cookie::{
|
||||||
time::{Duration, OffsetDateTime},
|
time::{Duration, OffsetDateTime},
|
||||||
Cookie, SameSite,
|
SameSite,
|
||||||
};
|
};
|
||||||
use masking::{ExposeInterface, Mask, Secret};
|
use error_stack::{report, ResultExt};
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
use masking::Mask;
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
use masking::{ExposeInterface, Secret};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{JWT_TOKEN_COOKIE_NAME, JWT_TOKEN_TIME_IN_SECS},
|
consts::JWT_TOKEN_COOKIE_NAME,
|
||||||
|
core::errors::{ApiErrorResponse, RouterResult},
|
||||||
|
};
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
use crate::{
|
||||||
|
consts::JWT_TOKEN_TIME_IN_SECS,
|
||||||
core::errors::{UserErrors, UserResponse},
|
core::errors::{UserErrors, UserResponse},
|
||||||
services::ApplicationResponse,
|
services::ApplicationResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
pub fn set_cookie_response<R>(response: R, token: Secret<String>) -> UserResponse<R> {
|
pub fn set_cookie_response<R>(response: R, token: Secret<String>) -> UserResponse<R> {
|
||||||
let jwt_expiry_in_seconds = JWT_TOKEN_TIME_IN_SECS
|
let jwt_expiry_in_seconds = JWT_TOKEN_TIME_IN_SECS
|
||||||
.try_into()
|
.try_into()
|
||||||
@ -19,16 +31,17 @@ pub fn set_cookie_response<R>(response: R, token: Secret<String>) -> UserRespons
|
|||||||
let header_value = create_cookie(token, expiry, max_age)
|
let header_value = create_cookie(token, expiry, max_age)
|
||||||
.to_string()
|
.to_string()
|
||||||
.into_masked();
|
.into_masked();
|
||||||
let header_key = get_cookie_header();
|
let header_key = get_set_cookie_header();
|
||||||
let header = vec![(header_key, header_value)];
|
let header = vec![(header_key, header_value)];
|
||||||
|
|
||||||
Ok(ApplicationResponse::JsonWithHeaders((response, header)))
|
Ok(ApplicationResponse::JsonWithHeaders((response, header)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
pub fn remove_cookie_response() -> UserResponse<()> {
|
pub fn remove_cookie_response() -> UserResponse<()> {
|
||||||
let (expiry, max_age) = get_expiry_and_max_age_from_seconds(0);
|
let (expiry, max_age) = get_expiry_and_max_age_from_seconds(0);
|
||||||
|
|
||||||
let header_key = get_cookie_header();
|
let header_key = get_set_cookie_header();
|
||||||
let header_value = create_cookie("".to_string().into(), expiry, max_age)
|
let header_value = create_cookie("".to_string().into(), expiry, max_age)
|
||||||
.to_string()
|
.to_string()
|
||||||
.into_masked();
|
.into_masked();
|
||||||
@ -36,6 +49,19 @@ pub fn remove_cookie_response() -> UserResponse<()> {
|
|||||||
Ok(ApplicationResponse::JsonWithHeaders(((), header)))
|
Ok(ApplicationResponse::JsonWithHeaders(((), header)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_cookie(cookies: &str) -> RouterResult<String> {
|
||||||
|
Cookie::split_parse(cookies)
|
||||||
|
.find_map(|cookie| {
|
||||||
|
cookie
|
||||||
|
.ok()
|
||||||
|
.filter(|parsed_cookie| parsed_cookie.name() == JWT_TOKEN_COOKIE_NAME)
|
||||||
|
.map(|parsed_cookie| parsed_cookie.value().to_owned())
|
||||||
|
})
|
||||||
|
.ok_or(report!(ApiErrorResponse::InvalidCookie))
|
||||||
|
.attach_printable("Cookie Parsing Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
fn create_cookie<'c>(
|
fn create_cookie<'c>(
|
||||||
token: Secret<String>,
|
token: Secret<String>,
|
||||||
expires: OffsetDateTime,
|
expires: OffsetDateTime,
|
||||||
@ -51,12 +77,18 @@ fn create_cookie<'c>(
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
fn get_expiry_and_max_age_from_seconds(seconds: i64) -> (OffsetDateTime, Duration) {
|
fn get_expiry_and_max_age_from_seconds(seconds: i64) -> (OffsetDateTime, Duration) {
|
||||||
let max_age = Duration::seconds(seconds);
|
let max_age = Duration::seconds(seconds);
|
||||||
let expiry = OffsetDateTime::now_utc().saturating_add(max_age);
|
let expiry = OffsetDateTime::now_utc().saturating_add(max_age);
|
||||||
(expiry, max_age)
|
(expiry, max_age)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cookie_header() -> String {
|
#[cfg(feature = "olap")]
|
||||||
|
fn get_set_cookie_header() -> String {
|
||||||
actix_http::header::SET_COOKIE.to_string()
|
actix_http::header::SET_COOKIE.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_cookie_header() -> String {
|
||||||
|
actix_http::header::COOKIE.to_string()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user