@@ -35,11 +35,14 @@ AzureClient::AzureClient(const common::backend_api::ObjectClientConfig_t& config
3535 // ClientConfiguration reads environment variables
3636 _account_name = _client_config.account_name ;
3737 _account_key = _client_config.account_key ;
38+ _sas_token = _client_config.sas_token ;
39+ _endpoint_suffix = _client_config.endpoint_suffix ;
3840#ifdef AZURITE_TESTING
3941 _connection_string = _client_config.connection_string ;
4042#endif
4143
4244 // Parse configuration parameters from API (overrides environment)
45+ // Empty values are treated as unset to avoid overriding env vars with no-ops
4346 auto ptr = config.initial_params ;
4447 if (ptr)
4548 {
@@ -48,6 +51,11 @@ AzureClient::AzureClient(const common::backend_api::ObjectClientConfig_t& config
4851 const char * key = ptr->key ;
4952 const char * value = ptr->value ;
5053
54+ if (!value || strlen (value) == 0 )
55+ {
56+ continue ;
57+ }
58+
5159 if (strcmp (key, " account_name" ) == 0 )
5260 {
5361 _account_name = std::string (value);
@@ -56,6 +64,14 @@ AzureClient::AzureClient(const common::backend_api::ObjectClientConfig_t& config
5664 {
5765 _account_key = std::string (value);
5866 }
67+ else if (strcmp (key, " sas_token" ) == 0 )
68+ {
69+ _sas_token = std::string (value);
70+ }
71+ else if (strcmp (key, " endpoint_suffix" ) == 0 )
72+ {
73+ _endpoint_suffix = std::string (value);
74+ }
5975#ifdef AZURITE_TESTING
6076 else if (strcmp (key, " connection_string" ) == 0 )
6177 {
@@ -94,17 +110,27 @@ AzureClient::AzureClient(const common::backend_api::ObjectClientConfig_t& config
94110 } else
95111#endif
96112 if (_account_name.has_value ()) {
97- if (_account_key.has_value ()) {
113+ if (_sas_token.has_value ()) {
114+ // Use SAS token authentication (no credential object needed)
115+ // The token is appended as query string to the service URL
116+ std::string token = _sas_token.value ();
117+ if (!token.empty () && token[0 ] == ' ?' ) {
118+ token = token.substr (1 );
119+ }
120+ std::string url = " https://" + _account_name.value () + " ." + _endpoint_suffix + " ?" + token;
121+ _blob_service_client = std::make_shared<BlobServiceClient>(url, options);
122+ LOG (DEBUG ) << " Azure client initialized with SAS token for account: " << _account_name.value ();
123+ } else if (_account_key.has_value ()) {
98124 // Use StorageSharedKeyCredential (account name + account key)
99- std::string url = " https://" + _account_name.value () + " .blob.core.windows.net " ;
125+ std::string url = " https://" + _account_name.value () + " ." + _endpoint_suffix ;
100126 auto credential = std::make_shared<Azure::Storage::StorageSharedKeyCredential>(
101127 _account_name.value (), _account_key.value ());
102128 _blob_service_client = std::make_shared<BlobServiceClient>(url, credential, options);
103129 LOG (DEBUG ) << " Azure client initialized with StorageSharedKeyCredential for account: " << _account_name.value ();
104130 } else {
105131 // Use DefaultAzureCredential (managed identity, Azure CLI, environment variables, etc.)
106132 // Reference: https://learn.microsoft.com/en-us/azure/developer/cpp/sdk/authentication
107- std::string url = " https://" + _account_name.value () + " .blob.core.windows.net " ;
133+ std::string url = " https://" + _account_name.value () + " ." + _endpoint_suffix ;
108134 // Share a single DefaultAzureCredential across all clients in the process to better
109135 // utilize token caching and reduce chances of overwhelming authentication sources
110136 // (e.g., IMDS) which can result in fatal throttling errors.
@@ -145,6 +171,8 @@ bool AzureClient::verify_credentials(const common::backend_api::ObjectClientConf
145171 ClientConfiguration temp_config;
146172 std::optional<std::string> temp_account_name = temp_config.account_name ;
147173 std::optional<std::string> temp_account_key = temp_config.account_key ;
174+ std::optional<std::string> temp_sas_token = temp_config.sas_token ;
175+ std::string temp_endpoint_suffix = temp_config.endpoint_suffix ;
148176#ifdef AZURITE_TESTING
149177 std::optional<std::string> temp_connection_string = temp_config.connection_string ;
150178#endif
@@ -155,6 +183,8 @@ bool AzureClient::verify_credentials(const common::backend_api::ObjectClientConf
155183 for (size_t i = 0 ; i < config.num_initial_params ; ++i, ++ptr) {
156184 if (strcmp (ptr->key , " account_name" ) == 0 ) temp_account_name = std::string (ptr->value );
157185 else if (strcmp (ptr->key , " account_key" ) == 0 ) temp_account_key = std::string (ptr->value );
186+ else if (strcmp (ptr->key , " sas_token" ) == 0 ) temp_sas_token = std::string (ptr->value );
187+ else if (strcmp (ptr->key , " endpoint_suffix" ) == 0 ) temp_endpoint_suffix = std::string (ptr->value );
158188#ifdef AZURITE_TESTING
159189 else if (strcmp (ptr->key , " connection_string" ) == 0 ) temp_connection_string = std::string (ptr->value );
160190#endif
@@ -166,7 +196,7 @@ bool AzureClient::verify_credentials(const common::backend_api::ObjectClientConf
166196 return (_connection_string == temp_connection_string);
167197 }
168198#endif
169- return (_account_name == temp_account_name) && (_account_key == temp_account_key);
199+ return (_account_name == temp_account_name) && (_account_key == temp_account_key) && (_sas_token == temp_sas_token) && (_endpoint_suffix == temp_endpoint_suffix) ;
170200}
171201
172202common::backend_api::Response AzureClient::async_read_response ()
0 commit comments