use JwtSigner for session cookie

This commit is contained in:
Brandon Thomas committed 2023-10-19 02:39:17 -04:00
1 parent fdd60b436a
commit c3e5b42bf3
13 files changed
+54 -29

No files matched your search

Generated
+1
View File
@@ -6837,6 +6837,7 @@ dependencies = [
"bcrypt",
"chrono",
"container_common",
"cookies",
"dotenv",
"enums",
"errors",
+1
View File
@@ -16,6 +16,7 @@ path = "src/lib.rs"
# Internal
container_common = { path = "../../../lib/deprecated/container_common" }
cookies = { path = "../../../lib/cookies" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
http_server_common = { path = "../../../lib/deprecated/http_server_common" }
@@ -0,0 +1,2 @@
pub mod session_cookie_manager;
pub mod session_cookie_payload;
@@ -8,15 +8,13 @@ use std::collections::BTreeMap;
use actix_web::cookie::Cookie;
use actix_web::HttpRequest;
use anyhow::anyhow;
use hmac::Hmac;
use hmac::NewMac;
use jwt::SignWithKey;
use jwt::VerifyWithKey;
use log::warn;
use sha2::Sha256;
use cookies::jwt_signer::JwtSigner;
use errors::AnyhowResult;
use crate::cookies::session_cookie_payload::SessionCookiePayload;
/**
* Cookie version history
*
@@ -34,15 +32,7 @@ const SESSION_COOKIE_NAME : &str = "session";
pub struct SessionCookieManager {
cookie_domain: String,
hmac_secret: String,
}
#[derive(Clone)]
pub struct SessionCookiePayload {
/// The database primary key for the session instance.
pub session_token: String,
/// The primary key identifier of the user.
/// Version 1 cookies do not have a user token, hence it is optional.
pub maybe_user_token: Option<String>,
jwt_signer: JwtSigner,
}
impl SessionCookieManager {
@@ -50,13 +40,11 @@ impl SessionCookieManager {
Self {
cookie_domain: cookie_domain.to_string(),
hmac_secret: hmac_secret.to_string(),
jwt_signer: JwtSigner::new(hmac_secret).unwrap(),//TODO FIXME
}
}
pub fn create_cookie(&self, session_token: &str, user_token: &str) -> AnyhowResult<Cookie> {
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
let cookie_version = COOKIE_VERSION.to_string();
let mut claims = BTreeMap::new();
@@ -64,7 +52,7 @@ impl SessionCookieManager {
claims.insert("user_token", user_token);
claims.insert("cookie_version", &cookie_version);
let jwt_string = claims.sign_with_key(&key)?;
let jwt_string = self.jwt_signer.claims_to_jwt(&claims)?;
let make_secure = !self.cookie_domain.to_lowercase().contains("jungle.horse")
&& !self.cookie_domain.to_lowercase().contains("localhost");
@@ -91,12 +79,9 @@ impl SessionCookieManager {
pub fn decode_session_cookie_payload(&self, session_cookie: &Cookie)
-> AnyhowResult<SessionCookiePayload>
{
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
let cookie_contents = session_cookie.value().to_string();
let claims: BTreeMap<String, String> = cookie_contents.verify_with_key(&key)?;
let claims = self.jwt_signer.jwt_to_claims(&cookie_contents)?;
let session_token = claims["session_token"].clone();
let maybe_user_token = claims.get("user_token")
@@ -149,3 +134,30 @@ impl SessionCookieManager {
}
}
#[cfg(test)]
mod tests {
use crate::cookies::session_cookie_manager::SessionCookieManager;
#[test]
fn test_cookie_payload() {
// NB: Let's make extra sure this always works when migrating cookies, else we'll accidentally log out logged-in users.
// (These are version 2 cookies.)
let manager = SessionCookieManager::new("fakeyou.com", "secret");
let cookie = manager.create_cookie("ex_session_token", "ex_user_token").unwrap();
assert_eq!(cookie.value(), "eyJhbGciOiJIUzI1NiJ9.eyJjb29raWVfdmVyc2lvbiI6IjIiLCJzZXNzaW9uX3Rva2VuIjoiZXhfc2Vzc2lvbl90b2tlbiIsInVzZXJfdG9rZW4iOiJleF91c2VyX3Rva2VuIn0.94ly2gHhlPVtnANsNy6cJozFVmId4imwW5v-mei7jD8");
}
#[test]
fn test_cookie_round_trip() {
// NB: Let's make extra sure this always works when migrating cookies, else we'll accidentally log out logged-in users.
// (These are version 2 cookies.)
let manager = SessionCookieManager::new("fakeyou.com", "secret");
let cookie = manager.create_cookie("ex_session_token", "ex_user_token").unwrap();
let decoded = manager.decode_session_cookie_payload(&cookie).unwrap();
assert_eq!(decoded.session_token, "ex_session_token".to_string());
assert_eq!(decoded.maybe_user_token, Some("ex_user_token".to_string()));
}
}
@@ -0,0 +1,9 @@
#[derive(Clone)]
pub struct SessionCookiePayload {
/// The database primary key for the session instance.
pub session_token: String,
/// The primary key identifier of the user.
/// Version 1 cookies do not have a user token, hence it is optional.
pub maybe_user_token: Option<String>,
}
@@ -21,8 +21,8 @@ use mysql_queries::queries::users::user_sessions::create_user_session::create_us
use mysql_queries::tokens::Tokens;
use user_input_common::check_for_slurs::contains_slurs;
use crate::cookies::session_cookie_manager::SessionCookieManager;
use crate::utils::email_to_gravatar::email_to_gravatar;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::validations::is_reserved_username::is_reserved_username;
use crate::validations::validate_passwords::validate_passwords;
use crate::validations::validate_username::validate_username;
@@ -19,7 +19,7 @@ use mysql_queries::queries::users::user::lookup_user_for_login_by_email::lookup_
use mysql_queries::queries::users::user::lookup_user_for_login_by_username::lookup_user_for_login_by_username;
use mysql_queries::queries::users::user_sessions::create_user_session::create_user_session;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::cookies::session_cookie_manager::SessionCookieManager;
#[derive(Deserialize)]
pub struct LoginRequest {
@@ -14,7 +14,7 @@ use sqlx::MySqlPool;
use http_server_common::response::response_error_helpers::to_simple_json_error;
use mysql_queries::queries::users::user_sessions::delete_user_session::delete_user_session;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::cookies::session_cookie_manager::SessionCookieManager;
#[derive(Serialize)]
pub struct LogoutSuccessResponse {
+1
View File
@@ -4,6 +4,7 @@
pub const RESERVED_USERNAMES : &str = include_str!("../../../../../includes/binary_includes/reserved_usernames.txt");
pub const RESERVED_SUBSTRINGS : &str = include_str!("../../../../../includes/binary_includes/reserved_usernames_including.txt");
pub mod cookies;
pub mod default_routes;
pub mod endpoints;
pub mod utils;
@@ -2,5 +2,4 @@ pub mod default_avatar_color_from_username;
pub mod default_avatar_from_username;
pub mod email_to_gravatar;
pub mod session_checker;
pub mod session_cookie_manager;
pub mod user_session_extended;
@@ -15,7 +15,7 @@ use mysql_queries::queries::users::user_subscriptions::list_active_user_subscrip
use redis_caching::redis_ttl_cache::{RedisTtlCache, RedisTtlCacheConnection};
use redis_common::redis_cache_keys::RedisCacheKeys;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::cookies::session_cookie_manager::SessionCookieManager;
use crate::utils::user_session_extended::{UserSessionExtended, UserSessionPreferences, UserSessionPremiumPlanInfo, UserSessionRoleAndPermissions, UserSessionSubscriptionPlan, UserSessionUserDetails};
#[derive(Clone)]
@@ -64,8 +64,8 @@ use redis_caching::redis_ttl_cache::RedisTtlCache;
use reusable_types::server_environment::ServerEnvironment;
use twitch_common::twitch_secrets::TwitchSecrets;
use url_config::third_party_url_redirector::ThirdPartyUrlRedirector;
use users_component::cookies::session_cookie_manager::SessionCookieManager;
use users_component::utils::session_checker::SessionChecker;
use users_component::utils::session_cookie_manager::SessionCookieManager;
use crate::billing::internal_product_to_stripe_lookup_impl::InternalProductToStripeLookupImpl;
use crate::billing::internal_session_cache_purge_impl::InternalSessionCachePurgeImpl;
@@ -15,8 +15,8 @@ use mysql_queries::queries::w2l::w2l_templates::list_w2l_templates::W2lTemplateR
use redis_caching::redis_ttl_cache::RedisTtlCache;
use reusable_types::server_environment::ServerEnvironment;
use url_config::third_party_url_redirector::ThirdPartyUrlRedirector;
use users_component::cookies::session_cookie_manager::SessionCookieManager;
use users_component::utils::session_checker::SessionChecker;
use users_component::utils::session_cookie_manager::SessionCookieManager;
use crate::http_server::endpoints::categories::tts::list_fully_computed_assigned_tts_categories::list_fully_computed_assigned_tts_categories::ModelTokensByCategoryToken;
use crate::http_server::endpoints::leaderboard::get_leaderboard::LeaderboardInfo;