From 8559764d318bd828f19b19feeb16c9fd88a6efda Mon Sep 17 00:00:00 2001 From: Lewis Date: Sun, 12 Jul 2026 08:03:14 +0200 Subject: [PATCH] auth: newtype jti claims, sessions, & store Lewis: May this revision serve well! --- .../src/server/passkey_account.rs | 4 +- crates/tranquil-auth/src/token.rs | 23 +++++----- crates/tranquil-auth/src/types.rs | 5 +- crates/tranquil-auth/src/verify.rs | 5 +- crates/tranquil-db-traits/src/session.rs | 34 +++++++------- crates/tranquil-db-traits/src/user.rs | 4 +- crates/tranquil-db/src/postgres/session.rs | 46 +++++++++---------- crates/tranquil-db/src/postgres/user.rs | 11 +++-- crates/tranquil-oauth/src/dpop.rs | 4 +- crates/tranquil-oauth/src/types.rs | 12 ----- crates/tranquil-pds/src/auth/extractor.rs | 2 +- crates/tranquil-pds/src/auth/service.rs | 4 +- crates/tranquil-store/src/metastore/client.rs | 32 +++++++------ .../tranquil-store/src/metastore/handler.rs | 8 ++-- crates/tranquil-store/src/metastore/mod.rs | 10 ++-- .../src/metastore/session_ops.rs | 18 ++++---- .../tranquil-store/src/metastore/user_ops.rs | 5 +- 17 files changed, 113 insertions(+), 114 deletions(-) diff --git a/crates/tranquil-api/src/server/passkey_account.rs b/crates/tranquil-api/src/server/passkey_account.rs index b9a958c..0a4a288 100644 --- a/crates/tranquil-api/src/server/passkey_account.rs +++ b/crates/tranquil-api/src/server/passkey_account.rs @@ -14,7 +14,7 @@ use tranquil_pds::auth::NormalizedLoginIdentifier; use tranquil_pds::auth::{ServiceTokenVerifier, generate_app_password, is_service_token}; use tranquil_pds::rate_limit::{AccountCreationLimit, PasswordResetLimit, RateLimited}; use tranquil_pds::state::AppState; -use tranquil_pds::types::{Did, Handle, PlainPassword}; +use tranquil_pds::types::{Did, Handle, Jti, Nsid, PlainPassword}; use tranquil_pds::validation::validate_password; fn generate_setup_token() -> String { @@ -368,7 +368,7 @@ pub async fn create_passkey_account( let access_jwt = if byod_auth.is_some() { match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) { Ok(token_meta) => { - let refresh_jti = uuid::Uuid::new_v4().to_string(); + let refresh_jti = Jti::from(uuid::Uuid::new_v4().to_string()); let refresh_expires = chrono::Utc::now() + chrono::Duration::hours(24); let session_data = tranquil_db_traits::SessionTokenCreate { did: did_typed.clone(), diff --git a/crates/tranquil-auth/src/token.rs b/crates/tranquil-auth/src/token.rs index dbd50aa..360e47d 100644 --- a/crates/tranquil-auth/src/token.rs +++ b/crates/tranquil-auth/src/token.rs @@ -8,6 +8,7 @@ use chrono::{DateTime, Duration, Utc}; use hmac::{Hmac, Mac}; use k256::ecdsa::{Signature, SigningKey, signature::Signer}; use sha2::Sha256; +use tranquil_types::{Did, Jti, Nsid}; type HmacSha256 = Hmac; @@ -83,7 +84,7 @@ pub fn create_access_token_with_jti( scopes: Option<&str>, controller_did: Option<&str>, hostname: Option<&str>, - jti: &str, + jti: &Jti, expires_at: DateTime, ) -> Result { let scope = scopes.unwrap_or(TokenScope::Access.as_str()); @@ -94,7 +95,7 @@ pub fn create_access_token_with_jti( TokenType::Access, key_bytes, expires_at, - jti.to_string(), + jti.clone(), act, hostname, )? @@ -106,7 +107,7 @@ pub fn create_access_token_with_jti( pub fn create_refresh_token_with_jti( did: &str, key_bytes: &[u8], - jti: &str, + jti: &Jti, expires_at: DateTime, ) -> Result { Ok(create_signed_token_pinned( @@ -115,7 +116,7 @@ pub fn create_refresh_token_with_jti( TokenType::Refresh, key_bytes, expires_at, - jti.to_string(), + jti.clone(), None, None, )? @@ -142,8 +143,8 @@ pub fn create_service_token( exp: expiration, iat: Utc::now().timestamp(), scope: None, - lxm: lxm.map(ToOwned::to_owned), - jti: uuid::Uuid::new_v4().to_string(), + lxm: lxm.cloned(), + jti: Jti::new(uuid::Uuid::new_v4().to_string()), act: None, }; @@ -173,7 +174,7 @@ fn create_signed_token_with_act( let expires_at = Utc::now() .checked_add_signed(duration) .expect("valid timestamp"); - let jti = uuid::Uuid::new_v4().to_string(); + let jti = Jti::new(uuid::Uuid::new_v4().to_string()); create_signed_token_pinned(did, scope, typ, key_bytes, expires_at, jti, act, hostname) } @@ -184,7 +185,7 @@ fn create_signed_token_pinned( typ: TokenType, key_bytes: &[u8], expires_at: DateTime, - jti: String, + jti: Jti, act: Option, hostname: Option<&str>, ) -> Result { @@ -294,8 +295,8 @@ pub fn create_service_token_hs256( exp: expiration, iat: Utc::now().timestamp(), scope: None, - lxm: Some(lxm.to_string()), - jti: uuid::Uuid::new_v4().to_string(), + lxm: Some(lxm.clone()), + jti: Jti::new(uuid::Uuid::new_v4().to_string()), act: None, }; @@ -314,7 +315,7 @@ fn create_hs256_token_with_metadata( .expect("valid timestamp"); let expiration = expires_at.timestamp(); - let jti = uuid::Uuid::new_v4().to_string(); + let jti = Jti::new(uuid::Uuid::new_v4().to_string()); let claims = Claims { iss: did.to_owned(), diff --git a/crates/tranquil-auth/src/types.rs b/crates/tranquil-auth/src/types.rs index 0fb4e1b..04cbbed 100644 --- a/crates/tranquil-auth/src/types.rs +++ b/crates/tranquil-auth/src/types.rs @@ -2,6 +2,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize, de, ser}; use std::fmt; use std::str::FromStr; +use tranquil_types::{Did, Jti, Nsid}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TokenType { @@ -217,7 +218,7 @@ pub struct Claims { pub scope: Option, #[serde(skip_serializing_if = "Option::is_none")] pub lxm: Option, - pub jti: String, + pub jti: Jti, #[serde(skip_serializing_if = "Option::is_none")] pub act: Option, } @@ -240,7 +241,7 @@ pub struct TokenData { pub struct TokenWithMetadata { pub token: String, - pub jti: String, + pub jti: Jti, pub expires_at: DateTime, } diff --git a/crates/tranquil-auth/src/verify.rs b/crates/tranquil-auth/src/verify.rs index b004372..0db95ab 100644 --- a/crates/tranquil-auth/src/verify.rs +++ b/crates/tranquil-auth/src/verify.rs @@ -10,6 +10,7 @@ use hmac::{Hmac, Mac}; use k256::ecdsa::{Signature, SigningKey, VerifyingKey, signature::Verifier}; use sha2::Sha256; use subtle::ConstantTimeEq; +use tranquil_types::{Did, Jti}; type HmacSha256 = Hmac; @@ -29,7 +30,7 @@ pub fn get_did_from_token(token: &str) -> Result { Ok(claims.sub.unwrap_or(claims.iss)) } -pub fn get_jti_from_token(token: &str) -> Result { +pub fn get_jti_from_token(token: &str) -> Result { let parts: Vec<&str> = token.split('.').collect(); if parts.len() != 3 { return Err(TokenDecodeError::InvalidFormat); @@ -45,7 +46,7 @@ pub fn get_jti_from_token(token: &str) -> Result { claims .get("jti") .and_then(|j| j.as_str()) - .map(|s| s.to_string()) + .map(Jti::new) .ok_or(TokenDecodeError::MissingClaim) } diff --git a/crates/tranquil-db-traits/src/session.rs b/crates/tranquil-db-traits/src/session.rs index 1b5eb88..dac4702 100644 --- a/crates/tranquil-db-traits/src/session.rs +++ b/crates/tranquil-db-traits/src/session.rs @@ -84,8 +84,8 @@ impl std::fmt::Display for SessionId { pub struct SessionToken { pub id: SessionId, pub did: Did, - pub access_jti: String, - pub refresh_jti: String, + pub access_jti: Jti, + pub refresh_jti: Jti, pub access_expires_at: DateTime, pub refresh_expires_at: DateTime, pub login_type: LoginType, @@ -100,8 +100,8 @@ pub struct SessionToken { #[derive(Debug, Clone)] pub struct SessionTokenCreate { pub did: Did, - pub access_jti: String, - pub refresh_jti: String, + pub access_jti: Jti, + pub refresh_jti: Jti, pub access_expires_at: DateTime, pub refresh_expires_at: DateTime, pub login_type: LoginType, @@ -124,7 +124,7 @@ pub struct SessionForRefresh { #[derive(Debug, Clone)] pub struct SessionListItem { pub id: SessionId, - pub access_jti: String, + pub access_jti: Jti, pub created_at: DateTime, pub refresh_expires_at: DateTime, } @@ -175,8 +175,8 @@ pub struct RefreshGraceReplay { pub did: Did, pub scope: Option, pub controller_did: Option, - pub access_jti: String, - pub refresh_jti: String, + pub access_jti: Jti, + pub refresh_jti: Jti, pub access_expires_at: DateTime, pub refresh_expires_at: DateTime, pub key_bytes: Vec, @@ -205,10 +205,10 @@ pub enum RefreshGraceLookup { #[derive(Debug, Clone)] pub struct SessionRefreshData { pub did: Did, - pub old_refresh_jti: String, + pub old_refresh_jti: Jti, pub session_id: SessionId, - pub new_access_jti: String, - pub new_refresh_jti: String, + pub new_access_jti: Jti, + pub new_refresh_jti: Jti, pub new_access_expires_at: DateTime, pub new_refresh_expires_at: DateTime, } @@ -219,17 +219,17 @@ pub trait SessionRepository: Send + Sync { async fn get_session_by_access_jti( &self, - access_jti: &str, + access_jti: &Jti, ) -> Result, DbError>; async fn get_session_for_refresh( &self, - refresh_jti: &str, + refresh_jti: &Jti, ) -> Result, DbError>; async fn delete_session_by_access_jti( &self, - access_jti: &str, + access_jti: &Jti, did: &Did, ) -> Result; @@ -244,7 +244,7 @@ pub trait SessionRepository: Send + Sync { async fn delete_sessions_by_did_except_jti( &self, did: &Did, - except_jti: &str, + except_jti: &Jti, ) -> Result; async fn list_sessions_by_did(&self, did: &Did) -> Result, DbError>; @@ -253,7 +253,7 @@ pub trait SessionRepository: Send + Sync { &self, session_id: SessionId, did: &Did, - ) -> Result, DbError>; + ) -> Result, DbError>; async fn delete_sessions_by_app_password( &self, @@ -265,9 +265,9 @@ pub trait SessionRepository: Send + Sync { &self, did: &Did, app_password_name: &str, - ) -> Result, DbError>; + ) -> Result, DbError>; - async fn lookup_refresh_grace(&self, refresh_jti: &str) -> Result; + async fn lookup_refresh_grace(&self, refresh_jti: &Jti) -> Result; async fn list_app_passwords(&self, user_id: Uuid) -> Result, DbError>; diff --git a/crates/tranquil-db-traits/src/user.rs b/crates/tranquil-db-traits/src/user.rs index d93161d..dcf3abd 100644 --- a/crates/tranquil-db-traits/src/user.rs +++ b/crates/tranquil-db-traits/src/user.rs @@ -119,7 +119,7 @@ pub trait UserRepository: Send + Sync { async fn get_session_access_expiry( &self, did: &Did, - access_jti: &str, + access_jti: &Jti, ) -> Result>, DbError>; async fn get_oauth_token_with_user( @@ -926,7 +926,7 @@ pub struct UserIdAndPasswordHash { #[derive(Debug, Clone)] pub struct PasswordResetResult { pub did: Did, - pub session_jtis: Vec, + pub session_jtis: Vec, } #[derive(Debug, Clone)] diff --git a/crates/tranquil-db/src/postgres/session.rs b/crates/tranquil-db/src/postgres/session.rs index fe31087..2560131 100644 --- a/crates/tranquil-db/src/postgres/session.rs +++ b/crates/tranquil-db/src/postgres/session.rs @@ -34,8 +34,8 @@ impl SessionRepository for PostgresSessionRepository { RETURNING id "#, data.did.as_str(), - data.access_jti, - data.refresh_jti, + data.access_jti.as_str(), + data.refresh_jti.as_str(), data.access_expires_at, data.refresh_expires_at, data.login_type.is_legacy(), @@ -53,7 +53,7 @@ impl SessionRepository for PostgresSessionRepository { async fn get_session_by_access_jti( &self, - access_jti: &str, + access_jti: &Jti, ) -> Result, DbError> { let row = sqlx::query!( r#" @@ -63,7 +63,7 @@ impl SessionRepository for PostgresSessionRepository { FROM session_tokens WHERE access_jti = $1 "#, - access_jti + access_jti.as_str() ) .fetch_optional(&self.pool) .await @@ -72,8 +72,8 @@ impl SessionRepository for PostgresSessionRepository { Ok(row.map(|r| SessionToken { id: SessionId::new(r.id), did: Did::from(r.did), - access_jti: r.access_jti, - refresh_jti: r.refresh_jti, + access_jti: Jti::from(r.access_jti), + refresh_jti: Jti::from(r.refresh_jti), access_expires_at: r.access_expires_at, refresh_expires_at: r.refresh_expires_at, login_type: LoginType::from_legacy_flag(r.legacy_login), @@ -88,7 +88,7 @@ impl SessionRepository for PostgresSessionRepository { async fn get_session_for_refresh( &self, - refresh_jti: &str, + refresh_jti: &Jti, ) -> Result, DbError> { let row = sqlx::query!( r#" @@ -98,7 +98,7 @@ impl SessionRepository for PostgresSessionRepository { JOIN user_keys k ON u.id = k.user_id WHERE st.refresh_jti = $1 AND st.refresh_expires_at > NOW() "#, - refresh_jti + refresh_jti.as_str() ) .fetch_optional(&self.pool) .await @@ -116,12 +116,12 @@ impl SessionRepository for PostgresSessionRepository { async fn delete_session_by_access_jti( &self, - access_jti: &str, + access_jti: &Jti, did: &Did, ) -> Result { let result = sqlx::query!( "DELETE FROM session_tokens WHERE access_jti = $1 AND did = $2", - access_jti, + access_jti.as_str(), did.as_str() ) .execute(&self.pool) @@ -160,7 +160,7 @@ impl SessionRepository for PostgresSessionRepository { async fn delete_sessions_by_did_except_jti( &self, did: &Did, - except_jti: &str, + except_jti: &Jti, ) -> Result { let result = sqlx::query!( "DELETE FROM session_tokens WHERE did = $1 AND access_jti != $2", @@ -192,7 +192,7 @@ impl SessionRepository for PostgresSessionRepository { .into_iter() .map(|r| SessionListItem { id: SessionId::new(r.id), - access_jti: r.access_jti, + access_jti: Jti::from(r.access_jti), created_at: r.created_at, refresh_expires_at: r.refresh_expires_at, }) @@ -203,7 +203,7 @@ impl SessionRepository for PostgresSessionRepository { &self, session_id: SessionId, did: &Did, - ) -> Result, DbError> { + ) -> Result, DbError> { let row = sqlx::query_scalar!( "SELECT access_jti FROM session_tokens WHERE id = $1 AND did = $2", session_id.as_i32(), @@ -213,7 +213,7 @@ impl SessionRepository for PostgresSessionRepository { .await .map_err(map_sqlx_error)?; - Ok(row) + Ok(row.map(Jti::from)) } async fn delete_sessions_by_app_password( @@ -237,7 +237,7 @@ impl SessionRepository for PostgresSessionRepository { &self, did: &Did, app_password_name: &str, - ) -> Result, DbError> { + ) -> Result, DbError> { let rows = sqlx::query_scalar!( "SELECT access_jti FROM session_tokens WHERE did = $1 AND app_password_name = $2", did.as_str(), @@ -247,10 +247,10 @@ impl SessionRepository for PostgresSessionRepository { .await .map_err(map_sqlx_error)?; - Ok(rows) + Ok(rows.into_iter().map(Jti::from).collect()) } - async fn lookup_refresh_grace(&self, refresh_jti: &str) -> Result { + async fn lookup_refresh_grace(&self, refresh_jti: &Jti) -> Result { let row = sqlx::query!( r#" SELECT u.used_at, st.id AS session_id, st.did, st.scope, st.controller_did, @@ -262,7 +262,7 @@ impl SessionRepository for PostgresSessionRepository { JOIN user_keys k ON us.id = k.user_id WHERE u.refresh_jti = $1 "#, - refresh_jti + refresh_jti.as_str() ) .fetch_optional(&self.pool) .await @@ -281,8 +281,8 @@ impl SessionRepository for PostgresSessionRepository { did: Did::from(r.did), scope: r.scope, controller_did: r.controller_did.map(Did::from), - access_jti: r.access_jti, - refresh_jti: r.refresh_jti, + access_jti: Jti::from(r.access_jti), + refresh_jti: Jti::from(r.refresh_jti), access_expires_at: r.access_expires_at, refresh_expires_at: r.refresh_expires_at, key_bytes: r.key_bytes, @@ -524,7 +524,7 @@ impl SessionRepository for PostgresSessionRepository { // rest see `rows_affected == 0`. let claimed = sqlx::query!( "INSERT INTO used_refresh_tokens (refresh_jti, session_id) VALUES ($1, $2) ON CONFLICT (refresh_jti) DO NOTHING", - data.old_refresh_jti, + data.old_refresh_jti.as_str(), data.session_id.as_i32() ) .execute(&mut *tx) @@ -567,8 +567,8 @@ impl SessionRepository for PostgresSessionRepository { refresh_expires_at = $4, updated_at = NOW() WHERE id = $5 "#, - data.new_access_jti, - data.new_refresh_jti, + data.new_access_jti.as_str(), + data.new_refresh_jti.as_str(), data.new_access_expires_at, data.new_refresh_expires_at, data.session_id.as_i32() diff --git a/crates/tranquil-db/src/postgres/user.rs b/crates/tranquil-db/src/postgres/user.rs index 1841201..fe4e29e 100644 --- a/crates/tranquil-db/src/postgres/user.rs +++ b/crates/tranquil-db/src/postgres/user.rs @@ -176,12 +176,12 @@ impl UserRepository for PostgresUserRepository { async fn get_session_access_expiry( &self, did: &Did, - access_jti: &str, + access_jti: &Jti, ) -> Result>, DbError> { let row = sqlx::query!( "SELECT access_expires_at FROM session_tokens WHERE did = $1 AND access_jti = $2", did.as_str(), - access_jti + access_jti.as_str() ) .fetch_optional(&self.pool) .await @@ -1874,13 +1874,16 @@ impl UserRepository for PostgresUserRepository { .await .map_err(map_sqlx_error)?; - let session_jtis: Vec = sqlx::query_scalar!( + let session_jtis: Vec = sqlx::query_scalar!( "SELECT access_jti FROM session_tokens WHERE did = $1", user_did ) .fetch_all(&mut *tx) .await - .map_err(map_sqlx_error)?; + .map_err(map_sqlx_error)? + .into_iter() + .map(Jti::from) + .collect(); sqlx::query!("DELETE FROM session_tokens WHERE did = $1", user_did) .execute(&mut *tx) diff --git a/crates/tranquil-oauth/src/dpop.rs b/crates/tranquil-oauth/src/dpop.rs index d80a489..12c2b3a 100644 --- a/crates/tranquil-oauth/src/dpop.rs +++ b/crates/tranquil-oauth/src/dpop.rs @@ -36,7 +36,7 @@ pub struct DPoPJwk { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DPoPProofPayload { - pub jti: String, + pub jti: DPoPProofId, pub htm: String, pub htu: String, pub iat: i64, @@ -181,7 +181,7 @@ impl DPoPVerifier { let jkt = compute_jwk_thumbprint(&header.jwk)?; Ok(DPoPVerifyResult { jkt: jkt.into(), - jti: payload.jti.clone().into(), + jti: payload.jti.clone(), }) } } diff --git a/crates/tranquil-oauth/src/types.rs b/crates/tranquil-oauth/src/types.rs index f8c1f3e..a17e767 100644 --- a/crates/tranquil-oauth/src/types.rs +++ b/crates/tranquil-oauth/src/types.rs @@ -271,18 +271,6 @@ pub struct TokenRequest { pub client_secret: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DPoPClaims { - pub jti: String, - pub htm: String, - pub htu: String, - pub iat: i64, - #[serde(skip_serializing_if = "Option::is_none")] - pub ath: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub nonce: Option, -} - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct JwkPublicKey { pub kty: String, diff --git a/crates/tranquil-pds/src/auth/extractor.rs b/crates/tranquil-pds/src/auth/extractor.rs index 4beefb9..4308649 100644 --- a/crates/tranquil-pds/src/auth/extractor.rs +++ b/crates/tranquil-pds/src/auth/extractor.rs @@ -105,7 +105,7 @@ pub fn extract_auth_token_from_header(auth_header: Option<&str>) -> Option Option { +pub fn extract_jti_from_headers(headers: &axum::http::HeaderMap) -> Option { let auth_header = headers.get(AUTHORIZATION)?.to_str().ok()?; let token = extract_bearer_token_from_header(Some(auth_header))?; tranquil_auth::get_jti_from_token(&token).ok() diff --git a/crates/tranquil-pds/src/auth/service.rs b/crates/tranquil-pds/src/auth/service.rs index 2771099..11cd8f1 100644 --- a/crates/tranquil-pds/src/auth/service.rs +++ b/crates/tranquil-pds/src/auth/service.rs @@ -6,7 +6,7 @@ use reqwest::Client; use serde::{Deserialize, Serialize}; use std::time::Duration; use tracing::debug; -use tranquil_types::Did; +use tranquil_types::{Did, Jti, Nsid}; #[derive(Debug, thiserror::Error)] pub enum ServiceTokenError { @@ -122,7 +122,7 @@ pub struct ServiceTokenClaims { #[serde(skip_serializing_if = "Option::is_none")] pub lxm: Option, #[serde(default)] - pub jti: Option, + pub jti: Option, } impl ServiceTokenClaims { diff --git a/crates/tranquil-store/src/metastore/client.rs b/crates/tranquil-store/src/metastore/client.rs index 6c31730..ef4c93f 100644 --- a/crates/tranquil-store/src/metastore/client.rs +++ b/crates/tranquil-store/src/metastore/client.rs @@ -1437,12 +1437,12 @@ impl tranquil_db_traits::SessionRepository for Metastore async fn get_session_by_access_jti( &self, - access_jti: &str, + access_jti: &Jti, ) -> Result, DbError> { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::Session( SessionRequest::GetSessionByAccessJti { - access_jti: access_jti.to_owned(), + access_jti: access_jti.to_string(), tx, }, ))?; @@ -1451,12 +1451,12 @@ impl tranquil_db_traits::SessionRepository for Metastore async fn get_session_for_refresh( &self, - refresh_jti: &str, + refresh_jti: &Jti, ) -> Result, DbError> { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::Session( SessionRequest::GetSessionForRefresh { - refresh_jti: refresh_jti.to_owned(), + refresh_jti: refresh_jti.to_string(), tx, }, ))?; @@ -1465,13 +1465,13 @@ impl tranquil_db_traits::SessionRepository for Metastore async fn delete_session_by_access_jti( &self, - access_jti: &str, + access_jti: &Jti, did: &Did, ) -> Result { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::Session( SessionRequest::DeleteSessionByAccessJti { - access_jti: access_jti.to_owned(), + access_jti: access_jti.to_string(), did: did.clone(), tx, }, @@ -1509,7 +1509,7 @@ impl tranquil_db_traits::SessionRepository for Metastore async fn delete_sessions_by_did_except_jti( &self, did: &Did, - except_jti: &str, + except_jti: &Jti, ) -> Result { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::Session( @@ -1540,7 +1540,7 @@ impl tranquil_db_traits::SessionRepository for Metastore &self, session_id: tranquil_db_traits::SessionId, did: &Did, - ) -> Result, DbError> { + ) -> Result, DbError> { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::Session( SessionRequest::GetSessionAccessJtiById { @@ -1549,7 +1549,7 @@ impl tranquil_db_traits::SessionRepository for Metastore tx, }, ))?; - recv(rx).await + recv(rx).await.map(|jti: Option| jti.map(Jti::from)) } async fn delete_sessions_by_app_password( @@ -1572,7 +1572,7 @@ impl tranquil_db_traits::SessionRepository for Metastore &self, did: &Did, app_password_name: &str, - ) -> Result, DbError> { + ) -> Result, DbError> { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::Session( SessionRequest::GetSessionJtisByAppPassword { @@ -1581,17 +1581,19 @@ impl tranquil_db_traits::SessionRepository for Metastore tx, }, ))?; - recv(rx).await + recv(rx) + .await + .map(|jtis: Vec| jtis.into_iter().map(Jti::from).collect()) } async fn lookup_refresh_grace( &self, - refresh_jti: &str, + refresh_jti: &Jti, ) -> Result { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::Session( SessionRequest::LookupRefreshGrace { - refresh_jti: refresh_jti.to_owned(), + refresh_jti: refresh_jti.to_string(), tx, }, ))?; @@ -3328,13 +3330,13 @@ impl tranquil_db_traits::UserRepository for MetastoreCli async fn get_session_access_expiry( &self, did: &Did, - access_jti: &str, + access_jti: &Jti, ) -> Result>, DbError> { let (tx, rx) = oneshot::channel(); self.pool.send(MetastoreRequest::User( UserRequest::GetSessionAccessExpiry { did: did.clone(), - access_jti: access_jti.to_owned(), + access_jti: access_jti.to_string(), tx, }, ))?; diff --git a/crates/tranquil-store/src/metastore/handler.rs b/crates/tranquil-store/src/metastore/handler.rs index c22c32d..cda444b 100644 --- a/crates/tranquil-store/src/metastore/handler.rs +++ b/crates/tranquil-store/src/metastore/handler.rs @@ -6094,7 +6094,7 @@ mod tests { use super::*; use crate::eventlog::{EventLog, EventLogConfig}; use crate::metastore::MetastoreConfig; - use tranquil_types::{Did, Handle}; + use tranquil_types::{Did, Handle, InviteCode, Jti}; struct TestHarness { _metastore_dir: tempfile::TempDir, @@ -6308,10 +6308,10 @@ mod tests { let refresh = SessionRequest::RefreshSessionAtomic { data: tranquil_db_traits::SessionRefreshData { did: did.clone(), - old_refresh_jti: "r0".to_string(), + old_refresh_jti: Jti::new("r0"), session_id: sid, - new_access_jti: "a1".to_string(), - new_refresh_jti: "r1".to_string(), + new_access_jti: Jti::new("a1"), + new_refresh_jti: Jti::new("r1"), new_access_expires_at: Utc::now(), new_refresh_expires_at: Utc::now(), }, diff --git a/crates/tranquil-store/src/metastore/mod.rs b/crates/tranquil-store/src/metastore/mod.rs index a6a8e3f..83d397a 100644 --- a/crates/tranquil-store/src/metastore/mod.rs +++ b/crates/tranquil-store/src/metastore/mod.rs @@ -438,8 +438,8 @@ mod tests { let now = chrono::Utc::now(); tranquil_db_traits::SessionTokenCreate { did: tranquil_types::Did::new(did.to_string()).unwrap(), - access_jti: access_jti.to_string(), - refresh_jti: refresh_jti.to_string(), + access_jti: tranquil_types::Jti::new(access_jti), + refresh_jti: tranquil_types::Jti::new(refresh_jti), access_expires_at: now + chrono::Duration::minutes(120), refresh_expires_at: now + chrono::Duration::days(90), login_type: tranquil_db_traits::LoginType::Legacy, @@ -484,10 +484,10 @@ mod tests { let now = chrono::Utc::now(); tranquil_db_traits::SessionRefreshData { did: did.clone(), - old_refresh_jti: old_refresh_jti.to_string(), + old_refresh_jti: tranquil_types::Jti::new(old_refresh_jti), session_id, - new_access_jti: new_access_jti.to_string(), - new_refresh_jti: new_refresh_jti.to_string(), + new_access_jti: tranquil_types::Jti::new(new_access_jti), + new_refresh_jti: tranquil_types::Jti::new(new_refresh_jti), new_access_expires_at: now + chrono::Duration::minutes(120), new_refresh_expires_at: now + chrono::Duration::days(90), } diff --git a/crates/tranquil-store/src/metastore/session_ops.rs b/crates/tranquil-store/src/metastore/session_ops.rs index 6d8dd92..a1e005d 100644 --- a/crates/tranquil-store/src/metastore/session_ops.rs +++ b/crates/tranquil-store/src/metastore/session_ops.rs @@ -85,8 +85,8 @@ impl SessionOps { id: SessionId::new(v.id), did: Did::new(v.did.clone()) .map_err(|_| MetastoreError::CorruptData("invalid session did"))?, - access_jti: v.access_jti.clone(), - refresh_jti: v.refresh_jti.clone(), + access_jti: Jti::from(v.access_jti.clone()), + refresh_jti: Jti::from(v.refresh_jti.clone()), access_expires_at: DateTime::from_timestamp_millis(v.access_expires_at_ms) .unwrap_or_default(), refresh_expires_at: DateTime::from_timestamp_millis(v.refresh_expires_at_ms) @@ -241,8 +241,8 @@ impl SessionOps { .controller_did .clone() .and_then(|d| Did::new(d).ok()), - access_jti: session.access_jti.clone(), - refresh_jti: session.refresh_jti.clone(), + access_jti: Jti::from(session.access_jti.clone()), + refresh_jti: Jti::from(session.refresh_jti.clone()), access_expires_at, refresh_expires_at, key_bytes, @@ -285,8 +285,8 @@ impl SessionOps { let value = SessionTokenValue { id: session_id, did: data.did.to_string(), - access_jti: data.access_jti.clone(), - refresh_jti: data.refresh_jti.clone(), + access_jti: data.access_jti.to_string(), + refresh_jti: data.refresh_jti.to_string(), access_expires_at_ms: data.access_expires_at.timestamp_millis(), refresh_expires_at_ms: data.refresh_expires_at.timestamp_millis(), login_type: login_type_to_u8(data.login_type), @@ -501,7 +501,7 @@ impl SessionOps { .iter() .map(|s| SessionListItem { id: SessionId::new(s.id), - access_jti: s.access_jti.clone(), + access_jti: Jti::from(s.access_jti.clone()), created_at: DateTime::from_timestamp_millis(s.created_at_ms).unwrap_or_default(), refresh_expires_at: DateTime::from_timestamp_millis(s.refresh_expires_at_ms) .unwrap_or_default(), @@ -848,8 +848,8 @@ impl SessionOps { let old_refresh_jti = session.refresh_jti.clone(); let rotated_at_ms = Utc::now().timestamp_millis(); - session.access_jti = data.new_access_jti.clone(); - session.refresh_jti = data.new_refresh_jti.clone(); + session.access_jti = data.new_access_jti.to_string(); + session.refresh_jti = data.new_refresh_jti.to_string(); session.access_expires_at_ms = data.new_access_expires_at.timestamp_millis(); session.refresh_expires_at_ms = data.new_refresh_expires_at.timestamp_millis(); session.updated_at_ms = rotated_at_ms; diff --git a/crates/tranquil-store/src/metastore/user_ops.rs b/crates/tranquil-store/src/metastore/user_ops.rs index 5730633..3b49a43 100644 --- a/crates/tranquil-store/src/metastore/user_ops.rs +++ b/crates/tranquil-store/src/metastore/user_ops.rs @@ -2210,7 +2210,10 @@ impl UserOps { }) .collect(); - let session_jtis: Vec = sessions.iter().map(|(_, _, jti)| jti.clone()).collect(); + let session_jtis: Vec = sessions + .iter() + .map(|(_, _, jti)| Jti::from(jti.clone())) + .collect(); let mut batch = self.db.batch();