Search code examples
c#mqttazure-iot-hubazure-iot-sdk

Azure SDK UnauthorizedException: Put token failed. status-code: 401, status description: Unauthorized: When KeyName is empty the resource URI must


Using C#, the Windows Form Template from Visual Studio and the Microsoft Azure Client SDK

I'm trying to send a message to an IOT Hub. I tested connection String with a Virtual Pi and it works there, I can see the connection and incoming messages in the Azure Shell.

Imports:

using System.Windows.Forms;
using Microsoft.Azure.Devices;
using Message = Microsoft.Azure.Devices.Message;
static string connectionString = "HostName=****.azure- devices.net;DeviceId=****;SharedAccessKey=*******=";
static string deviceId = "SensorTest";
static ServiceClient serviceClient;

serviceClient = ServiceClient.CreateFromConnectionString(connectionString);
SendMessageToCloud("takemystringyoupieceof***").Wait();


private static async Task SendMessageToCloud(string s)
        {
            MyData data = new MyData
            {
                Thing1 = "string",
                Thing2 = "string",
                Thing3 = 1234,
                Thing4 = "string",
                Thing5 = s
            };

            var serializeData = JsonConvert.SerializeObject(data);
            var commandMessage = new Message(Encoding.ASCII.GetBytes(serializeData));
            await serviceClient.SendAsync(deviceId, commandMessage);
        }

This throws an Inner Exception:

{"Put token failed. status-code: 401, status-description:
Unauthorized: When KeyName is empty the resource URI must include a device id (Value '****.azure- devices.net').."} 
System.Exception {Microsoft.Azure.Devices.Common.Exceptions.UnauthorizedException}

I would like some help understanding the error message:

  • Isn't KeyName "SharedKey" in this instance?`
  • The deviceID is included as you can see above?
  • The Value in the error message is the value for hostname?

For working code example, myData class:

internal class MyData
    {
        public string Thing1 { get; set; }
        public string Thing2 { get; internal set; }
        public int Thing3 { get; set; }
        public string Thing4 { get; set; }
        public string Thing5 { get; set; }

    }


Solution

  • You are using a DeviceClient connection string with the ServiceClient. The SendAsync you are calling in your SendMessageToCloud is actually a Cloud to Device API. So depending on your intention, the answer is you either need to use the DeviceClient (a helpful sample can be found here) or you want to use an appropriate Service connection string which can be found in the Shared access policies blade for the IoTHub in the portal. Practicing least privilege, the Service key allows cloud-to-device messages (details on shared access policies can be found here)