Summary
The Rust AMQP backend cannot authenticate with a SAS token. Three defects block it, one in each layer of the stack. ServiceBusSasConnectionStringCredential still compiles and links against the Rust backend, and it returns an empty token.
The request: implement SAS token generation and CBS SAS put-token for the Rust AMQP backend, so connection string authentication works on the default backend.
Motivation
The three defects
1. The SAS token generator is a stub. GenerateSasToken at sdk/core/azure-core-amqp/src/impl/rust_amqp/amqp/connection_string_credential.cpp:15-20 discards the expiration time and returns an empty string. The uAMQP copy at sdk/core/azure-core-amqp/src/impl/uamqp/amqp/connection_string_credential.cpp:37 does the real work with SASToken_Create from azure_c_shared_utility. That library is not built when USE_RUST_AMQP is on, so the Rust copy was left empty.
The stub fails silently. GetToken at sdk/core/azure-core-amqp/src/amqp/connection_string_credential.cpp:127-137 copies the empty string into AccessToken::Token and reports success. No unit test asserts a generated token value, so the stub passes CI.
2. The C++ CBS layer rejects the SAS token type. ClaimsBasedSecurityImpl::PutToken at sdk/core/azure-core-amqp/src/impl/rust_amqp/amqp/claim_based_security.cpp:79-83 throws "Unsupported Token Type" for every value except CbsTokenType::Jwt.
3. The FFI boundary drops the token type. amqpclaimsbasedsecurity_authorize_path at sdk/core/azure-core-amqp/src/impl/rust_amqp/rust_amqp/rust_wrapper/src/amqp/cbs.rs:120-138 accepts no token type and passes None to the Rust crate. fe2o3/cbs.rs:81 turns None into "jwt".
Four things already work, so the change is small
The shared connection layer already selects the SAS token type. sdk/core/azure-core-amqp/src/amqp/connection.cpp:208 picks CbsTokenType::Sas when IsSasCredential() returns true.
The Rust crate already accepts a token type. azure_core_amqp/src/cbs.rs:50-56 declares token_type: Option<String>, and the doc comment names "servicebus.windows.net:sastoken" as a supported value.
The Rust connection already uses SASL ANONYMOUS. azure_core_amqp/src/fe2o3/connection.rs:49 sets SaslProfile::Anonymous. AMQP CBS requires that mechanism.
The connection string parser and the credential class are backend neutral, and they have unit tests at sdk/core/azure-core-amqp/test/ut/connection_string_tests.cpp.
Impact
PR #7293 restores the connection string constructors for uAMQP only. It throws a clear unsupported-backend error for Rust AMQP. Rust AMQP is the default backend, so most builds hit the error.
The Event Hubs emulator does not support Microsoft Entra ID. A connection string is the only way to authenticate against it. So the emulator stays out of reach for default builds.
ServiceBusSasConnectionStringCredential is public _internal API in azure-core-amqp. Any other library that uses it on the Rust backend receives an empty token today, and it receives no error. A future C++ Service Bus library hits the same wall.
Where to compute the HMAC
There are two options, and the owner must pick one.
Option A puts the work in Rust. The workspace root Cargo.toml already declares hmac (line 39), sha2 (line 61), and base64 (line 26). A new FFI entry point returns the token string. This needs no new CMake linking, and it is portable across all three platforms.
Option B puts the work in C++. This needs an HMAC-SHA256 primitive inside azure-core-amqp. azure-core exposes none. azure-storage-common has one at sdk/storage/azure-storage-common/src/crypt.cpp:158 (BCrypt) and :212 (OpenSSL), and azure-core-amqp cannot depend on storage. The Rust backend also links only -framework Security on macOS (sdk/core/azure-core-amqp/CMakeLists.txt:342), and not OpenSSL, so a C++ path needs a third platform implementation or a CMake change.
Option A is the smaller change. Option B keeps the token format in one language and avoids an FFI round trip.
One detail that a rewrite gets wrong easily
The Service Bus SAS documentation states that the shared access key is a plain text string in Base64 representation, and that callers must not decode it before use. The uAMQP path agrees. It Base64-encodes the raw key string, and SASToken_Create Base64-decodes its argument, so the HMAC key ends up as the raw UTF-8 bytes of SharedAccessKey.
A new implementation must use those raw bytes as the HMAC key. A Base64 decode of the key produces a signature that the service rejects.
The same page gives the token format:
SharedAccessSignature sig=<signature>&se=<expiry>&skn=<keyName>&sr=<URL-encoded-resourceURI>
And the signature:
urlencode(base64(hmacsha256(urlencode(resourceUri) + "\n" + expiry, key)))
The expiry is a count of seconds since the UNIX epoch. The uAMQP path builds the resource URI from GetEndpoint() with GetEntityPath() appended.
Proposal
Validation
Summary
The Rust AMQP backend cannot authenticate with a SAS token. Three defects block it, one in each layer of the stack.
ServiceBusSasConnectionStringCredentialstill compiles and links against the Rust backend, and it returns an empty token.The request: implement SAS token generation and CBS SAS
put-tokenfor the Rust AMQP backend, so connection string authentication works on the default backend.Motivation
The three defects
1. The SAS token generator is a stub.
GenerateSasTokenatsdk/core/azure-core-amqp/src/impl/rust_amqp/amqp/connection_string_credential.cpp:15-20discards the expiration time and returns an empty string. The uAMQP copy atsdk/core/azure-core-amqp/src/impl/uamqp/amqp/connection_string_credential.cpp:37does the real work withSASToken_Createfromazure_c_shared_utility. That library is not built whenUSE_RUST_AMQPis on, so the Rust copy was left empty.The stub fails silently.
GetTokenatsdk/core/azure-core-amqp/src/amqp/connection_string_credential.cpp:127-137copies the empty string intoAccessToken::Tokenand reports success. No unit test asserts a generated token value, so the stub passes CI.2. The C++ CBS layer rejects the SAS token type.
ClaimsBasedSecurityImpl::PutTokenatsdk/core/azure-core-amqp/src/impl/rust_amqp/amqp/claim_based_security.cpp:79-83throws"Unsupported Token Type"for every value exceptCbsTokenType::Jwt.3. The FFI boundary drops the token type.
amqpclaimsbasedsecurity_authorize_pathatsdk/core/azure-core-amqp/src/impl/rust_amqp/rust_amqp/rust_wrapper/src/amqp/cbs.rs:120-138accepts no token type and passesNoneto the Rust crate.fe2o3/cbs.rs:81turnsNoneinto"jwt".Four things already work, so the change is small
The shared connection layer already selects the SAS token type.
sdk/core/azure-core-amqp/src/amqp/connection.cpp:208picksCbsTokenType::SaswhenIsSasCredential()returns true.The Rust crate already accepts a token type.
azure_core_amqp/src/cbs.rs:50-56declarestoken_type: Option<String>, and the doc comment names"servicebus.windows.net:sastoken"as a supported value.The Rust connection already uses SASL ANONYMOUS.
azure_core_amqp/src/fe2o3/connection.rs:49setsSaslProfile::Anonymous. AMQP CBS requires that mechanism.The connection string parser and the credential class are backend neutral, and they have unit tests at
sdk/core/azure-core-amqp/test/ut/connection_string_tests.cpp.Impact
PR #7293 restores the connection string constructors for uAMQP only. It throws a clear unsupported-backend error for Rust AMQP. Rust AMQP is the default backend, so most builds hit the error.
The Event Hubs emulator does not support Microsoft Entra ID. A connection string is the only way to authenticate against it. So the emulator stays out of reach for default builds.
ServiceBusSasConnectionStringCredentialis public_internalAPI inazure-core-amqp. Any other library that uses it on the Rust backend receives an empty token today, and it receives no error. A future C++ Service Bus library hits the same wall.Where to compute the HMAC
There are two options, and the owner must pick one.
Option A puts the work in Rust. The workspace root
Cargo.tomlalready declareshmac(line 39),sha2(line 61), andbase64(line 26). A new FFI entry point returns the token string. This needs no new CMake linking, and it is portable across all three platforms.Option B puts the work in C++. This needs an HMAC-SHA256 primitive inside
azure-core-amqp.azure-coreexposes none.azure-storage-commonhas one atsdk/storage/azure-storage-common/src/crypt.cpp:158(BCrypt) and:212(OpenSSL), andazure-core-amqpcannot depend on storage. The Rust backend also links only-framework Securityon macOS (sdk/core/azure-core-amqp/CMakeLists.txt:342), and not OpenSSL, so a C++ path needs a third platform implementation or a CMake change.Option A is the smaller change. Option B keeps the token format in one language and avoids an FFI round trip.
One detail that a rewrite gets wrong easily
The Service Bus SAS documentation states that the shared access key is a plain text string in Base64 representation, and that callers must not decode it before use. The uAMQP path agrees. It Base64-encodes the raw key string, and
SASToken_CreateBase64-decodes its argument, so the HMAC key ends up as the raw UTF-8 bytes ofSharedAccessKey.A new implementation must use those raw bytes as the HMAC key. A Base64 decode of the key produces a signature that the service rejects.
The same page gives the token format:
And the signature:
The expiry is a count of seconds since the UNIX epoch. The uAMQP path builds the resource URI from
GetEndpoint()withGetEntityPath()appended.Proposal
ServiceBusSasConnectionStringCredential::GenerateSasTokenfor the Rust backend (src/impl/rust_amqp/amqp/connection_string_credential.cpp:15). Sign with the raw UTF-8 bytes ofSharedAccessKey. Build the resource URI fromGetEndpoint()andGetEntityPath().GenerateSasTokenthrow when it cannot build a token. An empty token must never reachPutToken.amqpclaimsbasedsecurity_authorize_path(.../rust_wrapper/src/amqp/cbs.rs:120). PassSome("servicebus.windows.net:sastoken")for a SAS token, andNonefor a JWT. Update the generated header and every caller in the same change.CbsTokenType::Sasto that string inPutToken(src/impl/rust_amqp/amqp/claim_based_security.cpp:79), and delete the throw. Keep a throw forCbsTokenType::Invalid.ProducerClientandConsumerClientconnection string constructors, and delete the two tests that assert the guard.sdk/eventhubs/azure-messaging-eventhubs/README.md. Delete the statement that connection string authentication needs the uAMQP backend.CHANGELOG.mdentry forazure-core-amqpand forazure-messaging-eventhubs.Validation
azure-core-amqpbuilds, and its unit tests pass, on Windows, Linux, and macOS withUSE_RUST_AMQP.ProducerClientbuilt from a connection string sends an event to a live Event Hub on the Rust backend.ConsumerClientbuilt from a connection string reads that event back on the Rust backend.amqp://on the Rust backend.SharedAccessKeyfails with an authentication error. It does not hang, and it does not crash.