This is an automated email from the ASF dual-hosted git repository.

hgruszecki pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new cea62a95d fix(security): replace SecretString with String for API 
response tokens (#3008)
cea62a95d is described below

commit cea62a95d111231932f2b7deaceba6a96959e222
Author: Piotr Gankiewicz <[email protected]>
AuthorDate: Wed Mar 25 21:59:23 2026 +0100

    fix(security): replace SecretString with String for API response tokens 
(#3008)
---
 Cargo.lock                                              |  6 +++---
 Cargo.toml                                              |  6 +++---
 DEPENDENCIES.md                                         |  6 +++---
 bdd/python/uv.lock                                      |  2 +-
 core/binary_protocol/Cargo.toml                         |  2 +-
 .../create_personal_access_token.rs                     |  7 +++----
 core/cli/src/commands/binary_system/login.rs            |  3 +--
 core/common/Cargo.toml                                  |  2 +-
 core/common/src/traits/binary_mapper.rs                 |  5 +----
 .../src/types/permissions/personal_access_token.rs      | 16 ++--------------
 core/common/src/types/user/user_identity_info.rs        | 17 ++---------------
 .../cli/personal_access_token/test_pat_login_options.rs |  3 +--
 .../verify_no_plaintext_credentials_on_disk.rs          |  3 +--
 core/integration/tests/mcp/mod.rs                       |  4 +---
 .../tests/server/scenarios/authentication_scenario.rs   |  3 +--
 .../server/scenarios/cross_protocol_pat_scenario.rs     |  5 ++---
 .../integration/tests/server/scenarios/user_scenario.rs |  9 ++++-----
 core/sdk/Cargo.toml                                     |  2 +-
 core/sdk/src/http/http_client.rs                        |  3 +--
 core/server/src/http/mapper.rs                          |  3 +--
 core/server/src/http/personal_access_tokens.rs          |  6 ++----
 examples/python/uv.lock                                 |  2 +-
 foreign/python/Cargo.toml                               |  2 +-
 23 files changed, 38 insertions(+), 79 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index e69629314..02b353962 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5195,7 +5195,7 @@ checksum = 
"cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb"
 
 [[package]]
 name = "iggy"
-version = "0.9.3-edge.1"
+version = "0.9.4-edge.1"
 dependencies = [
  "async-broadcast",
  "async-dropper",
@@ -5396,7 +5396,7 @@ dependencies = [
 
 [[package]]
 name = "iggy_binary_protocol"
-version = "0.9.3-edge.1"
+version = "0.9.4-edge.1"
 dependencies = [
  "bytemuck",
  "bytes",
@@ -5406,7 +5406,7 @@ dependencies = [
 
 [[package]]
 name = "iggy_common"
-version = "0.9.3-edge.1"
+version = "0.9.4-edge.1"
 dependencies = [
  "aes-gcm",
  "ahash 0.8.12",
diff --git a/Cargo.toml b/Cargo.toml
index 6ac198684..0f8142ef2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -161,10 +161,10 @@ hwlocality = "1.0.0-alpha.11"
 iceberg = "0.9.0"
 iceberg-catalog-rest = "0.9.0"
 iceberg-storage-opendal = "0.9.0"
-iggy = { path = "core/sdk", version = "0.9.3-edge.1" }
+iggy = { path = "core/sdk", version = "0.9.4-edge.1" }
 iggy-cli = { path = "core/cli", version = "0.11.3-edge.1" }
-iggy_binary_protocol = { path = "core/binary_protocol", version = 
"0.9.3-edge.1" }
-iggy_common = { path = "core/common", version = "0.9.3-edge.1" }
+iggy_binary_protocol = { path = "core/binary_protocol", version = 
"0.9.4-edge.1" }
+iggy_common = { path = "core/common", version = "0.9.4-edge.1" }
 iggy_connector_sdk = { path = "core/connectors/sdk", version = "0.2.1-edge.1" }
 integration = { path = "core/integration" }
 journal = { path = "core/journal" }
diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index e89e01db6..83d9349ad 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -452,14 +452,14 @@ ident_case: 1.0.1, "Apache-2.0 OR MIT",
 idna: 1.1.0, "Apache-2.0 OR MIT",
 idna_adapter: 1.2.1, "Apache-2.0 OR MIT",
 if_chain: 1.0.3, "Apache-2.0 OR MIT",
-iggy: 0.9.3-edge.1, "Apache-2.0",
+iggy: 0.9.4-edge.1, "Apache-2.0",
 iggy-bench: 0.4.1-edge.1, "Apache-2.0",
 iggy-bench-dashboard-server: 0.6.3-edge.1, "Apache-2.0",
 iggy-cli: 0.11.3-edge.1, "Apache-2.0",
 iggy-connectors: 0.3.2-edge.1, "Apache-2.0",
 iggy-mcp: 0.3.2-edge.1, "Apache-2.0",
-iggy_binary_protocol: 0.9.3-edge.1, "Apache-2.0",
-iggy_common: 0.9.3-edge.1, "Apache-2.0",
+iggy_binary_protocol: 0.9.4-edge.1, "Apache-2.0",
+iggy_common: 0.9.4-edge.1, "Apache-2.0",
 iggy_connector_elasticsearch_sink: 0.3.2-edge.1, "Apache-2.0",
 iggy_connector_elasticsearch_source: 0.3.2-edge.1, "Apache-2.0",
 iggy_connector_iceberg_sink: 0.3.2-edge.1, "Apache-2.0",
diff --git a/bdd/python/uv.lock b/bdd/python/uv.lock
index e3d0aa275..26ea99b5c 100644
--- a/bdd/python/uv.lock
+++ b/bdd/python/uv.lock
@@ -4,7 +4,7 @@ requires-python = ">=3.10"
 
 [[package]]
 name = "apache-iggy"
-version = "0.7.3.dev1"
+version = "0.7.4.dev1"
 source = { directory = "../../foreign/python" }
 
 [package.metadata]
diff --git a/core/binary_protocol/Cargo.toml b/core/binary_protocol/Cargo.toml
index e3ec5ecdd..b479ecf09 100644
--- a/core/binary_protocol/Cargo.toml
+++ b/core/binary_protocol/Cargo.toml
@@ -17,7 +17,7 @@
 
 [package]
 name = "iggy_binary_protocol"
-version = "0.9.3-edge.1"
+version = "0.9.4-edge.1"
 description = "Wire protocol types and codec for the Iggy binary protocol. 
Shared between server and SDK."
 edition = "2024"
 license = "Apache-2.0"
diff --git 
a/core/cli/src/commands/binary_personal_access_tokens/create_personal_access_token.rs
 
b/core/cli/src/commands/binary_personal_access_tokens/create_personal_access_token.rs
index bf0aef95f..e6f87e2cd 100644
--- 
a/core/cli/src/commands/binary_personal_access_tokens/create_personal_access_token.rs
+++ 
b/core/cli/src/commands/binary_personal_access_tokens/create_personal_access_token.rs
@@ -23,7 +23,6 @@ use iggy_common::Client;
 use iggy_common::PersonalAccessTokenExpiry;
 use iggy_common::create_personal_access_token::CreatePersonalAccessToken;
 use keyring::Entry;
-use secrecy::ExposeSecret;
 use tracing::{Level, event};
 
 pub struct CreatePersonalAccessTokenCmd {
@@ -85,7 +84,7 @@ impl CliCommand for CreatePersonalAccessTokenCmd {
         if self.store_token {
             let server_address = format!("iggy:{}", self.server_address);
             let entry = Entry::new(&server_address, &self.create_token.name)?;
-            entry.set_password(token.token.expose_secret())?;
+            entry.set_password(&token.token)?;
             event!(target: PRINT_TARGET, Level::DEBUG,"Stored token under 
service: {} and name: {}", server_address,
                     self.create_token.name);
             event!(target: PRINT_TARGET, Level::INFO,
@@ -97,7 +96,7 @@ impl CliCommand for CreatePersonalAccessTokenCmd {
                 },
             );
         } else if self.quiet_mode {
-            println!("{}", token.token.expose_secret());
+            println!("{}", &token.token);
         } else {
             event!(target: PRINT_TARGET, Level::INFO,
                 "Personal access token with name: {} and {} created",
@@ -108,7 +107,7 @@ impl CliCommand for CreatePersonalAccessTokenCmd {
                 },
             );
             event!(target: PRINT_TARGET, Level::INFO,"Token: {}",
-                            token.token.expose_secret());
+                            &token.token);
         }
 
         Ok(())
diff --git a/core/cli/src/commands/binary_system/login.rs 
b/core/cli/src/commands/binary_system/login.rs
index efac41632..7c99c1db9 100644
--- a/core/cli/src/commands/binary_system/login.rs
+++ b/core/cli/src/commands/binary_system/login.rs
@@ -23,7 +23,6 @@ use anyhow::Context;
 use async_trait::async_trait;
 use iggy_common::Client;
 use iggy_common::SEC_IN_MICRO;
-use secrecy::ExposeSecret;
 use tracing::{Level, event};
 
 const DEFAULT_LOGIN_SESSION_TIMEOUT: u64 = SEC_IN_MICRO * 15 * 60;
@@ -95,7 +94,7 @@ impl CliCommand for LoginCmd {
                 )
             })?;
 
-        self.server_session.store(token.token.expose_secret())?;
+        self.server_session.store(&token.token)?;
 
         event!(target: PRINT_TARGET, Level::INFO,
             "Successfully logged into Iggy server {}",
diff --git a/core/common/Cargo.toml b/core/common/Cargo.toml
index 3af8fa87f..56e99742f 100644
--- a/core/common/Cargo.toml
+++ b/core/common/Cargo.toml
@@ -16,7 +16,7 @@
 # under the License.
 [package]
 name = "iggy_common"
-version = "0.9.3-edge.1"
+version = "0.9.4-edge.1"
 description = "Iggy is the persistent message streaming platform written in 
Rust, supporting QUIC, TCP and HTTP transport protocols, capable of processing 
millions of messages per second."
 edition = "2024"
 license = "Apache-2.0"
diff --git a/core/common/src/traits/binary_mapper.rs 
b/core/common/src/traits/binary_mapper.rs
index 52b3e6056..891dbc76f 100644
--- a/core/common/src/traits/binary_mapper.rs
+++ b/core/common/src/traits/binary_mapper.rs
@@ -24,7 +24,6 @@ use crate::{
     Stream, StreamDetails, Topic, TopicDetails, UserInfo, UserInfoDetails, 
UserStatus,
 };
 use bytes::Bytes;
-use secrecy::SecretString;
 use std::collections::HashMap;
 use std::str::from_utf8;
 
@@ -508,9 +507,7 @@ pub fn map_raw_pat(payload: Bytes) -> 
Result<RawPersonalAccessToken, IggyError>
     let token = from_utf8(&payload[1..1 + token_length as usize])
         .map_err(|_| IggyError::InvalidUtf8)?
         .to_string();
-    Ok(RawPersonalAccessToken {
-        token: SecretString::from(token),
-    })
+    Ok(RawPersonalAccessToken { token })
 }
 
 pub fn map_client(payload: Bytes) -> Result<ClientInfoDetails, IggyError> {
diff --git a/core/common/src/types/permissions/personal_access_token.rs 
b/core/common/src/types/permissions/personal_access_token.rs
index 6fc5be45c..0837ba17c 100644
--- a/core/common/src/types/permissions/personal_access_token.rs
+++ b/core/common/src/types/permissions/personal_access_token.rs
@@ -16,28 +16,16 @@
  * under the License.
  */
 
-use crate::utils::serde_secret::serialize_secret;
 use crate::utils::timestamp::IggyTimestamp;
-use secrecy::SecretString;
 use serde::{Deserialize, Serialize};
-use std::fmt;
 
 /// `RawPersonalAccessToken` represents the raw personal access token - the 
secured token which is returned only once during the creation.
 /// It consists of the following fields:
 /// - `token`: the unique token that should be securely stored by the user and 
can be used for authentication.
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
 pub struct RawPersonalAccessToken {
     /// The unique token that should be securely stored by the user and can be 
used for authentication.
-    #[serde(serialize_with = "serialize_secret")]
-    pub token: SecretString,
-}
-
-impl fmt::Debug for RawPersonalAccessToken {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("RawPersonalAccessToken")
-            .field("token", &"[REDACTED]")
-            .finish()
-    }
+    pub token: String,
 }
 
 /// `PersonalAccessToken` represents the personal access token. It does not 
contain the token itself, but the information about the token.
diff --git a/core/common/src/types/user/user_identity_info.rs 
b/core/common/src/types/user/user_identity_info.rs
index fef4946d3..64005eec2 100644
--- a/core/common/src/types/user/user_identity_info.rs
+++ b/core/common/src/types/user/user_identity_info.rs
@@ -17,10 +17,7 @@
  */
 
 use crate::UserId;
-use crate::utils::serde_secret::serialize_secret;
-use secrecy::SecretString;
 use serde::{Deserialize, Serialize};
-use std::fmt;
 
 /// `IdentityInfo` represents the information about an identity.
 /// It consists of the following fields:
@@ -38,20 +35,10 @@ pub struct IdentityInfo {
 /// It consists of the following fields:
 /// - `token`: the value of token.
 /// - `expiry`: the expiry of token.
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
 pub struct TokenInfo {
     /// The value of token.
-    #[serde(serialize_with = "serialize_secret")]
-    pub token: SecretString,
+    pub token: String,
     /// The expiry of token.
     pub expiry: u64,
 }
-
-impl fmt::Debug for TokenInfo {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("TokenInfo")
-            .field("token", &"[REDACTED]")
-            .field("expiry", &self.expiry)
-            .finish()
-    }
-}
diff --git 
a/core/integration/tests/cli/personal_access_token/test_pat_login_options.rs 
b/core/integration/tests/cli/personal_access_token/test_pat_login_options.rs
index 2709c5254..536d86f60 100644
--- a/core/integration/tests/cli/personal_access_token/test_pat_login_options.rs
+++ b/core/integration/tests/cli/personal_access_token/test_pat_login_options.rs
@@ -23,7 +23,6 @@ use iggy::prelude::Client;
 use iggy::prelude::PersonalAccessTokenExpiry;
 use keyring::Entry;
 use predicates::str::{contains, starts_with};
-use secrecy::ExposeSecret;
 use serial_test::parallel;
 use std::fmt::{Display, Formatter, Result};
 
@@ -89,7 +88,7 @@ impl IggyCmdTestCase for TestLoginOptions {
             .await;
         assert!(token.is_ok());
         let token = token.unwrap();
-        let token_value = token.token.expose_secret().to_owned();
+        let token_value = token.token.clone();
         self.keyring
             .set_password(&token_value)
             .expect("Failed to set token");
diff --git 
a/core/integration/tests/data_integrity/verify_no_plaintext_credentials_on_disk.rs
 
b/core/integration/tests/data_integrity/verify_no_plaintext_credentials_on_disk.rs
index 2307e00eb..851f4eb90 100644
--- 
a/core/integration/tests/data_integrity/verify_no_plaintext_credentials_on_disk.rs
+++ 
b/core/integration/tests/data_integrity/verify_no_plaintext_credentials_on_disk.rs
@@ -18,7 +18,6 @@
 
 use iggy::prelude::*;
 use integration::iggy_harness;
-use secrecy::ExposeSecret;
 use std::fs;
 use std::path::{Path, PathBuf};
 
@@ -38,7 +37,7 @@ async fn 
should_not_persist_plaintext_password_or_pat_to_disk(harness: &mut Test
         .create_personal_access_token(PAT_NAME, IggyExpiry::NeverExpire)
         .await
         .unwrap();
-    let raw_pat_token = raw_pat.token.expose_secret();
+    let raw_pat_token = &raw_pat.token;
 
     assert!(!raw_pat_token.is_empty(), "Expected non-empty PAT value");
 
diff --git a/core/integration/tests/mcp/mod.rs 
b/core/integration/tests/mcp/mod.rs
index 61560bb8c..ed8ade4b8 100644
--- a/core/integration/tests/mcp/mod.rs
+++ b/core/integration/tests/mcp/mod.rs
@@ -31,8 +31,6 @@ use rmcp::{
     serde::de::DeserializeOwned,
     serde_json::{self, json},
 };
-use secrecy::ExposeSecret;
-
 async fn invoke<T: DeserializeOwned>(
     client: &McpClient,
     method: &str,
@@ -513,7 +511,7 @@ async fn should_create_personal_access_token(harness: 
&TestHarness) {
     )
     .await;
 
-    assert!(!token.token.expose_secret().is_empty());
+    assert!(!token.token.is_empty());
 }
 
 #[iggy_harness(server(mcp), seed = seeds::mcp_standard)]
diff --git a/core/integration/tests/server/scenarios/authentication_scenario.rs 
b/core/integration/tests/server/scenarios/authentication_scenario.rs
index f7b08b520..4dc440c4f 100644
--- a/core/integration/tests/server/scenarios/authentication_scenario.rs
+++ b/core/integration/tests/server/scenarios/authentication_scenario.rs
@@ -30,7 +30,6 @@ use crate::server::scenarios::create_client;
 use bytes::Bytes;
 use iggy::prelude::*;
 use integration::harness::{TestHarness, login_root};
-use secrecy::ExposeSecret;
 use server::binary::command::ServerCommand;
 use strum::IntoEnumIterator;
 
@@ -91,7 +90,7 @@ pub async fn run(harness: &TestHarness) {
     );
 
     let identity = client
-        .login_with_personal_access_token(raw_pat.token.expose_secret())
+        .login_with_personal_access_token(&raw_pat.token)
         .await
         .expect("PAT login should work");
     assert_eq!(identity.user_id, 0, "PAT should authenticate as root");
diff --git 
a/core/integration/tests/server/scenarios/cross_protocol_pat_scenario.rs 
b/core/integration/tests/server/scenarios/cross_protocol_pat_scenario.rs
index 130c557a6..bedfcfe67 100644
--- a/core/integration/tests/server/scenarios/cross_protocol_pat_scenario.rs
+++ b/core/integration/tests/server/scenarios/cross_protocol_pat_scenario.rs
@@ -24,7 +24,6 @@ use iggy::prelude::*;
 use iggy_common::TransportProtocol;
 use integration::harness::TestHarness;
 use integration::iggy_harness;
-use secrecy::ExposeSecret;
 
 const PAT_NAME: &str = "cross-protocol-test-pat";
 const TCP_CLIENT_COUNT: usize = 20;
@@ -49,7 +48,7 @@ pub async fn 
should_see_pat_created_via_http_when_listing_via_tcp(harness: &Test
         .await
         .expect("Failed to create PAT via HTTP");
 
-    assert!(!created_pat.token.expose_secret().is_empty());
+    assert!(!created_pat.token.is_empty());
 
     let http_pats = http_client
         .get_personal_access_tokens()
@@ -108,7 +107,7 @@ pub async fn 
should_see_pat_created_via_tcp_when_listing_via_http(harness: &Test
         .await
         .expect("Failed to create PAT via TCP");
 
-    assert!(!created_pat.token.expose_secret().is_empty());
+    assert!(!created_pat.token.is_empty());
 
     let http_client = create_root_client(harness, 
TransportProtocol::Http).await;
 
diff --git a/core/integration/tests/server/scenarios/user_scenario.rs 
b/core/integration/tests/server/scenarios/user_scenario.rs
index d1904d6c1..f582711b3 100644
--- a/core/integration/tests/server/scenarios/user_scenario.rs
+++ b/core/integration/tests/server/scenarios/user_scenario.rs
@@ -24,7 +24,6 @@ use iggy::prelude::defaults::DEFAULT_ROOT_USERNAME;
 use iggy::prelude::{GlobalPermissions, Permissions};
 use iggy::prelude::{PersonalAccessTokenClient, SEC_IN_MICRO, SystemClient, 
UserClient};
 use integration::harness::{TestHarness, assert_clean_system, login_root};
-use secrecy::ExposeSecret;
 
 pub async fn run(harness: &TestHarness) {
     let client = create_client(harness).await;
@@ -147,14 +146,14 @@ pub async fn run(harness: &TestHarness) {
         .await
         .unwrap();
 
-    assert!(!raw_pat1.token.expose_secret().is_empty());
+    assert!(!raw_pat1.token.is_empty());
 
     let raw_pat2 = client
         .create_personal_access_token(pat_name2, 
PersonalAccessTokenExpiry::NeverExpire)
         .await
         .unwrap();
 
-    assert!(!raw_pat2.token.expose_secret().is_empty());
+    assert!(!raw_pat2.token.is_empty());
 
     // 14. Get personal access tokens and verify that the token is there
     let personal_access_tokens = 
client.get_personal_access_tokens().await.unwrap();
@@ -165,14 +164,14 @@ pub async fn run(harness: &TestHarness) {
 
     // 16. Login with the personal access tokens
     let identity_info = client
-        .login_with_personal_access_token(raw_pat1.token.expose_secret())
+        .login_with_personal_access_token(&raw_pat1.token)
         .await
         .unwrap();
 
     assert_eq!(identity_info.user_id, 1);
 
     let identity_info = client
-        .login_with_personal_access_token(raw_pat2.token.expose_secret())
+        .login_with_personal_access_token(&raw_pat2.token)
         .await
         .unwrap();
 
diff --git a/core/sdk/Cargo.toml b/core/sdk/Cargo.toml
index ab8953b24..cbd405065 100644
--- a/core/sdk/Cargo.toml
+++ b/core/sdk/Cargo.toml
@@ -17,7 +17,7 @@
 
 [package]
 name = "iggy"
-version = "0.9.3-edge.1"
+version = "0.9.4-edge.1"
 description = "Iggy is the persistent message streaming platform written in 
Rust, supporting QUIC, TCP and HTTP transport protocols, capable of processing 
millions of messages per second."
 edition = "2024"
 license = "Apache-2.0"
diff --git a/core/sdk/src/http/http_client.rs b/core/sdk/src/http/http_client.rs
index e2142f4f5..a6ddcaa65 100644
--- a/core/sdk/src/http/http_client.rs
+++ b/core/sdk/src/http/http_client.rs
@@ -29,7 +29,6 @@ use reqwest::{Response, StatusCode, Url};
 use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
 use reqwest_retry::{RetryTransientMiddleware, policies::ExponentialBackoff};
 use reqwest_tracing::{SpanBackendWithUrl, TracingMiddleware};
-use secrecy::ExposeSecret;
 use serde::Serialize;
 use std::ops::Deref;
 use std::str::FromStr;
@@ -250,7 +249,7 @@ impl HttpTransport for HttpClient {
         }
 
         let access_token = identity.access_token.as_ref().unwrap();
-        
self.set_access_token(Some(access_token.token.expose_secret().to_owned()))
+        self.set_access_token(Some(access_token.token.clone()))
             .await;
         Ok(())
     }
diff --git a/core/server/src/http/mapper.rs b/core/server/src/http/mapper.rs
index 04bc16396..879d98338 100644
--- a/core/server/src/http/mapper.rs
+++ b/core/server/src/http/mapper.rs
@@ -24,7 +24,6 @@ use iggy_common::PersonalAccessToken;
 use iggy_common::{ConsumerGroupDetails, ConsumerGroupInfo, 
ConsumerGroupMember, IggyByteSize};
 use iggy_common::{IdentityInfo, PersonalAccessTokenInfo, TokenInfo, 
TopicDetails};
 use iggy_common::{UserInfo, UserInfoDetails};
-use secrecy::SecretString;
 
 pub fn map_user(user: &User) -> UserInfoDetails {
     UserInfoDetails {
@@ -106,7 +105,7 @@ pub fn map_generated_access_token_to_identity_info(token: 
GeneratedToken) -> Ide
     IdentityInfo {
         user_id: token.user_id,
         access_token: Some(TokenInfo {
-            token: SecretString::from(token.access_token),
+            token: token.access_token,
             expiry: token.access_token_expiry,
         }),
     }
diff --git a/core/server/src/http/personal_access_tokens.rs 
b/core/server/src/http/personal_access_tokens.rs
index 1ff235ddb..fbdf7ab3f 100644
--- a/core/server/src/http/personal_access_tokens.rs
+++ b/core/server/src/http/personal_access_tokens.rs
@@ -36,7 +36,7 @@ use 
iggy_common::create_personal_access_token::CreatePersonalAccessToken;
 use iggy_common::delete_personal_access_token::DeletePersonalAccessToken;
 use 
iggy_common::login_with_personal_access_token::LoginWithPersonalAccessToken;
 use iggy_common::{IggyError, RawPersonalAccessToken};
-use secrecy::{ExposeSecret, SecretString};
+use secrecy::ExposeSecret;
 use std::sync::Arc;
 use tracing::instrument;
 
@@ -93,9 +93,7 @@ async fn create_personal_access_token(
 
     match state.shard.send_to_control_plane(request).await? {
         ShardResponse::CreatePersonalAccessTokenResponse(_, token) => {
-            Ok(Json(RawPersonalAccessToken {
-                token: SecretString::from(token),
-            }))
+            Ok(Json(RawPersonalAccessToken { token }))
         }
         ShardResponse::ErrorResponse(err) => Err(err.into()),
         _ => unreachable!("Expected CreatePersonalAccessTokenResponse"),
diff --git a/examples/python/uv.lock b/examples/python/uv.lock
index 9151f6941..d04b1d2bc 100644
--- a/examples/python/uv.lock
+++ b/examples/python/uv.lock
@@ -4,7 +4,7 @@ requires-python = ">=3.10"
 
 [[package]]
 name = "apache-iggy"
-version = "0.7.3.dev1"
+version = "0.7.4.dev1"
 source = { directory = "../../foreign/python" }
 
 [package.metadata]
diff --git a/foreign/python/Cargo.toml b/foreign/python/Cargo.toml
index 827b38c3a..9ce0d5265 100644
--- a/foreign/python/Cargo.toml
+++ b/foreign/python/Cargo.toml
@@ -28,7 +28,7 @@ repository = "https://github.com/apache/iggy";
 [dependencies]
 bytes = "1.11.1"
 futures = "0.3.32"
-iggy = { path = "../../core/sdk", version = "0.9.3-edge.1" }
+iggy = { path = "../../core/sdk", version = "0.9.4-edge.1" }
 pyo3 = "0.28.2"
 pyo3-async-runtimes = { version = "0.28.0", features = [
     "attributes",

Reply via email to