diff --git a/src/admin/keys/service.rs b/src/admin/keys/service.rs index abac9b0a5..2cd9cb206 100644 --- a/src/admin/keys/service.rs +++ b/src/admin/keys/service.rs @@ -4,13 +4,11 @@ use std::sync::Arc; use async_trait::async_trait; use chrono::Utc; -use secrecy::{ExposeSecret, SecretString}; use sqlx::{Row, SqlitePool}; use thiserror::Error; use uuid::Uuid; use crate::infra::{ - crypto::{CryptoError, SecretBox}, identity::{ApiKeyHasher, AuthError}, json::{decode_cursor, encode_cursor, Page}, }; @@ -332,9 +330,6 @@ pub enum SqliteClientKeyStoreError { /// API Key 验证错误。 #[error("sqlite client key auth error: {0}")] Auth(#[from] AuthError), - /// API Key 密文处理错误。 - #[error("sqlite client key crypto error: {0}")] - Crypto(#[from] CryptoError), /// 分页游标无效。 #[error("invalid client key pagination cursor")] InvalidCursor, @@ -387,18 +382,12 @@ pub struct CreatedClientApiKey { pub struct SqliteClientKeyStore { pool: SqlitePool, hasher: ApiKeyHasher, - secret_box: SecretBox, } impl SqliteClientKeyStore { /// 构造存储适配器。 pub fn new(pool: SqlitePool, hasher: ApiKeyHasher) -> Self { - let secret_box = hasher.secret_box(); - Self { - pool, - hasher, - secret_box, - } + Self { pool, hasher } } /// 暴露底层连接池,供集成测试和运行时组合层复用。 @@ -414,17 +403,14 @@ impl SqliteClientKeyStore { let generated = self.hasher.generate_client_api_key(name); let id = format!("key_{}", Uuid::new_v4().simple()); let now = Utc::now().to_rfc3339(); - let key_cipher = self - .secret_box - .encrypt(&SecretString::new(generated.key.clone().into()))?; sqlx::query( - "insert into client_api_keys (id, name, label, prefix, key_hash, key_cipher, enabled, created_at, last_used_at) values (?, ?, null, ?, ?, ?, 1, ?, null)", + "insert into client_api_keys (id, name, label, prefix, key_hash, key, enabled, created_at, last_used_at) values (?, ?, null, ?, ?, ?, 1, ?, null)", ) .bind(&id) .bind(name) .bind(&generated.prefix) .bind(&generated.key_hash) - .bind(&key_cipher) + .bind(&generated.key) .bind(&now) .execute(&self.pool) .await?; @@ -452,7 +438,7 @@ impl SqliteClientKeyStore { let (created_at, id) = decode_cursor(&cursor).ok_or(SqliteClientKeyStoreError::InvalidCursor)?; sqlx::query( - "select id, name, label, prefix, key_cipher, enabled, created_at, last_used_at from client_api_keys where created_at < ? or (created_at = ? and id < ?) order by created_at desc, id desc limit ?", + "select id, name, label, prefix, key, enabled, created_at, last_used_at from client_api_keys where created_at < ? or (created_at = ? and id < ?) order by created_at desc, id desc limit ?", ) .bind(&created_at) .bind(created_at) @@ -462,7 +448,7 @@ impl SqliteClientKeyStore { .await? } else { sqlx::query( - "select id, name, label, prefix, key_cipher, enabled, created_at, last_used_at from client_api_keys order by created_at desc, id desc limit ?", + "select id, name, label, prefix, key, enabled, created_at, last_used_at from client_api_keys order by created_at desc, id desc limit ?", ) .bind(fetch_limit) .fetch_all(&self.pool) @@ -493,7 +479,7 @@ impl SqliteClientKeyStore { id: &str, ) -> Result, SqliteClientKeyStoreError> { let row = sqlx::query( - "select id, name, label, prefix, key_cipher, enabled, created_at, last_used_at from client_api_keys where id = ?", + "select id, name, label, prefix, key, enabled, created_at, last_used_at from client_api_keys where id = ?", ) .bind(id) .fetch_optional(&self.pool) @@ -549,14 +535,12 @@ impl SqliteClientKeyStore { &self, row: &sqlx::sqlite::SqliteRow, ) -> Result { - let key_cipher = row.get::("key_cipher"); - let key = self.secret_box.decrypt(&key_cipher)?; Ok(StoredClientApiKey { id: row.get("id"), name: row.get("name"), label: row.get("label"), prefix: row.get("prefix"), - key: key.expose_secret().to_string(), + key: row.get("key"), enabled: row.get::("enabled") != 0, created_at: row.get("created_at"), last_used_at: row.get("last_used_at"), diff --git a/src/infra/identity.rs b/src/infra/identity.rs index 378d655cc..9e852318b 100644 --- a/src/infra/identity.rs +++ b/src/infra/identity.rs @@ -12,8 +12,6 @@ use rand::Rng; use sha2::Sha256; use thiserror::Error; -use crate::infra::crypto::SecretBox; - // --------------------------------------------------------------------------- // AuthError / AuthResult // --------------------------------------------------------------------------- @@ -164,8 +162,4 @@ impl ApiKeyHasher { Err(error) => unreachable!("HMAC accepts any key size: {error}"), } } - - pub(crate) fn secret_box(&self) -> SecretBox { - SecretBox::new(self.pepper) - } } diff --git a/src/infra/schema.sql b/src/infra/schema.sql index 4f171b17c..3ef0dd33b 100644 --- a/src/infra/schema.sql +++ b/src/infra/schema.sql @@ -39,7 +39,7 @@ create table if not exists client_api_keys ( name text not null, prefix text not null, key_hash text not null, - key_cipher text not null, + key text not null, label text, enabled integer not null default 1 check (enabled in (0, 1)), created_at text not null, @@ -58,8 +58,8 @@ create table if not exists accounts ( chatgpt_user_id text, label text, plan_type text, - access_token_cipher text not null, - refresh_token_cipher text, + access_token text not null, + refresh_token text, access_token_expires_at text, next_refresh_at text, status text not null check (status in ('active', 'expired', 'quota_exhausted', 'refreshing', 'disabled', 'banned')), diff --git a/src/runtime/bootstrap.rs b/src/runtime/bootstrap.rs index cbe689b91..7d392bf27 100644 --- a/src/runtime/bootstrap.rs +++ b/src/runtime/bootstrap.rs @@ -41,10 +41,7 @@ async fn build_application( let fingerprint_repository = FingerprintRepository::new(pool.clone()); let stores = BackgroundTaskStores { - accounts: crate::upstream::accounts::store::SqliteAccountStore::new( - pool.clone(), - secret.clone(), - ), + accounts: crate::upstream::accounts::store::SqliteAccountStore::new(pool.clone()), admin_sessions: crate::admin::auth::service::SqliteAdminSessionStore::new(pool.clone()), cookies: crate::upstream::accounts::cookies::SqliteCookieStore::new( pool.clone(), diff --git a/src/upstream/accounts/store.rs b/src/upstream/accounts/store.rs index 042eaf96a..6c69d0d03 100644 --- a/src/upstream/accounts/store.rs +++ b/src/upstream/accounts/store.rs @@ -7,7 +7,6 @@ use serde_json::Value; use sqlx::{sqlite::SqliteRow, QueryBuilder, Row, Sqlite, SqlitePool}; use thiserror::Error; -use crate::infra::crypto::{CryptoError, SecretBox}; use crate::infra::json::{decode_cursor, page_offset, NumberedPage, Page}; use crate::upstream::accounts::model::{Account, AccountStatus, AccountUsageDelta}; @@ -23,8 +22,8 @@ select a.chatgpt_user_id as user_id, a.label, a.plan_type, - a.access_token_cipher, - a.refresh_token_cipher, + a.access_token, + a.refresh_token, a.access_token_expires_at, a.next_refresh_at, a.status, @@ -63,8 +62,8 @@ select a.chatgpt_user_id as user_id, a.label, a.plan_type, - a.access_token_cipher, - a.refresh_token_cipher, + a.access_token, + a.refresh_token, a.access_token_expires_at, a.next_refresh_at, a.status, @@ -103,8 +102,8 @@ insert into accounts ( chatgpt_user_id, label, plan_type, - access_token_cipher, - refresh_token_cipher, + access_token, + refresh_token, access_token_expires_at, status, added_at, @@ -119,8 +118,8 @@ select chatgpt_user_id as user_id, label, plan_type, - access_token_cipher, - refresh_token_cipher, + access_token, + refresh_token, access_token_expires_at, next_refresh_at, status, @@ -137,8 +136,8 @@ select chatgpt_user_id as user_id, label, plan_type, - access_token_cipher, - refresh_token_cipher, + access_token, + refresh_token, access_token_expires_at, next_refresh_at, status, @@ -185,8 +184,8 @@ select chatgpt_user_id as user_id, label, plan_type, - access_token_cipher, - refresh_token_cipher, + access_token, + refresh_token, access_token_expires_at, next_refresh_at, status, @@ -206,8 +205,8 @@ select chatgpt_user_id as user_id, label, plan_type, - access_token_cipher, - refresh_token_cipher, + access_token, + refresh_token, access_token_expires_at, next_refresh_at, status, @@ -472,8 +471,8 @@ set chatgpt_account_id = ?, chatgpt_user_id = ?, plan_type = ?, - access_token_cipher = ?, - refresh_token_cipher = ?, + access_token = ?, + refresh_token = ?, access_token_expires_at = ?, next_refresh_at = ?, status = ?, @@ -487,7 +486,7 @@ set chatgpt_account_id = ?, chatgpt_user_id = ?, plan_type = ?, - access_token_cipher = ?, + access_token = ?, access_token_expires_at = ?, next_refresh_at = ?, status = ?, @@ -509,8 +508,8 @@ set chatgpt_user_id = ?, label = ?, plan_type = ?, - access_token_cipher = ?, - refresh_token_cipher = ?, + access_token = ?, + refresh_token = ?, access_token_expires_at = ?, status = ?, quota_json = coalesce(?, quota_json), @@ -529,7 +528,7 @@ set chatgpt_user_id = ?, label = ?, plan_type = ?, - access_token_cipher = ?, + access_token = ?, access_token_expires_at = ?, status = ?, quota_json = coalesce(?, quota_json), @@ -566,9 +565,6 @@ pub enum SqliteAccountStoreError { /// 数据库错误。 #[error("sqlite account store database error: {0}")] Database(#[from] sqlx::Error), - /// 加解密错误。 - #[error("sqlite account store crypto error: {0}")] - Crypto(#[from] CryptoError), /// 时间格式错误。 #[error("sqlite account store timestamp error: {0}")] Timestamp(#[from] chrono::ParseError), @@ -665,9 +661,9 @@ pub struct StoredAccount { pub label: Option, /// 计划类型。 pub plan_type: Option, - /// access token 密文。 + /// access token 明文。 pub access_token: SecretString, - /// refresh token 密文。 + /// refresh token 明文。 pub refresh_token: Option, /// access token 过期时间。 pub access_token_expires_at: Option>, @@ -917,13 +913,12 @@ pub trait AccountStore: Send + Sync + 'static { #[derive(Clone)] pub struct SqliteAccountStore { pool: SqlitePool, - secret_box: SecretBox, } impl SqliteAccountStore { /// 构造存储。 - pub fn new(pool: SqlitePool, secret_box: SecretBox) -> Self { - Self { pool, secret_box } + pub fn new(pool: SqlitePool) -> Self { + Self { pool } } /// 返回底层连接池。 @@ -934,11 +929,10 @@ impl SqliteAccountStore { /// 插入新账号。 pub async fn insert(&self, account: NewAccount) -> SqliteAccountStoreResult<()> { let now = Utc::now().to_rfc3339(); - let access_token_cipher = self.secret_box.encrypt(&account.access_token)?; - let refresh_token_cipher = account + let refresh_token = account .refresh_token - .map(|token| self.secret_box.encrypt(&token)) - .transpose()?; + .as_ref() + .map(ExposeSecret::expose_secret); sqlx::query(INSERT_ACCOUNT_SQL) .bind(&account.id) .bind(&account.email) @@ -946,8 +940,8 @@ impl SqliteAccountStore { .bind(&account.user_id) .bind(&account.label) .bind(&account.plan_type) - .bind(&access_token_cipher) - .bind(&refresh_token_cipher) + .bind(account.access_token.expose_secret()) + .bind(refresh_token) .bind(account.access_token_expires_at.map(|dt| dt.to_rfc3339())) .bind(status_to_db(account.status)) .bind( @@ -968,9 +962,7 @@ impl SqliteAccountStore { .bind(account_id) .fetch_optional(&self.pool) .await?; - row.as_ref() - .map(|row| stored_account_from_row(self, row)) - .transpose() + row.as_ref().map(stored_account_from_row).transpose() } /// 通过 ChatGPT 身份查找账号。 @@ -985,9 +977,7 @@ impl SqliteAccountStore { .bind(chatgpt_user_id) .fetch_optional(&self.pool) .await?; - row.as_ref() - .map(|row| stored_account_from_row(self, row)) - .transpose() + row.as_ref().map(stored_account_from_row).transpose() } /// 分页列出所有账号(含 token)。 @@ -1010,7 +1000,7 @@ impl SqliteAccountStore { Ok(to_page( rows, limit, - |row| stored_account_from_row(self, row), + stored_account_from_row, ("added_at", "id"), )) } else { @@ -1021,7 +1011,7 @@ impl SqliteAccountStore { Ok(to_page( rows, limit, - |row| stored_account_from_row(self, row), + stored_account_from_row, ("added_at", "id"), )) } @@ -1137,20 +1127,19 @@ impl SqliteAccountStore { update: AccountClaimsUpdate, ) -> SqliteAccountStoreResult { let now = Utc::now().to_rfc3339(); - let access_token_cipher = self.secret_box.encrypt(&update.access_token)?; - let refresh_token_cipher = update + let refresh_token = update .refresh_token - .map(|token| self.secret_box.encrypt(&token)) - .transpose()?; + .as_ref() + .map(ExposeSecret::expose_secret); - let result = if let Some(refresh_cipher) = &refresh_token_cipher { + let result = if let Some(refresh_token) = refresh_token { sqlx::query(UPDATE_ACCOUNT_CLAIMS_WITH_REFRESH_SQL) .bind(&update.email) .bind(&update.account_id) .bind(&update.user_id) .bind(&update.plan_type) - .bind(&access_token_cipher) - .bind(refresh_cipher) + .bind(update.access_token.expose_secret()) + .bind(refresh_token) .bind(update.access_token_expires_at.map(|dt| dt.to_rfc3339())) .bind(update.next_refresh_at.map(|dt| dt.to_rfc3339())) .bind(status_to_db(update.status)) @@ -1164,7 +1153,7 @@ impl SqliteAccountStore { .bind(&update.account_id) .bind(&update.user_id) .bind(&update.plan_type) - .bind(&access_token_cipher) + .bind(update.access_token.expose_secret()) .bind(update.access_token_expires_at.map(|dt| dt.to_rfc3339())) .bind(update.next_refresh_at.map(|dt| dt.to_rfc3339())) .bind(status_to_db(update.status)) @@ -1182,25 +1171,24 @@ impl SqliteAccountStore { update: ImportedAccountUpdate, ) -> SqliteAccountStoreResult { let now = Utc::now().to_rfc3339(); - let access_token_cipher = self.secret_box.encrypt(&update.account.access_token)?; - let refresh_token_cipher = update + let refresh_token = update .account .refresh_token - .map(|token| self.secret_box.encrypt(&token)) - .transpose()?; + .as_ref() + .map(ExposeSecret::expose_secret); let quota_json = update.quota_json; let quota_fetched_at = update.quota_fetched_at.map(|dt| dt.to_rfc3339()); let quota_verify_required = if update.quota_verify_required { 1 } else { 0 }; - let result = if let Some(refresh_cipher) = &refresh_token_cipher { + let result = if let Some(refresh_token) = refresh_token { sqlx::query(UPDATE_IMPORTED_ACCOUNT_WITH_REFRESH_SQL) .bind(&update.account.email) .bind(&update.account.account_id) .bind(&update.account.user_id) .bind(&update.account.label) .bind(&update.account.plan_type) - .bind(&access_token_cipher) - .bind(refresh_cipher) + .bind(update.account.access_token.expose_secret()) + .bind(refresh_token) .bind( update .account @@ -1223,7 +1211,7 @@ impl SqliteAccountStore { .bind(&update.account.user_id) .bind(&update.account.label) .bind(&update.account.plan_type) - .bind(&access_token_cipher) + .bind(update.account.access_token.expose_secret()) .bind( update .account @@ -1660,7 +1648,7 @@ async fn list_pool_accounts(store: &SqliteAccountStore) -> SqliteAccountStoreRes let mut accounts = Vec::with_capacity(rows.len()); for row in rows { - accounts.push(pool_account_from_row(store, &row)?); + accounts.push(pool_account_from_row(&row)?); } Ok(accounts) @@ -1674,22 +1662,10 @@ async fn get_pool_account( .bind(account_id) .fetch_optional(&store.pool) .await?; - row.map(|row| pool_account_from_row(store, &row)) - .transpose() + row.map(|row| pool_account_from_row(&row)).transpose() } -fn pool_account_from_row( - store: &SqliteAccountStore, - row: &SqliteRow, -) -> SqliteAccountStoreResult { - let access_token_cipher = row.get::("access_token_cipher"); - let access_token = store.secret_box.decrypt(&access_token_cipher)?; - let refresh_token = row - .get::, _>("refresh_token_cipher") - .map(|cipher| store.secret_box.decrypt(&cipher)) - .transpose()? - .map(|token| token.expose_secret().to_string()); - +fn pool_account_from_row(row: &SqliteRow) -> SqliteAccountStoreResult { Ok(Account { id: row.get("id"), email: row.get("email"), @@ -1697,8 +1673,8 @@ fn pool_account_from_row( user_id: row.get("user_id"), label: row.get("label"), plan_type: row.get("plan_type"), - access_token: access_token.expose_secret().to_string(), - refresh_token, + access_token: row.get("access_token"), + refresh_token: row.get("refresh_token"), access_token_expires_at: parse_optional_rfc3339( row.get::, _>("access_token_expires_at"), )?, @@ -1758,17 +1734,7 @@ fn pool_account_from_row( }) } -fn stored_account_from_row( - store: &SqliteAccountStore, - row: &SqliteRow, -) -> SqliteAccountStoreResult { - let access_token_cipher = row.get::("access_token_cipher"); - let access_token = store.secret_box.decrypt(&access_token_cipher)?; - let refresh_token = row - .get::, _>("refresh_token_cipher") - .map(|cipher| store.secret_box.decrypt(&cipher)) - .transpose()?; - +fn stored_account_from_row(row: &SqliteRow) -> SqliteAccountStoreResult { Ok(StoredAccount { id: row.get("id"), email: row.get("email"), @@ -1776,8 +1742,10 @@ fn stored_account_from_row( user_id: row.get("user_id"), label: row.get("label"), plan_type: row.get("plan_type"), - access_token, - refresh_token, + access_token: SecretString::new(row.get::("access_token").into()), + refresh_token: row + .get::, _>("refresh_token") + .map(|token| SecretString::new(token.into())), access_token_expires_at: parse_optional_rfc3339( row.get::, _>("access_token_expires_at"), )?, diff --git a/tests/admin/accounts/import_export.rs b/tests/admin/accounts/import_export.rs index 8a6335175..7db3ab723 100644 --- a/tests/admin/accounts/import_export.rs +++ b/tests/admin/accounts/import_export.rs @@ -12,9 +12,8 @@ async fn admin_accounts_export_should_return_native_account_tokens() { let pool = connect_sqlite(&url).await.unwrap(); seed_admin_session(&pool, "session_1").await; let secret_box = SecretBox::new([77u8; 32]); - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_export".to_string(), email: Some("export@example.com".to_string()), @@ -33,7 +32,7 @@ async fn admin_accounts_export_should_return_native_account_tokens() { let config = test_config(url); let hasher = ApiKeyHasher::new([78u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box.clone()), fingerprints: FingerprintRepository::new(pool.clone()), @@ -91,7 +90,7 @@ async fn admin_accounts_import_should_store_native_account_tokens() { let config = test_config(url); let hasher = ApiKeyHasher::new([80u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box.clone()), fingerprints: FingerprintRepository::new(pool.clone()), @@ -134,7 +133,7 @@ async fn admin_accounts_import_should_store_native_account_tokens() { .unwrap(); let status = response.status(); let body = response_json(response).await; - let stored = SqliteAccountStore::new(pool, secret_box) + let stored = SqliteAccountStore::new(pool) .get("acct_import") .await .unwrap() @@ -191,7 +190,7 @@ async fn admin_accounts_import_should_fetch_wham_usage_for_current_openai_token_ }))) .mount(&server) .await; - let (app, _state, pool, _dir, secret_box) = admin_accounts_test_app_with_api_base_url( + let (app, _state, pool, _dir, _) = admin_accounts_test_app_with_api_base_url( "admin-accounts-import-current-openai-token.sqlite", 118, format!("{}/backend-api", server.uri()), @@ -240,7 +239,7 @@ async fn admin_accounts_import_should_fetch_wham_usage_for_current_openai_token_ .unwrap(); let status = response.status(); let body = response_json(response).await; - let stored = SqliteAccountStore::new(pool, secret_box) + let stored = SqliteAccountStore::new(pool) .get("a18bcfa9ae932857") .await .unwrap() @@ -296,7 +295,7 @@ async fn admin_accounts_import_should_complete_chatgpt_account_id_from_refresh_t }))) .mount(&server) .await; - let (app, _state, pool, _dir, secret_box) = + let (app, _state, pool, _dir, _) = admin_accounts_test_app_with_api_base_url_and_oauth_token_endpoint( "admin-accounts-import-rt-complete.sqlite", 125, @@ -325,7 +324,7 @@ async fn admin_accounts_import_should_complete_chatgpt_account_id_from_refresh_t .await .unwrap(); let body = response_json(response).await; - let stored = SqliteAccountStore::new(pool, secret_box) + let stored = SqliteAccountStore::new(pool) .list_metadata_page(1, 10, None) .await .unwrap() @@ -388,7 +387,7 @@ async fn admin_accounts_import_should_fetch_usage_to_complete_missing_plan_and_q config.api.base_url = format!("{}/backend-api", server.uri()); let hasher = ApiKeyHasher::new([121u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box.clone()), fingerprints: FingerprintRepository::new(pool.clone()), @@ -441,7 +440,7 @@ async fn admin_accounts_import_should_fetch_usage_to_complete_missing_plan_and_q .unwrap(); let status = response.status(); let body = response_json(response).await; - let stored = SqliteAccountStore::new(pool.clone(), secret_box) + let stored = SqliteAccountStore::new(pool.clone()) .get("acct_import_supplement") .await .unwrap() @@ -500,7 +499,7 @@ async fn admin_accounts_import_should_fetch_usage_to_complete_missing_plan_and_q #[tokio::test] async fn admin_accounts_import_should_update_existing_sub2api_account_and_clear_stale_quota_lock() { - let (app, _state, pool, _dir, secret_box) = + let (app, _state, pool, _dir, _) = admin_accounts_test_app("admin-accounts-import-update-existing.sqlite", 116).await; let new_access_token = test_jwt( "chatgpt-import-update", @@ -508,9 +507,8 @@ async fn admin_accounts_import_should_update_existing_sub2api_account_and_clear_ Some("new@example.com"), Some("plus"), ); - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_import_update".to_string(), email: Some("old@example.com".to_string()), @@ -580,7 +578,7 @@ async fn admin_accounts_import_should_update_existing_sub2api_account_and_clear_ .unwrap(); let status = response.status(); let body = response_json(response).await; - let stored = SqliteAccountStore::new(pool.clone(), secret_box) + let stored = SqliteAccountStore::new(pool.clone()) .get("acct_import_update") .await .unwrap() @@ -608,7 +606,7 @@ async fn admin_accounts_import_should_require_admin_session_cookie() { let secret_box = SecretBox::new([111u8; 32]); let hasher = ApiKeyHasher::new([112u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box.clone()), fingerprints: FingerprintRepository::new(pool.clone()), @@ -642,7 +640,7 @@ async fn admin_accounts_import_should_require_admin_session_cookie() { } #[tokio::test] -async fn admin_accounts_import_should_store_tokens_encrypted_and_list_sanitized_accounts() { +async fn admin_accounts_import_should_store_plain_tokens_and_list_sanitized_accounts() { let (app, _state, pool, _dir, _secret_box) = admin_accounts_test_app("admin-accounts-import-sanitized.sqlite", 113).await; let access_token = test_jwt( @@ -670,17 +668,14 @@ async fn admin_accounts_import_should_store_tokens_encrypted_and_list_sanitized_ let body = response_json(response).await; assert_eq!(body["data"]["imported"], 1); - let stored: (String, String) = sqlx::query_as( - "select access_token_cipher, refresh_token_cipher from accounts where id = ?", - ) - .bind("acct_imported_sanitized") - .fetch_one(&pool) - .await - .unwrap(); - assert!(stored.0.starts_with("v1:")); - assert!(!stored.0.contains(&access_token)); - assert!(stored.1.starts_with("v1:")); - assert!(!stored.1.contains("refresh-secret")); + let stored: (String, String) = + sqlx::query_as("select access_token, refresh_token from accounts where id = ?") + .bind("acct_imported_sanitized") + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(stored.0, access_token); + assert_eq!(stored.1, "refresh-secret"); let list = app .clone() @@ -705,11 +700,10 @@ async fn admin_accounts_import_should_store_tokens_encrypted_and_list_sanitized_ #[tokio::test] async fn admin_accounts_export_should_return_native_accounts_with_tokens_and_filter_ids() { - let (app, _state, pool, _dir, secret_box) = + let (app, _state, pool, _dir, _) = admin_accounts_test_app("admin-accounts-export-filter.sqlite", 117).await; - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_export_a".to_string(), email: Some("export-a@example.com".to_string()), diff --git a/tests/admin/accounts/lifecycle.rs b/tests/admin/accounts/lifecycle.rs index 74993f1fc..517e94de5 100644 --- a/tests/admin/accounts/lifecycle.rs +++ b/tests/admin/accounts/lifecycle.rs @@ -7,16 +7,16 @@ async fn admin_accounts_lifecycle_should_update_and_delete_accounts() { let url = format!("sqlite://{}", db.display()); let pool = connect_sqlite(&url).await.unwrap(); seed_admin_session(&pool, "session_1").await; - sqlx::query("insert into accounts (id, email, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") + sqlx::query("insert into accounts (id, email, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") .bind("acct_lifecycle").bind("life@example.com").bind("cipher").bind("active") .bind("2026-06-18T00:00:00Z").bind("2026-06-18T00:00:00Z").execute(&pool).await.unwrap(); let config = test_config(url); let secret_box = SecretBox::new([75u8; 32]); let hasher = ApiKeyHasher::new([76u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([75u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -118,11 +118,10 @@ async fn admin_accounts_lifecycle_should_update_and_delete_accounts() { #[tokio::test] async fn admin_account_status_update_should_update_proxy_account_pool() { - let (app, state, pool, _dir, secret_box) = + let (app, state, pool, _dir, _) = admin_accounts_test_app("admin-account-status-runtime-pool.sqlite", 121).await; - seed_encrypted_account( + seed_account( &pool, - secret_box, NewAccount { id: "acct_runtime_status".to_string(), email: Some("runtime-status@example.com".to_string()), @@ -200,15 +199,14 @@ async fn admin_account_refresh_should_not_mark_valid_account_banned_when_refresh }))) .mount(&server) .await; - let (app, _state, pool, _dir, secret_box) = admin_accounts_test_app_with_oauth_token_endpoint( + let (app, _state, pool, _dir, _) = admin_accounts_test_app_with_oauth_token_endpoint( "admin-account-refresh-rt-reused.sqlite", 124, format!("{}/oauth/token", server.uri()), ) .await; - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_refresh_rt_reused".to_string(), email: Some("rt-reused@example.com".to_string()), @@ -247,7 +245,7 @@ async fn admin_account_refresh_should_not_mark_valid_account_banned_when_refresh ) .await .unwrap(); - let stored = SqliteAccountStore::new(pool, secret_box) + let stored = SqliteAccountStore::new(pool) .get("acct_refresh_rt_reused") .await .unwrap() @@ -261,10 +259,10 @@ async fn admin_account_refresh_should_not_mark_valid_account_banned_when_refresh async fn admin_account_update_should_accept_batch_status_payload() { let (app, _state, pool, _dir, _secret_box) = admin_accounts_test_app("admin-account-batch-status-update.sqlite", 122).await; - sqlx::query("insert into accounts (id, email, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") + sqlx::query("insert into accounts (id, email, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") .bind("acct_batch_status_1").bind("batch-1@example.com").bind("cipher").bind("active") .bind("2026-06-18T00:00:00Z").bind("2026-06-18T00:00:00Z").execute(&pool).await.unwrap(); - sqlx::query("insert into accounts (id, email, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") + sqlx::query("insert into accounts (id, email, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") .bind("acct_batch_status_2").bind("batch-2@example.com").bind("cipher").bind("active") .bind("2026-06-18T00:00:00Z").bind("2026-06-18T00:00:00Z").execute(&pool).await.unwrap(); @@ -325,9 +323,8 @@ async fn admin_account_cookies_should_store_encrypted_cookie_header() { let pool = connect_sqlite(&url).await.unwrap(); seed_admin_session(&pool, "session_1").await; let secret_box = SecretBox::new([85u8; 32]); - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_cookies".to_string(), email: Some("cookies@example.com".to_string()), @@ -346,9 +343,9 @@ async fn admin_account_cookies_should_store_encrypted_cookie_header() { let config = test_config(url); let hasher = ApiKeyHasher::new([86u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([85u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -414,7 +411,7 @@ async fn admin_account_cookies_should_store_encrypted_cookie_header() { } #[tokio::test] -async fn admin_account_create_should_derive_claims_and_store_encrypted_tokens() { +async fn admin_account_create_should_derive_claims_and_store_plain_tokens() { let dir = tempfile::tempdir().unwrap(); let db = dir.path().join("admin-account-create.sqlite"); let url = format!("sqlite://{}", db.display()); @@ -430,7 +427,7 @@ async fn admin_account_create_should_derive_claims_and_store_encrypted_tokens() let config = test_config(url); let hasher = ApiKeyHasher::new([92u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box.clone()), fingerprints: FingerprintRepository::new(pool.clone()), @@ -476,9 +473,9 @@ async fn admin_account_manual_create_should_reject_missing_invalid_expired_or_un let secret_box = SecretBox::new([93u8; 32]); let hasher = ApiKeyHasher::new([94u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([93u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -510,7 +507,7 @@ async fn admin_account_manual_create_should_reject_missing_invalid_expired_or_un #[tokio::test] async fn admin_account_manual_create_should_accept_current_openai_token_without_chatgpt_account_id() { - let (app, _state, pool, _dir, secret_box) = + let (app, _state, pool, _dir, _) = admin_accounts_test_app("admin-account-create-current-openai-token.sqlite", 119).await; let token = test_jwt_with_exp( None, @@ -527,7 +524,7 @@ async fn admin_account_manual_create_should_accept_current_openai_token_without_ .await; let status = response.status(); let body = response_json(response).await; - let stored = SqliteAccountStore::new(pool, secret_box) + let stored = SqliteAccountStore::new(pool) .get(body["data"]["id"].as_str().unwrap()) .await .unwrap() diff --git a/tests/admin/accounts/list.rs b/tests/admin/accounts/list.rs index 21079084b..99ca04724 100644 --- a/tests/admin/accounts/list.rs +++ b/tests/admin/accounts/list.rs @@ -2,23 +2,23 @@ use super::*; use codex_proxy_rs::admin::monitoring::events::{EventLevel, EventLog}; #[tokio::test] -async fn admin_accounts_list_should_not_decrypt_account_tokens() { +async fn admin_accounts_list_should_not_expose_account_tokens() { let dir = tempfile::tempdir().unwrap(); let db = dir.path().join("admin-accounts.sqlite"); let url = format!("sqlite://{}", db.display()); let pool = connect_sqlite(&url).await.unwrap(); seed_admin_session(&pool, "session_1").await; - sqlx::query("insert into accounts (id, email, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") - .bind("acct_corrupt").bind("user@example.com").bind("not-a-secret-box-cipher") + sqlx::query("insert into accounts (id, email, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)") + .bind("acct_plain").bind("user@example.com").bind("plain-access-token") .bind("active").bind("2026-06-18T00:00:00Z").bind("2026-06-18T00:00:00Z") .execute(&pool).await.unwrap(); let config = test_config(url); let secret_box = SecretBox::new([63u8; 32]); let hasher = ApiKeyHasher::new([64u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([63u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -48,7 +48,7 @@ async fn admin_accounts_list_should_not_decrypt_account_tokens() { assert_eq!(response.status(), StatusCode::OK); let body = response_json(response).await; - assert_eq!(body["data"]["items"][0]["id"], "acct_corrupt"); + assert_eq!(body["data"]["items"][0]["id"], "acct_plain"); assert_eq!(body["data"]["items"][0]["email"], "user@example.com"); assert_eq!( body["data"]["items"][0]["addedAt"], @@ -221,7 +221,7 @@ async fn admin_accounts_list_should_return_numbered_page_with_search_total() { "2026-06-18T00:00:00Z", ), ] { - sqlx::query("insert into accounts (id, email, label, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?)") + sqlx::query("insert into accounts (id, email, label, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?)") .bind(id) .bind(email) .bind(label) diff --git a/tests/admin/accounts/mod.rs b/tests/admin/accounts/mod.rs index 78e507e19..9f617d07f 100644 --- a/tests/admin/accounts/mod.rs +++ b/tests/admin/accounts/mod.rs @@ -78,8 +78,8 @@ async fn seed_usage_account( cached_tokens: i64, last_used_at: &str, ) { - sqlx::query("insert into accounts (id, email, label, plan_type, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, 'active', ?, ?)") - .bind(id).bind(email).bind(label).bind(plan_type).bind("encrypted") + sqlx::query("insert into accounts (id, email, label, plan_type, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, 'active', ?, ?)") + .bind(id).bind(email).bind(label).bind(plan_type).bind("access-token") .bind("2026-06-11T00:00:00Z").bind("2026-06-11T00:00:00Z") .execute(pool).await.unwrap(); sqlx::query("insert into account_usage (account_id, request_count, empty_response_count, input_tokens, output_tokens, cached_tokens, last_used_at) values (?, ?, ?, ?, ?, ?, ?)") @@ -205,7 +205,7 @@ async fn admin_accounts_test_app_with_overrides( config.auth.oauth_token_endpoint = oauth_token_endpoint; } let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box.clone()), fingerprints: FingerprintRepository::new(pool.clone()), @@ -224,8 +224,8 @@ async fn admin_accounts_test_app_with_overrides( (app, state, pool, dir, secret_box) } -async fn seed_encrypted_account(pool: &SqlitePool, secret_box: SecretBox, account: NewAccount) { - SqliteAccountStore::new(pool.clone(), secret_box) +async fn seed_account(pool: &SqlitePool, account: NewAccount) { + SqliteAccountStore::new(pool.clone()) .insert(account) .await .unwrap(); diff --git a/tests/admin/accounts/quota.rs b/tests/admin/accounts/quota.rs index 1c55c3314..9e03f0b9a 100644 --- a/tests/admin/accounts/quota.rs +++ b/tests/admin/accounts/quota.rs @@ -11,7 +11,7 @@ async fn admin_usage_stats_should_return_page_and_summary() { let url = format!("sqlite://{}", db.display()); let pool = connect_sqlite(&url).await.unwrap(); seed_admin_session(&pool, "session_1").await; - sqlx::query("insert into accounts (id, email, label, plan_type, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?)") + sqlx::query("insert into accounts (id, email, label, plan_type, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?)") .bind("acct_usage").bind("usage@example.com").bind("primary").bind("plus") .bind("cipher").bind("active").bind("2026-06-18T00:00:00Z").bind("2026-06-18T00:00:00Z") .execute(&pool).await.unwrap(); @@ -21,9 +21,9 @@ async fn admin_usage_stats_should_return_page_and_summary() { let secret_box = SecretBox::new([73u8; 32]); let hasher = ApiKeyHasher::new([74u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([73u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -86,9 +86,9 @@ async fn admin_usage_stats_should_require_admin_session_cookie() { let secret_box = SecretBox::new([125u8; 32]); let hasher = ApiKeyHasher::new([126u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([125u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -157,9 +157,9 @@ async fn admin_usage_stats_should_cursor_page_account_usage() { let secret_box = SecretBox::new([127u8; 32]); let hasher = ApiKeyHasher::new([128u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([127u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -235,9 +235,8 @@ async fn admin_account_quota_should_send_usage_cookie() { format!("{}/backend-api", server.uri()), ) .await; - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_quota_cookie".to_string(), email: Some("quota-cookie@example.com".to_string()), @@ -299,9 +298,8 @@ async fn admin_account_health_check_should_send_probe_cookie() { format!("{}/backend-api", server.uri()), ) .await; - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_health_cookie".to_string(), email: Some("health-cookie@example.com".to_string()), @@ -358,9 +356,8 @@ async fn admin_account_quota_warnings_should_return_threshold_matches_from_cache let pool = connect_sqlite(&url).await.unwrap(); seed_admin_session(&pool, "session_1").await; let secret_box = SecretBox::new([89u8; 32]); - seed_encrypted_account( + seed_account( &pool, - secret_box.clone(), NewAccount { id: "acct_warn".to_string(), email: Some("warn@example.com".to_string()), @@ -418,9 +415,9 @@ async fn admin_account_quota_warnings_should_return_threshold_matches_from_cache let config = test_config(url); let hasher = ApiKeyHasher::new([90u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([89u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), diff --git a/tests/admin/accounts/testing.rs b/tests/admin/accounts/testing.rs index e7cf93a5c..7419480e2 100644 --- a/tests/admin/accounts/testing.rs +++ b/tests/admin/accounts/testing.rs @@ -5,10 +5,9 @@ use wiremock::{ Mock, MockServer, ResponseTemplate, }; -async fn seed_test_account(pool: &SqlitePool, secret_box: SecretBox) { - seed_encrypted_account( +async fn seed_test_account(pool: &SqlitePool) { + seed_account( pool, - secret_box, NewAccount { id: "acct_test".to_string(), email: Some("test@example.com".to_string()), @@ -53,13 +52,13 @@ async fn account_test_models_should_return_upstream_models_only() { }))) .mount(&server) .await; - let (app, _state, pool, _dir, secret_box) = admin_accounts_test_app_with_api_base_url( + let (app, _state, pool, _dir, _secret_box) = admin_accounts_test_app_with_api_base_url( "admin-account-test-models.sqlite", 91, format!("{}/backend-api", server.uri()), ) .await; - seed_test_account(&pool, secret_box).await; + seed_test_account(&pool).await; let response = app .oneshot( @@ -125,13 +124,13 @@ async fn account_test_stream_should_translate_upstream_responses_sse() { ) .mount(&server) .await; - let (app, _state, pool, _dir, secret_box) = admin_accounts_test_app_with_api_base_url( + let (app, _state, pool, _dir, _secret_box) = admin_accounts_test_app_with_api_base_url( "admin-account-test-stream.sqlite", 92, format!("{}/backend-api", server.uri()), ) .await; - seed_test_account(&pool, secret_box).await; + seed_test_account(&pool).await; let response = app .oneshot( diff --git a/tests/admin/auth/session.rs b/tests/admin/auth/session.rs index e7390d387..c06c1d9d9 100644 --- a/tests/admin/auth/session.rs +++ b/tests/admin/auth/session.rs @@ -24,10 +24,7 @@ async fn admin_login_should_issue_http_only_session_cookie() { let secret_box = SecretBox::new([121u8; 32]); let hasher = ApiKeyHasher::new([122u8; 32]); let stores = codex_proxy_rs::runtime::services::BackgroundTaskStores { - accounts: codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new( - pool.clone(), - secret_box.clone(), - ), + accounts: codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new(pool.clone()), admin_sessions: codex_proxy_rs::admin::auth::service::SqliteAdminSessionStore::new( pool.clone(), ), @@ -108,10 +105,7 @@ async fn admin_login_should_reject_client_api_key_as_password_or_authorization() let secret_box = SecretBox::new([123u8; 32]); let hasher = ApiKeyHasher::new([124u8; 32]); let stores = codex_proxy_rs::runtime::services::BackgroundTaskStores { - accounts: codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new( - pool.clone(), - secret_box.clone(), - ), + accounts: codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new(pool.clone()), admin_sessions: codex_proxy_rs::admin::auth::service::SqliteAdminSessionStore::new( pool.clone(), ), diff --git a/tests/admin/auth/session_routes.rs b/tests/admin/auth/session_routes.rs index 29d9fb594..70bad880d 100644 --- a/tests/admin/auth/session_routes.rs +++ b/tests/admin/auth/session_routes.rs @@ -42,9 +42,9 @@ async fn admin_login_should_issue_http_only_session_cookie() { let secret_box = SecretBox::new([121u8; 32]); let hasher = ApiKeyHasher::new([122u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([121u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -109,9 +109,9 @@ async fn admin_login_should_reject_client_api_key_as_password_or_authorization() let secret_box = SecretBox::new([123u8; 32]); let hasher = ApiKeyHasher::new([124u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([123u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), diff --git a/tests/admin/keys/mod.rs b/tests/admin/keys/mod.rs index 78a7123ac..3b73a21a6 100644 --- a/tests/admin/keys/mod.rs +++ b/tests/admin/keys/mod.rs @@ -41,7 +41,7 @@ async fn admin_client_key_test_app(db_name: &str) -> (axum::Router, tempfile::Te let secret_box = SecretBox::new([43u8; 32]); let hasher = ApiKeyHasher::new([44u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), diff --git a/tests/admin/keys/store.rs b/tests/admin/keys/store.rs index 32203fa6a..cb82f3b26 100644 --- a/tests/admin/keys/store.rs +++ b/tests/admin/keys/store.rs @@ -11,13 +11,12 @@ async fn client_key_store_should_create_list_disable_enable_and_delete_keys() { assert_eq!(created.name, "cursor"); assert!(created.key.starts_with("sk_")); - let key_cipher: (String,) = - sqlx::query_as("select key_cipher from client_api_keys where id = ?") - .bind(&created.id) - .fetch_one(store.pool()) - .await - .unwrap(); - assert_ne!(key_cipher.0, created.key); + let key: (String,) = sqlx::query_as("select key from client_api_keys where id = ?") + .bind(&created.id) + .fetch_one(store.pool()) + .await + .unwrap(); + assert_eq!(key.0, created.key); assert!(store.verify_and_touch(&created.key).await.unwrap()); diff --git a/tests/admin/models/routes.rs b/tests/admin/models/routes.rs index 1eeb92930..d32c28452 100644 --- a/tests/admin/models/routes.rs +++ b/tests/admin/models/routes.rs @@ -47,9 +47,9 @@ async fn admin_refresh_models_should_require_admin_session() { let secret_box = SecretBox::new([81u8; 32]); let hasher = ApiKeyHasher::new([82u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([81u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -106,7 +106,7 @@ async fn admin_refresh_models_should_store_snapshots_for_distinct_active_plans() let secret_box = SecretBox::new([83u8; 32]); let config = test_config(url, upstream.uri()); let hasher = ApiKeyHasher::new([84u8; 32]); - let store = SqliteAccountStore::new(pool.clone(), secret_box.clone()); + let store = SqliteAccountStore::new(pool.clone()); store .insert(NewAccount { id: "acct_plus".to_string(), diff --git a/tests/admin/monitoring/dashboard.rs b/tests/admin/monitoring/dashboard.rs index 044f1ad08..4d6ac775f 100644 --- a/tests/admin/monitoring/dashboard.rs +++ b/tests/admin/monitoring/dashboard.rs @@ -283,7 +283,7 @@ async fn dashboard_test_app( let secret_box = SecretBox::new([73u8; 32]); let hasher = ApiKeyHasher::new([74u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), @@ -357,7 +357,7 @@ async fn seed_usage_summary_with_last_used( last_used_at: &str, ) { sqlx::query( - "insert into accounts (id, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?)", + "insert into accounts (id, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?)", ) .bind("acct_usage") .bind("cipher") diff --git a/tests/admin/monitoring/logs_routes.rs b/tests/admin/monitoring/logs_routes.rs index 757a46972..f9689e409 100644 --- a/tests/admin/monitoring/logs_routes.rs +++ b/tests/admin/monitoring/logs_routes.rs @@ -261,9 +261,9 @@ async fn admin_logs_test_app( let secret_box = SecretBox::new([73u8; 32]); let hasher = ApiKeyHasher::new([74u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([73u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), diff --git a/tests/admin/settings/routes.rs b/tests/admin/settings/routes.rs index 86b8008b5..aa884108d 100644 --- a/tests/admin/settings/routes.rs +++ b/tests/admin/settings/routes.rs @@ -93,9 +93,9 @@ async fn admin_settings_update_should_persist_retained_fields_to_config_yaml() { let secret_box = SecretBox::new([53u8; 32]); let hasher = ApiKeyHasher::new([54u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([53u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -183,9 +183,9 @@ async fn admin_settings_update_should_reject_unsupported_or_invalid_fields() { let secret_box = SecretBox::new([53u8; 32]); let hasher = ApiKeyHasher::new([54u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([53u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -228,9 +228,9 @@ async fn admin_settings_test_app(db_name: &str) -> (axum::Router, tempfile::Temp let secret_box = SecretBox::new([53u8; 32]); let hasher = ApiKeyHasher::new([54u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([53u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), diff --git a/tests/http/trace_middleware/mod.rs b/tests/http/trace_middleware/mod.rs index 20329587e..17fae292a 100644 --- a/tests/http/trace_middleware/mod.rs +++ b/tests/http/trace_middleware/mod.rs @@ -50,7 +50,7 @@ impl Write for SharedLogWriter { } } -#[tokio::test] +#[tokio::test(flavor = "current_thread")] async fn http_trace_should_include_request_id_and_completion_fields() { let logs = SharedLogBuffer::default(); let subscriber = tracing_subscriber::fmt() diff --git a/tests/http/web_assets/mod.rs b/tests/http/web_assets/mod.rs index 665c49d6b..afeaeab36 100644 --- a/tests/http/web_assets/mod.rs +++ b/tests/http/web_assets/mod.rs @@ -44,10 +44,7 @@ async fn server_router_should_serve_frontend_assets_without_shadowing_api_routes let secret_box = SecretBox::new([61u8; 32]); let hasher = ApiKeyHasher::new([62u8; 32]); let stores = codex_proxy_rs::runtime::services::BackgroundTaskStores { - accounts: codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new( - pool.clone(), - secret_box.clone(), - ), + accounts: codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new(pool.clone()), admin_sessions: codex_proxy_rs::admin::auth::service::SqliteAdminSessionStore::new( pool.clone(), ), diff --git a/tests/infra/storage_schema/mod.rs b/tests/infra/storage_schema/mod.rs index 08718eacf..f16de1844 100644 --- a/tests/infra/storage_schema/mod.rs +++ b/tests/infra/storage_schema/mod.rs @@ -118,13 +118,13 @@ async fn sqlite_schema_should_enforce_unique_chatgpt_identity() { let pool = connect_sqlite(&url).await.unwrap(); sqlx::query( - "insert into accounts (id, chatgpt_account_id, chatgpt_user_id, access_token_cipher, status, added_at, updated_at) values ('acct_a', 'chatgpt-account', 'chatgpt-user', 'cipher', 'active', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", + "insert into accounts (id, chatgpt_account_id, chatgpt_user_id, access_token, status, added_at, updated_at) values ('acct_a', 'chatgpt-account', 'chatgpt-user', 'access-token-a', 'active', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", ) .execute(&pool) .await .unwrap(); let result = sqlx::query( - "insert into accounts (id, chatgpt_account_id, chatgpt_user_id, access_token_cipher, status, added_at, updated_at) values ('acct_b', 'chatgpt-account', 'chatgpt-user', 'cipher', 'active', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", + "insert into accounts (id, chatgpt_account_id, chatgpt_user_id, access_token, status, added_at, updated_at) values ('acct_b', 'chatgpt-account', 'chatgpt-user', 'access-token-b', 'active', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", ) .execute(&pool) .await; @@ -233,7 +233,7 @@ async fn sqlite_schema_should_reject_invalid_account_status() { let pool = connect_sqlite(&url).await.unwrap(); let result = sqlx::query( - "insert into accounts (id, access_token_cipher, status, added_at, updated_at) values ('acct_bad', 'cipher', 'unknown', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", + "insert into accounts (id, access_token, status, added_at, updated_at) values ('acct_bad', 'access-token', 'unknown', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", ) .execute(&pool) .await; @@ -265,17 +265,17 @@ async fn sqlite_schema_should_reject_non_boolean_flags() { let pool = connect_sqlite(&url).await.unwrap(); let api_key_result = sqlx::query( - "insert into client_api_keys (id, name, prefix, key_hash, key_cipher, enabled, created_at) values ('key_bad', 'bad', 'sk_x', 'hash', 'cipher', 2, '2026-06-14T00:00:00Z')", + "insert into client_api_keys (id, name, prefix, key_hash, key, enabled, created_at) values ('key_bad', 'bad', 'sk_x', 'hash', 'sk_test', 2, '2026-06-14T00:00:00Z')", ) .execute(&pool) .await; let account_result = sqlx::query( - "insert into accounts (id, access_token_cipher, status, quota_limit_reached, added_at, updated_at) values ('acct_bad', 'cipher', 'active', 2, '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", + "insert into accounts (id, access_token, status, quota_limit_reached, added_at, updated_at) values ('acct_bad', 'access-token', 'active', 2, '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", ) .execute(&pool) .await; let quota_verify_result = sqlx::query( - "insert into accounts (id, access_token_cipher, status, quota_verify_required, added_at, updated_at) values ('acct_verify_bad', 'cipher', 'active', 2, '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", + "insert into accounts (id, access_token, status, quota_verify_required, added_at, updated_at) values ('acct_verify_bad', 'access-token', 'active', 2, '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", ) .execute(&pool) .await; @@ -293,7 +293,7 @@ async fn sqlite_schema_should_reject_negative_account_usage_counts() { let pool = connect_sqlite(&url).await.unwrap(); sqlx::query( - "insert into accounts (id, access_token_cipher, status, added_at, updated_at) values ('acct_a', 'cipher', 'active', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", + "insert into accounts (id, access_token, status, added_at, updated_at) values ('acct_a', 'access-token', 'active', '2026-06-14T00:00:00Z', '2026-06-14T00:00:00Z')", ) .execute(&pool) .await diff --git a/tests/proxy/dispatch/chat_upstream/mod.rs b/tests/proxy/dispatch/chat_upstream/mod.rs index baad3e783..479f29830 100644 --- a/tests/proxy/dispatch/chat_upstream/mod.rs +++ b/tests/proxy/dispatch/chat_upstream/mod.rs @@ -38,7 +38,6 @@ use codex_proxy_rs::{ upstream::fingerprint::FingerprintRepository, }; use futures::{SinkExt, StreamExt}; -use secrecy::SecretString; use serde_json::{json, Value}; use sqlx::SqlitePool; use tokio::{ @@ -245,7 +244,7 @@ fn test_app_state_with_pool_secret_api_key_hasher_and_installation_id( installation_id: String, ) -> AppState { let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), @@ -278,7 +277,7 @@ async fn test_app_with_account_and_installation_id( let secret_box = SecretBox::new([83u8; 32]); let hasher = ApiKeyHasher::new([84u8; 32]); let api_key = insert_client_api_key(&pool, &hasher).await; - insert_account(&pool, &secret_box).await; + insert_account(&pool).await; let state = test_app_state_with_pool_secret_api_key_hasher_and_installation_id( test_config(url, base_url), pool, @@ -355,7 +354,7 @@ async fn test_app_with_account_pool_config( let secret_box = SecretBox::new([83u8; 32]); let hasher = ApiKeyHasher::new([84u8; 32]); let api_key = insert_client_api_key(&pool, &hasher).await; - insert_account(&pool, &secret_box).await; + insert_account(&pool).await; let mut config = test_config(url, base_url); configure(&mut config); let state = test_app_state_with_pool_secret_api_key_hasher_and_installation_id( @@ -384,7 +383,7 @@ async fn test_app_with_restored_pool_then_disabled_account( let secret_box = SecretBox::new([83u8; 32]); let hasher = ApiKeyHasher::new([84u8; 32]); let api_key = insert_client_api_key(&pool, &hasher).await; - insert_account(&pool, &secret_box).await; + insert_account(&pool).await; let state = test_app_state_with_pool_secret_api_key_hasher_and_installation_id( test_config(url, base_url), pool.clone(), @@ -416,22 +415,8 @@ async fn test_app_with_two_accounts_and_affinity( let secret_box = SecretBox::new([83u8; 32]); let hasher = ApiKeyHasher::new([84u8; 32]); let api_key = insert_client_api_key(&pool, &hasher).await; - insert_named_account( - &pool, - &secret_box, - "acct_a", - "access-default", - "chatgpt-default", - ) - .await; - insert_named_account( - &pool, - &secret_box, - "acct_z", - "access-affinity", - "chatgpt-affinity", - ) - .await; + insert_named_account(&pool, "acct_a", "access-default", "chatgpt-default").await; + insert_named_account(&pool, "acct_z", "access-affinity", "chatgpt-affinity").await; let now = Utc::now(); SqliteSessionAffinityStore::new(pool.clone()) .upsert( @@ -485,17 +470,9 @@ async fn test_app_with_two_accounts_and_affinity_status( let secret_box = SecretBox::new([83u8; 32]); let hasher = ApiKeyHasher::new([84u8; 32]); let api_key = insert_client_api_key(&pool, &hasher).await; + insert_named_account(&pool, "acct_primary", "access-primary", "chatgpt-primary").await; insert_named_account( &pool, - &secret_box, - "acct_primary", - "access-primary", - "chatgpt-primary", - ) - .await; - insert_named_account( - &pool, - &secret_box, "acct_affinity", "access-affinity", "chatgpt-affinity", @@ -557,17 +534,9 @@ async fn test_app_with_two_accounts( let secret_box = SecretBox::new([83u8; 32]); let hasher = ApiKeyHasher::new([84u8; 32]); let api_key = insert_client_api_key(&pool, &hasher).await; + insert_named_account(&pool, "acct_primary", "access-primary", "chatgpt-primary").await; insert_named_account( &pool, - &secret_box, - "acct_primary", - "access-primary", - "chatgpt-primary", - ) - .await; - insert_named_account( - &pool, - &secret_box, "acct_secondary", "access-secondary", "chatgpt-secondary", @@ -639,18 +608,15 @@ async fn update_admin_account_status(app: &axum::Router, account_id: &str, statu assert_eq!(response.status(), StatusCode::OK); } -async fn insert_account(pool: &SqlitePool, secret_box: &SecretBox) { - let access_token = secret_box - .encrypt(&SecretString::new("access-secret".to_string().into())) - .unwrap(); +async fn insert_account(pool: &SqlitePool) { sqlx::query( - "insert into accounts (id, email, chatgpt_account_id, chatgpt_user_id, access_token_cipher, access_token_expires_at, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", + "insert into accounts (id, email, chatgpt_account_id, chatgpt_user_id, access_token, access_token_expires_at, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind("acct_chat") .bind("user@example.com") .bind("chatgpt-account") .bind("chatgpt-user") - .bind(access_token) + .bind("access-secret") .bind("2100-01-01T00:00:00Z") .bind("active") .bind("2026-06-18T00:00:00Z") @@ -662,18 +628,12 @@ async fn insert_account(pool: &SqlitePool, secret_box: &SecretBox) { async fn insert_named_account( pool: &SqlitePool, - secret_box: &SecretBox, id: &str, - access_token_plaintext: &str, + access_token: &str, chatgpt_account_id: &str, ) { - let access_token = secret_box - .encrypt(&SecretString::new( - access_token_plaintext.to_string().into(), - )) - .unwrap(); sqlx::query( - "insert into accounts (id, email, chatgpt_account_id, chatgpt_user_id, access_token_cipher, access_token_expires_at, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", + "insert into accounts (id, email, chatgpt_account_id, chatgpt_user_id, access_token, access_token_expires_at, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(id) .bind(format!("{id}@example.com")) diff --git a/tests/proxy/dispatch/session_affinity.rs b/tests/proxy/dispatch/session_affinity.rs index aa5a31b8f..87dbbae80 100644 --- a/tests/proxy/dispatch/session_affinity.rs +++ b/tests/proxy/dispatch/session_affinity.rs @@ -149,7 +149,7 @@ async fn session_affinity_store_should_forget_record_by_response_id() { async fn insert_account(pool: &sqlx::SqlitePool, id: &str) { sqlx::query( - "insert into accounts (id, access_token_cipher, status, added_at, updated_at) values (?, 'cipher', 'active', '2026-06-18T12:00:00Z', '2026-06-18T12:00:00Z')", + "insert into accounts (id, access_token, status, added_at, updated_at) values (?, 'access-token', 'active', '2026-06-18T12:00:00Z', '2026-06-18T12:00:00Z')", ) .bind(id) .execute(pool) diff --git a/tests/proxy/dispatch/session_affinity_integration.rs b/tests/proxy/dispatch/session_affinity_integration.rs index 44af54c45..55bb48503 100644 --- a/tests/proxy/dispatch/session_affinity_integration.rs +++ b/tests/proxy/dispatch/session_affinity_integration.rs @@ -44,7 +44,7 @@ async fn session_affinity_store_should_restore_active_records() { async fn insert_account(pool: &sqlx::SqlitePool, id: &str) { sqlx::query( - "insert into accounts (id, access_token_cipher, status, added_at, updated_at) values (?, 'cipher', 'active', '2026-06-18T12:00:00Z', '2026-06-18T12:00:00Z')", + "insert into accounts (id, access_token, status, added_at, updated_at) values (?, 'access-token', 'active', '2026-06-18T12:00:00Z', '2026-06-18T12:00:00Z')", ) .bind(id) .execute(pool) diff --git a/tests/proxy/openai/models_routes.rs b/tests/proxy/openai/models_routes.rs index 2e9e62fbc..2a67b516a 100644 --- a/tests/proxy/openai/models_routes.rs +++ b/tests/proxy/openai/models_routes.rs @@ -35,9 +35,9 @@ async fn models_route_should_reject_unknown_client_api_key() { let secret_box = SecretBox::new([91u8; 32]); let hasher = ApiKeyHasher::new([92u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([91u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), @@ -77,9 +77,9 @@ async fn models_route_should_accept_stored_client_api_key() { let config = test_config(url); let secret_box = SecretBox::new([94u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([94u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), diff --git a/tests/proxy/openai/responses_routes.rs b/tests/proxy/openai/responses_routes.rs index e83d0c677..0b2e55fb7 100644 --- a/tests/proxy/openai/responses_routes.rs +++ b/tests/proxy/openai/responses_routes.rs @@ -144,9 +144,9 @@ async fn test_app_with_client_api_key() -> (axum::Router, String, tempfile::Temp let config = test_config(url); let secret_box = SecretBox::new([96u8; 32]); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), - cookies: SqliteCookieStore::new(pool.clone(), SecretBox::new([96u8; 32])), + cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), session_affinity: SqliteSessionAffinityStore::new(pool.clone()), refresh_leases: RefreshLeaseStore::new(pool.clone()), diff --git a/tests/runtime/account_pool_restore/mod.rs b/tests/runtime/account_pool_restore/mod.rs index a3604ef7a..ea7847a6c 100644 --- a/tests/runtime/account_pool_restore/mod.rs +++ b/tests/runtime/account_pool_restore/mod.rs @@ -1,24 +1,19 @@ use std::sync::Arc; use chrono::Utc; -use codex_proxy_rs::{ - infra::crypto::SecretBox, - upstream::accounts::{ - pool::{AccountAcquireRequest, AccountPoolOptions, RuntimeAccountPoolService}, - store::{AccountStore, SqliteAccountStore}, - }, +use codex_proxy_rs::upstream::accounts::{ + pool::{AccountAcquireRequest, AccountPoolOptions, RuntimeAccountPoolService}, + store::{AccountStore, SqliteAccountStore}, }; -use secrecy::SecretString; use crate::support::sqlite::init_test_db; #[tokio::test] async fn runtime_account_pool_should_restore_capacity_and_clear_runtime_state() { let (pool, _dir) = init_test_db("runtime-account-pool.sqlite").await; - let secret_box = SecretBox::new([34u8; 32]); - insert_account(&pool, &secret_box, "acct_pool").await; + insert_account(&pool, "acct_pool").await; - let store = SqliteAccountStore::new(pool, secret_box); + let store = SqliteAccountStore::new(pool); let service = RuntimeAccountPoolService::new( Arc::new(store) as Arc, AccountPoolOptions { @@ -50,18 +45,15 @@ async fn runtime_account_pool_should_restore_capacity_and_clear_runtime_state() assert_eq!(cleared_capacity.total_slots, 0); } -async fn insert_account(pool: &sqlx::SqlitePool, secret_box: &SecretBox, id: &str) { - let access_token = secret_box - .encrypt(&SecretString::new(format!("access-{id}").into())) - .expect("access token should encrypt"); +async fn insert_account(pool: &sqlx::SqlitePool, id: &str) { sqlx::query( - "insert into accounts (id, email, chatgpt_account_id, chatgpt_user_id, access_token_cipher, access_token_expires_at, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", + "insert into accounts (id, email, chatgpt_account_id, chatgpt_user_id, access_token, access_token_expires_at, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(id) .bind(format!("{id}@example.com")) .bind(format!("chatgpt-{id}")) .bind(format!("user-{id}")) - .bind(access_token) + .bind(format!("access-{id}")) .bind("2999-01-01T00:00:00Z") .bind("active") .bind("2026-06-18T00:00:00Z") diff --git a/tests/runtime/services.rs b/tests/runtime/services.rs index acc9c5ef6..bc66e1d3a 100644 --- a/tests/runtime/services.rs +++ b/tests/runtime/services.rs @@ -32,7 +32,7 @@ async fn services_try_new_should_use_configured_tls_transport_builder() { let hasher = ApiKeyHasher::load_or_create(temp_dir.path().join("api-key-pepper.key")) .expect("api key hasher"); let stores = BackgroundTaskStores { - accounts: SqliteAccountStore::new(pool.clone(), secret_box.clone()), + accounts: SqliteAccountStore::new(pool.clone()), admin_sessions: SqliteAdminSessionStore::new(pool.clone()), cookies: SqliteCookieStore::new(pool.clone(), secret_box), fingerprints: FingerprintRepository::new(pool.clone()), diff --git a/tests/runtime/tasks/mod.rs b/tests/runtime/tasks/mod.rs index aec76af9f..82265df46 100644 --- a/tests/runtime/tasks/mod.rs +++ b/tests/runtime/tasks/mod.rs @@ -154,10 +154,10 @@ async fn wait_for_appcast_requests(server: &MockServer, expected_count: usize) { async fn insert_account(pool: &sqlx::SqlitePool, account_id: &str) { let now = Utc::now().to_rfc3339(); sqlx::query( - "insert into accounts (id, access_token_cipher, status, added_at, updated_at) values (?, ?, 'active', ?, ?)", + "insert into accounts (id, access_token, status, added_at, updated_at) values (?, ?, 'active', ?, ?)", ) .bind(account_id) - .bind("cipher") + .bind("access-token") .bind(&now) .bind(&now) .execute(pool) diff --git a/tests/upstream/accounts/account_pool/selection.rs b/tests/upstream/accounts/account_pool/selection.rs index e2e266ff3..39d689437 100644 --- a/tests/upstream/accounts/account_pool/selection.rs +++ b/tests/upstream/accounts/account_pool/selection.rs @@ -83,10 +83,7 @@ fn acquire_should_skip_accounts_with_expired_jwt_when_metadata_is_missing() { async fn runtime_account_pool_should_persist_expired_status_when_jwt_expiry_is_discovered() { let (pool, _dir) = crate::support::sqlite::init_test_db("runtime-pool-expired-status.sqlite").await; - let store = codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new( - pool.clone(), - codex_proxy_rs::infra::crypto::SecretBox::new([91u8; 32]), - ); + let store = codex_proxy_rs::upstream::accounts::store::SqliteAccountStore::new(pool.clone()); store .insert(codex_proxy_rs::upstream::accounts::store::NewAccount { id: "acct_expired".to_string(), diff --git a/tests/upstream/accounts/account_repository.rs b/tests/upstream/accounts/account_repository.rs index b036894c2..dfff21310 100644 --- a/tests/upstream/accounts/account_repository.rs +++ b/tests/upstream/accounts/account_repository.rs @@ -1,6 +1,6 @@ use chrono::{DateTime, Duration, Utc}; use codex_proxy_rs::{ - infra::{crypto::SecretBox, database::connect_sqlite}, + infra::database::connect_sqlite, upstream::accounts::{ model::{AccountStatus, AccountUsageDelta}, store::{AccountStore, NewAccount, SqliteAccountStore}, @@ -9,9 +9,9 @@ use codex_proxy_rs::{ use secrecy::{ExposeSecret, SecretString}; #[tokio::test] -async fn account_repository_should_encrypt_tokens_and_load_decrypted_account() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 4).await; - let repo = SqliteAccountStore::new(pool.clone(), secret_box); +async fn account_repository_should_store_plain_tokens_and_load_secret_wrappers() { + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 4).await; + let repo = SqliteAccountStore::new(pool.clone()); let expires_at = Utc::now() + Duration::hours(1); repo.insert(NewAccount { @@ -30,17 +30,14 @@ async fn account_repository_should_encrypt_tokens_and_load_decrypted_account() { .await .unwrap(); - let stored: (String, String) = sqlx::query_as( - "select access_token_cipher, refresh_token_cipher from accounts where id = ?", - ) - .bind("acct_a") - .fetch_one(&pool) - .await - .unwrap(); - assert!(stored.0.starts_with("v1:")); - assert!(!stored.0.contains("access-secret")); - assert!(stored.1.starts_with("v1:")); - assert!(!stored.1.contains("refresh-secret")); + let stored: (String, String) = + sqlx::query_as("select access_token, refresh_token from accounts where id = ?") + .bind("acct_a") + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(stored.0, "access-secret"); + assert_eq!(stored.1, "refresh-secret"); let loaded = repo.get("acct_a").await.unwrap().unwrap(); assert_eq!(loaded.access_token.expose_secret(), "access-secret"); @@ -54,12 +51,12 @@ async fn account_repository_should_encrypt_tokens_and_load_decrypted_account() { #[tokio::test] async fn account_repository_should_cursor_page_accounts_by_added_at_desc() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 5).await; - let repo = SqliteAccountStore::new(pool.clone(), secret_box.clone()); + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 5).await; + let repo = SqliteAccountStore::new(pool.clone()); - seed_repo_account(&pool, &secret_box, "acct_a", "2026-06-11T00:00:00Z").await; - seed_repo_account(&pool, &secret_box, "acct_b", "2026-06-11T00:01:00Z").await; - seed_repo_account(&pool, &secret_box, "acct_c", "2026-06-11T00:02:00Z").await; + seed_repo_account(&pool, "acct_a", "2026-06-11T00:00:00Z").await; + seed_repo_account(&pool, "acct_b", "2026-06-11T00:01:00Z").await; + seed_repo_account(&pool, "acct_c", "2026-06-11T00:02:00Z").await; let first_page = repo.list(None, 2).await.unwrap(); assert_eq!( @@ -83,15 +80,15 @@ async fn account_repository_should_cursor_page_accounts_by_added_at_desc() { } #[tokio::test] -async fn account_repository_should_list_metadata_without_decrypting_tokens() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 35).await; - let repo = SqliteAccountStore::new(pool.clone(), secret_box); +async fn account_repository_should_list_metadata_without_exposing_tokens() { + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 35).await; + let repo = SqliteAccountStore::new(pool.clone()); sqlx::query( - "insert into accounts (id, email, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)", + "insert into accounts (id, email, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?, ?)", ) - .bind("acct_corrupt") + .bind("acct_plain") .bind("user@example.com") - .bind("not-a-secret-box-cipher") + .bind("plain-access-token") .bind("active") .bind("2026-06-11T00:00:00Z") .bind("2026-06-11T00:00:00Z") @@ -101,15 +98,15 @@ async fn account_repository_should_list_metadata_without_decrypting_tokens() { let page = repo.list_metadata(None, 10).await.unwrap(); - assert_eq!(page.items[0].id, "acct_corrupt"); + assert_eq!(page.items[0].id, "acct_plain"); assert_eq!(page.items[0].email.as_deref(), Some("user@example.com")); assert_eq!(page.items[0].status, AccountStatus::Active); } #[tokio::test] async fn account_repository_should_update_status_and_label_without_rewriting_tokens() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 6).await; - let repo = SqliteAccountStore::new(pool.clone(), secret_box); + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 6).await; + let repo = SqliteAccountStore::new(pool.clone()); repo.insert(NewAccount { id: "acct_a".to_string(), @@ -126,7 +123,7 @@ async fn account_repository_should_update_status_and_label_without_rewriting_tok }) .await .unwrap(); - let before: (String,) = sqlx::query_as("select access_token_cipher from accounts where id = ?") + let before: (String,) = sqlx::query_as("select access_token from accounts where id = ?") .bind("acct_a") .fetch_one(&pool) .await @@ -141,7 +138,7 @@ async fn account_repository_should_update_status_and_label_without_rewriting_tok .await .unwrap()); - let after: (String,) = sqlx::query_as("select access_token_cipher from accounts where id = ?") + let after: (String,) = sqlx::query_as("select access_token from accounts where id = ?") .bind("acct_a") .fetch_one(&pool) .await @@ -160,8 +157,8 @@ async fn account_repository_should_update_status_and_label_without_rewriting_tok #[tokio::test] async fn account_repository_should_accumulate_usage_counters() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 7).await; - let repo = SqliteAccountStore::new(pool, secret_box); + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 7).await; + let repo = SqliteAccountStore::new(pool); seed_new_account(&repo, "acct_a").await; AccountStore::record_usage_delta( @@ -210,12 +207,12 @@ async fn account_repository_should_accumulate_usage_counters() { #[tokio::test] async fn account_repository_should_restore_window_usage_into_runtime_pool_accounts() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 33).await; - let repo = SqliteAccountStore::new(pool.clone(), secret_box.clone()); + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 33).await; + let repo = SqliteAccountStore::new(pool.clone()); let window_started_at = Utc::now() - Duration::minutes(2); let window_reset_at = Utc::now() + Duration::minutes(3); - seed_repo_account(&pool, &secret_box, "acct_a", "2026-06-11T00:00:00Z").await; + seed_repo_account(&pool, "acct_a", "2026-06-11T00:00:00Z").await; sqlx::query( "insert into account_usage (account_id, request_count, image_input_tokens, image_output_tokens, image_request_count, image_request_failed_count, window_request_count, window_input_tokens, window_output_tokens, window_cached_tokens, window_image_input_tokens, window_image_output_tokens, window_image_request_count, window_image_request_failed_count, window_started_at, window_reset_at, limit_window_seconds) values (?, 9, 31, 9, 2, 1, 7, 11, 13, 17, 19, 5, 1, 1, ?, ?, 300)", ) @@ -250,10 +247,10 @@ async fn account_repository_should_restore_window_usage_into_runtime_pool_accoun #[tokio::test] async fn account_repository_should_restore_quota_verify_required_into_runtime_pool_accounts() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 34).await; - let repo = SqliteAccountStore::new(pool.clone(), secret_box.clone()); + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 34).await; + let repo = SqliteAccountStore::new(pool.clone()); - seed_repo_account(&pool, &secret_box, "acct_a", "2026-06-11T00:00:00Z").await; + seed_repo_account(&pool, "acct_a", "2026-06-11T00:00:00Z").await; sqlx::query("update accounts set quota_verify_required = 1 where id = ?") .bind("acct_a") .execute(&pool) @@ -270,9 +267,9 @@ async fn account_repository_should_restore_quota_verify_required_into_runtime_po #[tokio::test] async fn account_repository_should_update_quota_json_and_fetched_at() { - let (pool, secret_box, _dir) = sqlite_account_store_parts("accounts.sqlite", 36).await; - let repo = SqliteAccountStore::new(pool.clone(), secret_box.clone()); - seed_repo_account(&pool, &secret_box, "acct_a", "2026-06-11T00:00:00Z").await; + let (pool, _dir) = sqlite_account_store_parts("accounts.sqlite", 36).await; + let repo = SqliteAccountStore::new(pool.clone()); + seed_repo_account(&pool, "acct_a", "2026-06-11T00:00:00Z").await; let updated = repo .update_quota_json( @@ -306,13 +303,13 @@ async fn account_repository_should_update_quota_json_and_fetched_at() { async fn sqlite_account_store_parts( db_name: &str, - key_byte: u8, -) -> (sqlx::SqlitePool, SecretBox, tempfile::TempDir) { + _key_byte: u8, +) -> (sqlx::SqlitePool, tempfile::TempDir) { let dir = tempfile::tempdir().unwrap(); let db = dir.path().join(db_name); let url = format!("sqlite://{}", db.display()); let pool = connect_sqlite(&url).await.unwrap(); - (pool, SecretBox::new([key_byte; 32]), dir) + (pool, dir) } async fn seed_new_account(repo: &SqliteAccountStore, id: &str) { @@ -333,19 +330,12 @@ async fn seed_new_account(repo: &SqliteAccountStore, id: &str) { .unwrap(); } -async fn seed_repo_account( - pool: &sqlx::SqlitePool, - secret_box: &SecretBox, - id: &str, - added_at: &str, -) { - let token = SecretString::new(format!("access-{id}").into()); - let cipher = secret_box.encrypt(&token).unwrap(); +async fn seed_repo_account(pool: &sqlx::SqlitePool, id: &str, added_at: &str) { sqlx::query( - "insert into accounts (id, access_token_cipher, status, added_at, updated_at) values (?, ?, ?, ?, ?)", + "insert into accounts (id, access_token, status, added_at, updated_at) values (?, ?, ?, ?, ?)", ) .bind(id) - .bind(cipher) + .bind(format!("access-{id}")) .bind("active") .bind(added_at) .bind(added_at) diff --git a/tests/upstream/accounts/cookies.rs b/tests/upstream/accounts/cookies.rs index b740ce944..4af3926a6 100644 --- a/tests/upstream/accounts/cookies.rs +++ b/tests/upstream/accounts/cookies.rs @@ -13,7 +13,7 @@ use sqlx::SqlitePool; #[tokio::test] async fn cookie_store_should_cleanup_expired_cookies() { let (pool, secret_box, _dir) = sqlite_cookie_store_parts("cookies-cleanup.sqlite").await; - seed_account(&pool, &secret_box, "acct_a").await; + seed_account(&pool, "acct_a").await; let store = SqliteCookieStore::new(pool.clone(), secret_box); let now = Utc::now(); @@ -68,8 +68,8 @@ async fn sqlite_cookie_store_parts(name: &str) -> (SqlitePool, SecretBox, tempfi (pool, SecretBox::new([21u8; 32]), dir) } -async fn seed_account(pool: &SqlitePool, secret_box: &SecretBox, id: &str) { - SqliteAccountStore::new(pool.clone(), secret_box.clone()) +async fn seed_account(pool: &SqlitePool, id: &str) { + SqliteAccountStore::new(pool.clone()) .insert(NewAccount { id: id.to_string(), email: Some(format!("{id}@example.com")), diff --git a/tests/upstream/accounts/quota_refresh.rs b/tests/upstream/accounts/quota_refresh.rs index b09cf4d3e..21bf546ab 100644 --- a/tests/upstream/accounts/quota_refresh.rs +++ b/tests/upstream/accounts/quota_refresh.rs @@ -205,7 +205,7 @@ async fn quota_refresh_service_should_send_usage_cookie_when_cookie_store_is_con .await .expect("sqlite pool"); let secret_box = SecretBox::new([14u8; 32]); - let store = SqliteAccountStore::new(pool.clone(), secret_box.clone()); + let store = SqliteAccountStore::new(pool.clone()); let cookies = SqliteCookieStore::new(pool.clone(), secret_box); insert_quota_locked_account(&store, &pool, "acct-quota-cookie", "access-token-cookie").await; cookies @@ -270,7 +270,7 @@ async fn quota_refresh_service_should_fetch_usage_for_quota_locked_accounts_and_ let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool.clone(), SecretBox::new([12u8; 32])); + let store = SqliteAccountStore::new(pool.clone()); store .insert(NewAccount { id: "acct-quota".to_string(), @@ -352,7 +352,7 @@ async fn quota_refresh_service_should_stagger_multiple_locked_account_requests() let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool.clone(), SecretBox::new([13u8; 32])); + let store = SqliteAccountStore::new(pool.clone()); insert_quota_locked_account(&store, &pool, "acct-quota-1", "access-token-1").await; insert_quota_locked_account(&store, &pool, "acct-quota-2", "access-token-2").await; let codex = CodexBackendClient::new( diff --git a/tests/upstream/accounts/refresh_leases.rs b/tests/upstream/accounts/refresh_leases.rs index ca981fb58..155c8f8b7 100644 --- a/tests/upstream/accounts/refresh_leases.rs +++ b/tests/upstream/accounts/refresh_leases.rs @@ -1,6 +1,6 @@ use chrono::{Duration, TimeZone, Utc}; use codex_proxy_rs::{ - infra::{crypto::SecretBox, database::connect_sqlite}, + infra::database::connect_sqlite, upstream::accounts::{ model::AccountStatus, store::{NewAccount, SqliteAccountStore}, @@ -16,8 +16,7 @@ async fn refresh_lease_store_should_acquire_release_and_respect_expiry() { let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let secret_box = SecretBox::new([31u8; 32]); - let accounts = SqliteAccountStore::new(pool.clone(), secret_box); + let accounts = SqliteAccountStore::new(pool.clone()); accounts .insert(NewAccount { id: "acct-lease".to_string(), diff --git a/tests/upstream/accounts/token_refresh/failures.rs b/tests/upstream/accounts/token_refresh/failures.rs index 28e80584b..db9d88de5 100644 --- a/tests/upstream/accounts/token_refresh/failures.rs +++ b/tests/upstream/accounts/token_refresh/failures.rs @@ -7,7 +7,7 @@ async fn token_refresh_task_should_mark_due_account_refreshing_before_refresher_ let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([13u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 9, 0, 0).unwrap(); let new_expires_at = now + Duration::hours(1); let new_access_token = test_jwt(new_expires_at.timestamp()); @@ -75,7 +75,7 @@ async fn token_refresh_task_should_recover_refreshing_account_after_restart() { let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([14u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 10, 0, 0).unwrap(); let old_access_token = test_jwt((now + Duration::hours(1)).timestamp()); let new_access_token = test_jwt((now + Duration::hours(2)).timestamp()); @@ -135,7 +135,7 @@ async fn token_refresh_task_should_not_retry_ambiguous_transport_failure() { let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([15u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 11, 0, 0).unwrap(); let old_access_token = test_jwt((now + Duration::seconds(30)).timestamp()); store @@ -199,7 +199,7 @@ async fn token_refresh_task_should_delay_recovery_after_retry_exhaustion() { let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([19u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 15, 0, 0).unwrap(); let old_access_token = test_jwt((now + Duration::seconds(30)).timestamp()); let new_access_token = test_jwt((now + Duration::hours(1)).timestamp()); @@ -281,7 +281,7 @@ async fn token_refresh_task_should_not_reuse_stale_refresh_token_after_retryable let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([31u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 16, 0, 0).unwrap(); let old_access_token = test_jwt((now + Duration::seconds(30)).timestamp()); let rotated_access_token = test_jwt((now + Duration::hours(1)).timestamp()); @@ -350,7 +350,7 @@ async fn token_refresh_task_should_confirm_invalid_grant_before_expiring_account let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([17u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 13, 0, 0).unwrap(); let old_access_token = test_jwt((now + Duration::seconds(30)).timestamp()); store diff --git a/tests/upstream/accounts/token_refresh/mod.rs b/tests/upstream/accounts/token_refresh/mod.rs index f5cb38849..0db6fa508 100644 --- a/tests/upstream/accounts/token_refresh/mod.rs +++ b/tests/upstream/accounts/token_refresh/mod.rs @@ -10,7 +10,6 @@ use std::{ use async_trait::async_trait; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; use chrono::{Duration, TimeZone, Utc}; -use codex_proxy_rs::infra::crypto::SecretBox; use codex_proxy_rs::infra::database::connect_sqlite; use codex_proxy_rs::upstream::accounts::model::AccountStatus; use codex_proxy_rs::upstream::accounts::store::SqliteAccountStore; diff --git a/tests/upstream/accounts/token_refresh/scheduling.rs b/tests/upstream/accounts/token_refresh/scheduling.rs index a16f5b210..206de21e7 100644 --- a/tests/upstream/accounts/token_refresh/scheduling.rs +++ b/tests/upstream/accounts/token_refresh/scheduling.rs @@ -7,7 +7,7 @@ async fn token_refresh_task_should_skip_account_when_refresh_lease_is_held() { let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool.clone(), SecretBox::new([12u8; 32])); + let store = SqliteAccountStore::new(pool.clone()); let leases = RefreshLeaseStore::new(pool.clone()); let now = Utc.with_ymd_and_hms(2026, 6, 19, 8, 0, 0).unwrap(); let old_access_token = test_jwt((now + Duration::seconds(30)).timestamp()); @@ -74,7 +74,7 @@ async fn token_refresh_task_should_skip_duplicate_in_flight_refresh() { let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([18u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 14, 0, 0).unwrap(); let new_access_token = test_jwt((now + Duration::hours(1)).timestamp()); store @@ -163,7 +163,7 @@ async fn token_refresh_task_should_schedule_future_per_account_timer_without_ref let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([20u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 16, 0, 0).unwrap(); let old_access_token = test_jwt((now + Duration::minutes(10)).timestamp()); store @@ -210,7 +210,7 @@ async fn token_refresh_task_should_fire_per_account_timer_at_refresh_time() { let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([21u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc::now() + Duration::minutes(5); let old_access_token = test_jwt((now + Duration::seconds(1)).timestamp()); let new_access_token = test_jwt((now + Duration::hours(1)).timestamp()); @@ -281,7 +281,7 @@ async fn token_refresh_task_should_reschedule_next_timer_after_scheduled_refresh let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([22u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc::now(); let old_expires_at = now + Duration::milliseconds(50); let new_access_token = test_jwt((now + Duration::hours(1)).timestamp()); diff --git a/tests/upstream/accounts/token_refresh/success.rs b/tests/upstream/accounts/token_refresh/success.rs index 678f8620d..d350f2957 100644 --- a/tests/upstream/accounts/token_refresh/success.rs +++ b/tests/upstream/accounts/token_refresh/success.rs @@ -7,7 +7,7 @@ async fn token_refresh_task_should_persist_refreshed_access_token_and_keep_refre let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([11u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 18, 8, 0, 0).unwrap(); let new_expires_at = now + Duration::hours(1); let new_access_token = test_jwt(new_expires_at.timestamp()); @@ -82,7 +82,7 @@ async fn token_refresh_task_should_persist_success_after_transient_transport_ret let pool = connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let store = SqliteAccountStore::new(pool, SecretBox::new([16u8; 32])); + let store = SqliteAccountStore::new(pool); let now = Utc.with_ymd_and_hms(2026, 6, 19, 12, 0, 0).unwrap(); let new_access_token = test_jwt((now + Duration::hours(1)).timestamp()); store diff --git a/tests/upstream/models/store.rs b/tests/upstream/models/store.rs index 81828e481..db21e9008 100644 --- a/tests/upstream/models/store.rs +++ b/tests/upstream/models/store.rs @@ -1,15 +1,11 @@ use chrono::Utc; -use codex_proxy_rs::{ - infra::crypto::SecretBox, - upstream::accounts::{ +use codex_proxy_rs::upstream::{ + accounts::{ model::AccountStatus, store::{AccountStore, SqliteAccountStore}, }, - upstream::models::{ - BackendModelEntry, ModelPlanSnapshot, ModelSnapshotStore, SqliteModelSnapshotStore, - }, + models::{BackendModelEntry, ModelPlanSnapshot, ModelSnapshotStore, SqliteModelSnapshotStore}, }; -use secrecy::SecretString; #[tokio::test] async fn model_snapshot_repository_should_replace_and_load_plan_snapshots() { @@ -50,17 +46,12 @@ async fn sqlite_account_store_should_list_pool_accounts() { codex_proxy_rs::infra::database::connect_sqlite(&format!("sqlite://{}", db.display())) .await .expect("sqlite pool"); - let secret_box = SecretBox::new([41u8; 32]); let now = Utc::now().to_rfc3339(); - let access_token_cipher = secret_box - .encrypt(&SecretString::new("access-token".to_string().into())) - .expect("encrypt access token"); - let refresh_token_cipher = secret_box - .encrypt(&SecretString::new("refresh-token".to_string().into())) - .expect("encrypt refresh token"); + let access_token = "access-token"; + let refresh_token = "refresh-token"; sqlx::query( "insert into accounts ( - id, email, chatgpt_account_id, chatgpt_user_id, label, plan_type, access_token_cipher, refresh_token_cipher, + id, email, chatgpt_account_id, chatgpt_user_id, label, plan_type, access_token, refresh_token, access_token_expires_at, status, added_at, updated_at ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) @@ -70,8 +61,8 @@ async fn sqlite_account_store_should_list_pool_accounts() { .bind(Option::::None) .bind("primary") .bind("plus") - .bind(&access_token_cipher) - .bind(Some(&refresh_token_cipher)) + .bind(access_token) + .bind(Some(refresh_token)) .bind(Option::::None) .bind(match AccountStatus::Active { AccountStatus::Active => "active", @@ -83,7 +74,7 @@ async fn sqlite_account_store_should_list_pool_accounts() { .await .expect("insert account"); - let store = SqliteAccountStore::new(pool, secret_box); + let store = SqliteAccountStore::new(pool); let accounts = AccountStore::list_pool_accounts(&store) .await .expect("list accounts");