[BUG] Event Hubs service rejecting SAS token for unknown reason
See original GitHub issueLibrary name and version
Azure.Messaging.EventHub 5.6.2
Describe the bug
I am using the Azure.Messaging.EventHubs.Producer.EventHubProducerClient
from my .NET 6 Web project to publish events to an Azure Event Hub. This was working fine when I used a connection string to authenticate the EventHubProducerClient
but I want to switch to using SharedAccessSignature Tokens instead.
Unfortunately, when I try to use SAS tokens, I get this error:
System.UnauthorizedAccessException: Put token failed. status-code: 401, status-description: Malformed authorization token.
Expected behavior
Correctly authenticate when using SAS tokens.
Actual behavior
An error is returned:
System.UnauthorizedAccessException: Put token failed. status-code: 401, status-description: Malformed authorization token.
Reproduction Steps
I am registering my EventHubProdocuerClient
as a Scoped lifetime service in my service provider. I am reading the SAS token from a file (which will actually be mounted to my pod as a Kubernetes secret but that detail isn’t important here):
services.AddScoped<Azure.Messaging.EventHubs.Producer.EventHubProducerClient>((sp) =>
{
string sasToken = fileReader.ReadAllText(sharedAccessSignatureFilePath).Result;
return new Azure.Messaging.EventHubs.Producer.EventHubProducerClient(fullyQualifiedNamespace,
eventHubName,
new AzureSasCredential(sasToken)
});
fullyQualifiedNamespace
is like this:
my-eventhub-001.servicebus.windows.net
First I created a Shared Access Policy in my Event Hub in the Azure Portal called “EventProducer” and I gave it “Send” rights. If I click on that, it shows me a connection string and keys. I pulled some of the info like the resourceUri, keyName, and key from there.
I generated my SAS token using the C# code example as shown in Authenticate access to Event Hubs resources using shared access signatures (SAS) -> Generate a Shared Access Signature token -> C#
My SAS token looks like this:
SharedAccessSignature sr=sb%3a%2f%2fmy-eventhub-001.servicebus.windows.net%2fmyhub1&sig=9q3N{...omitted...}KJy78%3d&se=1640997904&skn=EventProducer
Now I try to create a message batch with my `EventHubProducerClient:
using EventDataBatch eventBatch = await _producerClient.CreateBatchAsync();
It fails with the following exception:
{System.UnauthorizedAccessException: Put token failed. status-code: 401, status-description: Malformed authorization token. TrackingId:775b1ad2-1250-4523-b840-4c7781cb6522_G7, SystemTracker:my-eventhub-001.servicebus.windows.net:myhub1, Timestamp:2021-12-31T23:12:12.
---> Microsoft.Azure.Amqp.AmqpException: Put token failed. status-code: 401, status-description: Malformed authorization token. TrackingId:775b1ad2-1250-4523-b840-4c7781cb6522_G7, SystemTracker:my-eventhub-001.servicebus.windows.net:myhub1, Timestamp:2021-12-31T23:12:12.
at Microsoft.Azure.Amqp.ExceptionDispatcher.Throw(Exception exception)
at Microsoft.Azure.Amqp.AsyncResult.End[TAsyncResult](IAsyncResult result)
at Microsoft.Azure.Amqp.AsyncResult`1.End(IAsyncResult asyncResult)
at Microsoft.Azure.Amqp.AmqpCbsLink.EndSendToken(IAsyncResult result)
at Microsoft.Azure.Amqp.AmqpCbsLink.<>c__DisplayClass4_0.<SendTokenAsync>b__1(IAsyncResult a)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location ---
at Azure.Messaging.EventHubs.Amqp.AmqpConnectionScope.RequestAuthorizationUsingCbsAsync(AmqpConnection connection, CbsTokenProvider tokenProvider, Uri endpoint, String audience, String resource, String[] requiredClaims, TimeSpan timeout)
at Azure.Messaging.EventHubs.Amqp.AmqpConnectionScope.CreateSendingLinkAsync(AmqpConnection connection, Uri endpoint, TransportProducerFeatures features, PartitionPublishingOptionsInternal options, TimeSpan timeout, String linkIdentifier, CancellationToken cancellationToken)
at Azure.Messaging.EventHubs.Amqp.AmqpConnectionScope.OpenProducerLinkAsync(String partitionId, TransportProducerFeatures features, PartitionPublishingOptionsInternal options, TimeSpan timeout, String linkIdentifier, CancellationToken cancellationToken)
at Azure.Messaging.EventHubs.Amqp.AmqpProducer.CreateLinkAndEnsureProducerStateAsync(String partitionId, String producerIdentifier, PartitionPublishingOptionsInternal partitionOptions, TimeSpan timeout, CancellationToken cancellationToken)
at Azure.Messaging.EventHubs.Amqp.AmqpProducer.CreateLinkAndEnsureProducerStateAsync(String partitionId, String producerIdentifier, PartitionPublishingOptionsInternal partitionOptions, TimeSpan timeout, CancellationToken cancellationToken)
at Microsoft.Azure.Amqp.FaultTolerantAmqpObject`1.OnCreateAsync(TimeSpan timeout)
at Microsoft.Azure.Amqp.Singleton`1.GetOrCreateAsync(TimeSpan timeout)
at Microsoft.Azure.Amqp.Singleton`1.GetOrCreateAsync(TimeSpan timeout)
at Azure.Messaging.EventHubs.Amqp.AmqpProducer.CreateBatchAsync(CreateBatchOptions options, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Azure.Messaging.EventHubs.Amqp.AmqpProducer.CreateBatchAsync(CreateBatchOptions options, CancellationToken cancellationToken)
at Azure.Messaging.EventHubs.Producer.EventHubProducerClient.CreateBatchAsync(CreateBatchOptions options, CancellationToken cancellationToken)
at Azure.Messaging.EventHubs.Producer.EventHubProducerClient.CreateBatchAsync(CancellationToken cancellationToken)
at Epic.ApiGateway.Diagnostics.TestPerformer.ResultsReporting.AzureEventHubResultReporter.Report(DestinationTestResult testResult) in /home/justin/source/repos/src/MyProject/AzureEventHubEventProducer.cs:line 34}
I have tried changing the connection options in my producer client to use WebSockets like this but that doesn’t help:
services.AddScoped<Azure.Messaging.EventHubs.Producer.EventHubProducerClient>((sp) =>
{
string sasToken = fileReader.ReadAllText(sharedAccessSignatureFilePath).Result;
return new Azure.Messaging.EventHubs.Producer.EventHubProducerClient(fullyQualifiedNamespace,
eventHubName,
new AzureSasCredential(sasToken),
new EventHubProducerClientOptions()
{
ConnectionOptions = new EventHubConnectionOptions()
{
TransportType = EventHubsTransportType.AmqpWebSockets
}
});
});
I also tried taking the SAS token and manually sending a request to the Event Hub using HTTPS and that seemed to work just fine:
curl -v -H 'Authorization: SharedAccessSignature sr=https%3a%2f%2fmy-eventhub-001.servicebus.windows.net%2fmyhub1&sig=KlJ4U1{...omitted...}g%3d&se=1640988523&skn=EventProducer' --data 'hello world!' https://my-eventhub-001.servicebus.windows.net/myhub1/messages?timeout=60\&api-version=2014-01
This returns a 201:
< HTTP/1.1 201 Created
It seems to me there is a bug in Azure.Messaging.EventHubs.Producer.EventHubProducerClient
which makes it not work with SAS tokens… Unless I am using it incorrectly in which case the documentation should be updated to clarify the proper use. On that note, I also created an issue on the documentation page about some of the code there: https://github.com/MicrosoftDocs/azure-docs/issues/85870
Environment
dotnet --info:
.NET SDK (reflecting any global.json):
Version: 6.0.100
Commit: 9e8b04bbff
Runtime Environment:
OS Name: ubuntu
OS Version: 20.04
OS Platform: Linux
RID: ubuntu.20.04-x64
Base Path: /usr/share/dotnet/sdk/6.0.100/
Host (useful for support):
Version: 6.0.0
Commit: 4822e3c3aa
.NET SDKs installed:
3.1.416 [/usr/share/dotnet/sdk]
5.0.403 [/usr/share/dotnet/sdk]
6.0.100 [/usr/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 3.1.22 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.12 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.0 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.22 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.12 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
IDE is VS Code
1.63.2
899d46d82c4c95423fb7e10e68eba52050e30ba3
x64
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (3 by maintainers)
Top GitHub Comments
Glad to hear that you were able to figure it out and that the fix was simple.
I figured it out…
My SAS token that I was reading from a file had a
\n
at the end of it… 🤦For folks that may encounter this issue in the future – check carefully that the SAS token you are passing to
new AzureSasCredential(sasToken)
does NOT have any stray characters like\n
at the end of it as the constructor will not detect that and throw an error on the client side.