From f96d5a1f1f05563f318770d8688010d43b690793 Mon Sep 17 00:00:00 2001 From: Nick Patilsen Date: Tue, 12 Sep 2023 10:21:17 -0700 Subject: [PATCH 1/9] jwt rbac outline --- .../dotnet/jwt_authentication.sln | 14 +++++++++ .../dotnet/jwt_authentication/Program.cs | 31 +++++++++++++++++++ .../dotnet/jwt_authentication/auth.json | 19 ++++++++++++ .../jwt_authentication.csproj | 10 ++++++ 4 files changed, 74 insertions(+) create mode 100644 scenarios/jwt_authentication/dotnet/jwt_authentication.sln create mode 100644 scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs create mode 100644 scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json create mode 100644 scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication.sln b/scenarios/jwt_authentication/dotnet/jwt_authentication.sln new file mode 100644 index 00000000..58ea5666 --- /dev/null +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication.sln @@ -0,0 +1,14 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs new file mode 100644 index 00000000..89d4ea7c --- /dev/null +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs @@ -0,0 +1,31 @@ +using MQTTnet; +using MQTTnet.Client; +using MQTTnet.Client.Extensions; +using System.Text; + +//System.Diagnostics.Trace.Listeners.Add(new System.Diagnostics.ConsoleTraceListener()); + +MqttConnectionSettings cs = MqttConnectionSettings.CreateFromEnvVars(); +Console.WriteLine($"Connecting to {cs}"); + +IMqttClient mqttClient = new MqttFactory().CreateMqttClient(MqttNetTraceLogger.CreateTraceLogger()); +byte[] auth = Encoding.UTF8.GetBytes(File.ReadAllText("auth.json")); + +MqttClientConnectResult connAck = await mqttClient!.ConnectAsync(new MqttClientOptionsBuilder() + .WithConnectionSettings(cs) + .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V500) + .WithAuthentication("OAUTH2-JWT", auth) + .Build()); + +Console.WriteLine($"Client Connected: {mqttClient.IsConnected} with CONNACK: {connAck.ResultCode}"); + +mqttClient.ApplicationMessageReceivedAsync += async m => await Console.Out.WriteAsync( + $"Received message on topic: '{m.ApplicationMessage.Topic}' with content: '{m.ApplicationMessage.ConvertPayloadToString()}'\n\n"); + +MqttClientSubscribeResult suback = await mqttClient.SubscribeAsync("sample/+", MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); +suback.Items.ToList().ForEach(s => Console.WriteLine($"subscribed to '{s.TopicFilter.Topic}' with '{s.ResultCode}'")); + +MqttClientPublishResult puback = await mqttClient.PublishStringAsync("sample/topic1", "hello world!", MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); +Console.WriteLine(puback.ReasonString); + +Console.ReadLine(); diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json b/scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json new file mode 100644 index 00000000..89ce01b8 --- /dev/null +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json @@ -0,0 +1,19 @@ +{ + "properties": { + "roleName": "Event Grid Pub-Sub", + "description": "communicate with Event Grid.", + "assignableScopes": [ + "/subscriptions/" + ], + "permissions": [ + { + "actions": [], + "notActions": [], + "dataActions": [ + "Microsoft.EventGrid/*" + ], + "notDataActions": [] + } + ] + } +} \ No newline at end of file diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj b/scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj new file mode 100644 index 00000000..f02677bf --- /dev/null +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj @@ -0,0 +1,10 @@ + + + + Exe + net7.0 + enable + enable + + + From 90cd1e051ce3e7eef92872122f9d25775317ea8c Mon Sep 17 00:00:00 2001 From: Nick Patilsen Date: Thu, 21 Sep 2023 14:39:36 -0700 Subject: [PATCH 2/9] jwt sample running --- scenarios/jwt_authentication/README.md | 167 ++++++++++++++++++ .../dotnet/jwt_authentication.sln | 11 ++ .../dotnet/jwt_authentication/Program.cs | 26 ++- .../dotnet/jwt_authentication/auth.json | 19 -- .../jwt_authentication.csproj | 13 +- 5 files changed, 211 insertions(+), 25 deletions(-) create mode 100644 scenarios/jwt_authentication/README.md delete mode 100644 scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json diff --git a/scenarios/jwt_authentication/README.md b/scenarios/jwt_authentication/README.md new file mode 100644 index 00000000..8d375d79 --- /dev/null +++ b/scenarios/jwt_authentication/README.md @@ -0,0 +1,167 @@ +# :point_right: JWT Authentication to Event Grid + +| [Create the Client Certificate](#lock-create-the-client-certificate) | [Configure Event Grid Namespaces](#triangular_ruler-configure-event-grid-namespaces) | [Configure Mosquitto](#fly-configure-mosquitto) | [Run the Sample](#game_die-run-the-sample) | + +This scenario showcases how to authenticate to Azure Event Grid via JWT authentication using MQTT 5. This scenario is identical to `getting_started` in functionality. + +The sample provides step by step instructions on how to perform following tasks: + +- Create a Json Web Token, which is used to authenticate to Event Grid. +- Create the resources including client, topic spaces, permission bindings +- Use $all client group, which is the default client group with all the clients in a namespace, to authorize publish and subscribe access in permission bindings +- Connect with MQTT 5.0.0 + - Configure connection settings such as KeepAlive and CleanSession +- Publish messages to a topic +- Subscribe to a topic to receive messages + +To keep the scenario simple, a single client called "sample_client" publishes and subscribes to MQTT messages on topics shown in the table. + +|Client|Role|Operation|Topic/Topic Filter| +|------|----|---------|------------------| +|sample_client|publisher|publish|sample/topic1| +|sample_client|subscriber|subscribe|sample/+| + + +## :lock: Configure the Json Web Token and AAD Role Assignments + +1. Modify the JSON file `auth.json`, found in `./dotnet/jwt_authentication/` with a subscription Id: + +```json +{ + "properties": { + "roleName": "Event Grid Pub-Sub", + "description": "communicate with Event Grid.", + "assignableScopes": [ + "/subscriptions/" + ], + "permissions": [ + { + "actions": [], + "notActions": [], + "dataActions": [ + "Microsoft.EventGrid/*" + ], + "notDataActions": [] + } + ] + } +} +``` +2. In the Azure portal, go to your Resource Group and open the Access control (IAM) page. +3. Click Add and then click Add custom role. This opens the custom roles editor. +4. + +## :triangular_ruler: Configure Event Grid Namespaces + +Ensure to create an Event Grid namespace by following the steps in [setup](../setup). Event Grid namespace requires registering the client, and the topic spaces to authorize the publish/subscribe permissions. + +### Create the Client + +We will use the SubjectMatchesAuthenticationName validation scheme for `sample_client` to create the client from the portal or with the script: + +```bash +# from folder scenarios/getting_started +source ../../az.env + +az resource create --id "$res_id/clients/sample_client" --properties '{ + "authenticationName": "sample_client", + "state": "Enabled", + "clientCertificateAuthentication": { + "validationScheme": "SubjectMatchesAuthenticationName" + }, + "attributes": { + "type": "sample-client" + }, + "description": "This is a test publisher client" +}' +``` + +### Create topic spaces and permission bindings +Run the commands to create the "samples" topic space, and the two permission bindings that provide publish and subscribe access to $all client group on the samples topic space. + +```bash +# from folder scenarios/getting_started +source ../../az.env + +az resource create --id "$res_id/topicSpaces/samples" --properties '{ + "topicTemplates": ["sample/#"] +}' + +az resource create --id "$res_id/permissionBindings/samplesPub" --properties '{ + "clientGroupName":"$all", + "topicSpaceName":"samples", + "permission":"Publisher" +}' + +az resource create --id "$res_id/permissionBindings/samplesSub" --properties '{ + "clientGroupName":"$all", + "topicSpaceName":"samples", + "permission":"Subscriber" +}' +``` + +### Create the .env file with connection details + +The required `.env` files can be configured manually, we provide the script below as a reference to create those files, as they are ignored from git. + +```bash +# from folder scenarios/getting_started +source ../../az.env +host_name=$(az resource show --ids $res_id --query "properties.topicSpacesConfiguration.hostname" -o tsv) + +echo "MQTT_HOST_NAME=$host_name" > .env +echo "MQTT_USERNAME=sample_client" >> .env +echo "MQTT_CLIENT_ID=sample_client" >> .env +echo "MQTT_CERT_FILE=sample_client.pem" >> .env +echo "MQTT_KEY_FILE=sample_client.key" >> .env +``` + +## :fly: Configure Mosquitto + +To establish the TLS connection, the CA needs to be trusted, most MQTT clients allow to specify the ca trust chain as part of the connection, to create a chain file with the root and the intermediate use: + +```bash +# from folder _mosquitto +cat ~/.step/certs/root_ca.crt ~/.step/certs/intermediate_ca.crt > chain.pem +cp chain.pem ../scenarios/getting_started +``` +The `chain.pem` is used by mosquitto via the `cafile` settings to authenticate X509 client connections. + +```bash +# from folder scenarios/getting_started +echo "MQTT_HOST_NAME=localhost" > .env +echo "MQTT_CLIENT_ID=sample_client" >> .env +echo "MQTT_CERT_FILE=sample_client.pem" >> .env +echo "MQTT_KEY_FILE=sample_client.key" >> .env +echo "MQTT_CA_FILE=chain.pem" >> .env +``` + +To use mosquitto without certificates + +```bash +# from folder scenarios/getting_started +echo "MQTT_HOST_NAME=localhost" > .env +echo "MQTT_TCP_PORT=1883" >> .env +echo "MQTT_USE_TLS=false" >> .env +echo "MQTT_CLIENT_ID=sample_client" >> .env +``` + +## :game_die: Run the Sample + +All samples are designed to be executed from the root scenario folder. + +### dotnet + +To build the dotnet sample run: + +```bash +# from folder scenarios/getting_started +dotnet build dotnet/getting_started.sln +``` + +To run the dotnet sample: + +```bash + dotnet/getting_started/bin/Debug/net7.0/getting_started +``` +(this will use the `.env` file created before) diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication.sln b/scenarios/jwt_authentication/dotnet/jwt_authentication.sln index 58ea5666..3af2a802 100644 --- a/scenarios/jwt_authentication/dotnet/jwt_authentication.sln +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication.sln @@ -3,12 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "jwt_authentication", "jwt_authentication\jwt_authentication.csproj", "{64CD6647-A322-4F5C-AFD1-3B657CE65FA5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {64CD6647-A322-4F5C-AFD1-3B657CE65FA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64CD6647-A322-4F5C-AFD1-3B657CE65FA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64CD6647-A322-4F5C-AFD1-3B657CE65FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64CD6647-A322-4F5C-AFD1-3B657CE65FA5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7A2246F6-BDDC-4173-AD14-FD2DC0F879D0} + EndGlobalSection EndGlobal diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs index 89d4ea7c..501f170a 100644 --- a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs @@ -2,19 +2,29 @@ using MQTTnet.Client; using MQTTnet.Client.Extensions; using System.Text; +using Azure.Identity; +using Azure.Core; //System.Diagnostics.Trace.Listeners.Add(new System.Diagnostics.ConsoleTraceListener()); -MqttConnectionSettings cs = MqttConnectionSettings.CreateFromEnvVars(); -Console.WriteLine($"Connecting to {cs}"); +//MqttConnectionSettings cs = MqttConnectionSettings.CreateFromEnvVars(); +//Console.WriteLine($"Connecting to {cs}"); +// Create client IMqttClient mqttClient = new MqttFactory().CreateMqttClient(MqttNetTraceLogger.CreateTraceLogger()); -byte[] auth = Encoding.UTF8.GetBytes(File.ReadAllText("auth.json")); +string hostname = ""; + +// Create JWT +var defaultCredential = new DefaultAzureCredential(); +var tokenRequestContext = new TokenRequestContext(new string[] { "https://eventgrid.azure.net/" }); +AccessToken jwt = defaultCredential.GetToken(tokenRequestContext); MqttClientConnectResult connAck = await mqttClient!.ConnectAsync(new MqttClientOptionsBuilder() - .WithConnectionSettings(cs) + .WithClientId("sample_client") + .WithTcpServer(hostname, 8883) .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V500) - .WithAuthentication("OAUTH2-JWT", auth) + .WithAuthentication("OAUTH2-JWT", Encoding.UTF8.GetBytes(jwt.Token)) + .WithTlsOptions(new MqttClientTlsOptions() { UseTls = true }) .Build()); Console.WriteLine($"Client Connected: {mqttClient.IsConnected} with CONNACK: {connAck.ResultCode}"); @@ -29,3 +39,9 @@ Console.WriteLine(puback.ReasonString); Console.ReadLine(); + +// TODO -- app authentication +// TODO -- mqtt client extensions? +// TODO -- test which params are needed (e.g., tls, username) +// TODO -- catch mqtt autnetication failed exceptions and gracefully exit +// MSAL -- borwser flow authentication diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json b/scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json deleted file mode 100644 index 89ce01b8..00000000 --- a/scenarios/jwt_authentication/dotnet/jwt_authentication/auth.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "properties": { - "roleName": "Event Grid Pub-Sub", - "description": "communicate with Event Grid.", - "assignableScopes": [ - "/subscriptions/" - ], - "permissions": [ - { - "actions": [], - "notActions": [], - "dataActions": [ - "Microsoft.EventGrid/*" - ], - "notDataActions": [] - } - ] - } -} \ No newline at end of file diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj b/scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj index f02677bf..96ed8360 100644 --- a/scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj @@ -1,4 +1,4 @@ - + Exe @@ -7,4 +7,15 @@ enable + + + + + + + + ..\..\..\..\mqttclients\dotnet\MQTTnet.Client.Extensions\bin\Debug\net7.0\MQTTnet.Client.Extensions.dll + + + From 96651f7dce7c1807b158e3d95997b58a8012fb30 Mon Sep 17 00:00:00 2001 From: Nick Patilsen Date: Mon, 25 Sep 2023 11:40:54 -0700 Subject: [PATCH 3/9] cleanup --- .../dotnet/jwt_authentication/Program.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs index 501f170a..2b178f7d 100644 --- a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs @@ -5,11 +5,6 @@ using Azure.Identity; using Azure.Core; -//System.Diagnostics.Trace.Listeners.Add(new System.Diagnostics.ConsoleTraceListener()); - -//MqttConnectionSettings cs = MqttConnectionSettings.CreateFromEnvVars(); -//Console.WriteLine($"Connecting to {cs}"); - // Create client IMqttClient mqttClient = new MqttFactory().CreateMqttClient(MqttNetTraceLogger.CreateTraceLogger()); string hostname = ""; @@ -39,9 +34,3 @@ Console.WriteLine(puback.ReasonString); Console.ReadLine(); - -// TODO -- app authentication -// TODO -- mqtt client extensions? -// TODO -- test which params are needed (e.g., tls, username) -// TODO -- catch mqtt autnetication failed exceptions and gracefully exit -// MSAL -- borwser flow authentication From 3200f944d48a385ef863e2b4a9a588d4ade41245 Mon Sep 17 00:00:00 2001 From: Nick Patilsen <110431552+patilsnr@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:54:28 -0700 Subject: [PATCH 4/9] Update README.md --- scenarios/jwt_authentication/README.md | 109 ++++--------------------- 1 file changed, 14 insertions(+), 95 deletions(-) diff --git a/scenarios/jwt_authentication/README.md b/scenarios/jwt_authentication/README.md index 8d375d79..e1495287 100644 --- a/scenarios/jwt_authentication/README.md +++ b/scenarios/jwt_authentication/README.md @@ -6,9 +6,10 @@ This scenario showcases how to authenticate to Azure Event Grid via JWT authenti The sample provides step by step instructions on how to perform following tasks: -- Create a Json Web Token, which is used to authenticate to Event Grid. - Create the resources including client, topic spaces, permission bindings - Use $all client group, which is the default client group with all the clients in a namespace, to authorize publish and subscribe access in permission bindings +- Create a custom role assignment on the Azure Portal to access Event Grid via Json Web Token (JWT) authentication. +- Create a JWT, which is used to authenticate to Event Grid. - Connect with MQTT 5.0.0 - Configure connection settings such as KeepAlive and CleanSession - Publish messages to a topic @@ -24,7 +25,7 @@ To keep the scenario simple, a single client called "sample_client" publishes an ## :lock: Configure the Json Web Token and AAD Role Assignments -1. Modify the JSON file `auth.json`, found in `./dotnet/jwt_authentication/` with a subscription Id: +1. Modify the following JSON snippet by adding an Azure subscription Id: ```json { @@ -47,9 +48,12 @@ To keep the scenario simple, a single client called "sample_client" publishes an } } ``` -2. In the Azure portal, go to your Resource Group and open the Access control (IAM) page. -3. Click Add and then click Add custom role. This opens the custom roles editor. -4. +2. Copy the modified snippet and save it locally. +3. In the Azure portal, go to your Resource Group that contains Event Grid and open the Access control (IAM) page. +4. Click Add and then click Add custom role. This opens the custom roles editor. +5. On the `Basics` tab, select `Start from JSON`, and upload the modified JSON file you saved locally. +6. Select the `Review and Create` tab and then `Create`. +7. **NOTE:** It is possible that your Azure account may not have room for more custom role assignments. In this instance the current workaround is to create a free Azure account and complete this process while logged in from there. ## :triangular_ruler: Configure Event Grid Namespaces @@ -57,94 +61,10 @@ Ensure to create an Event Grid namespace by following the steps in [setup](../se ### Create the Client -We will use the SubjectMatchesAuthenticationName validation scheme for `sample_client` to create the client from the portal or with the script: - -```bash -# from folder scenarios/getting_started -source ../../az.env - -az resource create --id "$res_id/clients/sample_client" --properties '{ - "authenticationName": "sample_client", - "state": "Enabled", - "clientCertificateAuthentication": { - "validationScheme": "SubjectMatchesAuthenticationName" - }, - "attributes": { - "type": "sample-client" - }, - "description": "This is a test publisher client" -}' -``` +We will use the SubjectMatchesAuthenticationName validation scheme for `sample_client`. Instructions for how to do this can be found in [getting_started](../getting_started). If this has already been done once, it does not have to be done again (unless using a different Azure account). ### Create topic spaces and permission bindings -Run the commands to create the "samples" topic space, and the two permission bindings that provide publish and subscribe access to $all client group on the samples topic space. - -```bash -# from folder scenarios/getting_started -source ../../az.env - -az resource create --id "$res_id/topicSpaces/samples" --properties '{ - "topicTemplates": ["sample/#"] -}' - -az resource create --id "$res_id/permissionBindings/samplesPub" --properties '{ - "clientGroupName":"$all", - "topicSpaceName":"samples", - "permission":"Publisher" -}' - -az resource create --id "$res_id/permissionBindings/samplesSub" --properties '{ - "clientGroupName":"$all", - "topicSpaceName":"samples", - "permission":"Subscriber" -}' -``` - -### Create the .env file with connection details - -The required `.env` files can be configured manually, we provide the script below as a reference to create those files, as they are ignored from git. - -```bash -# from folder scenarios/getting_started -source ../../az.env -host_name=$(az resource show --ids $res_id --query "properties.topicSpacesConfiguration.hostname" -o tsv) - -echo "MQTT_HOST_NAME=$host_name" > .env -echo "MQTT_USERNAME=sample_client" >> .env -echo "MQTT_CLIENT_ID=sample_client" >> .env -echo "MQTT_CERT_FILE=sample_client.pem" >> .env -echo "MQTT_KEY_FILE=sample_client.key" >> .env -``` - -## :fly: Configure Mosquitto - -To establish the TLS connection, the CA needs to be trusted, most MQTT clients allow to specify the ca trust chain as part of the connection, to create a chain file with the root and the intermediate use: - -```bash -# from folder _mosquitto -cat ~/.step/certs/root_ca.crt ~/.step/certs/intermediate_ca.crt > chain.pem -cp chain.pem ../scenarios/getting_started -``` -The `chain.pem` is used by mosquitto via the `cafile` settings to authenticate X509 client connections. - -```bash -# from folder scenarios/getting_started -echo "MQTT_HOST_NAME=localhost" > .env -echo "MQTT_CLIENT_ID=sample_client" >> .env -echo "MQTT_CERT_FILE=sample_client.pem" >> .env -echo "MQTT_KEY_FILE=sample_client.key" >> .env -echo "MQTT_CA_FILE=chain.pem" >> .env -``` - -To use mosquitto without certificates - -```bash -# from folder scenarios/getting_started -echo "MQTT_HOST_NAME=localhost" > .env -echo "MQTT_TCP_PORT=1883" >> .env -echo "MQTT_USE_TLS=false" >> .env -echo "MQTT_CLIENT_ID=sample_client" >> .env -``` +Run the commands to create the "samples" topic space, and the two permission bindings that provide publish and subscribe access to $all client group on the samples topic space. As for above, the instructions to do this are part of [getting_started](../getting_started) and do not have to be repeated if they have already been done in the Azure account being used to run this sample. ## :game_die: Run the Sample @@ -155,13 +75,12 @@ All samples are designed to be executed from the root scenario folder. To build the dotnet sample run: ```bash -# from folder scenarios/getting_started -dotnet build dotnet/getting_started.sln +# from folder scenarios/jwt_authenticaton +dotnet build dotnet/jwt_authentication.sln ``` To run the dotnet sample: ```bash - dotnet/getting_started/bin/Debug/net7.0/getting_started + dotnet/jwt_authentication/bin/Debug/net7.0/jwt_authentication ``` -(this will use the `.env` file created before) From 48127c96936b29143fa9ce485f4e302c28476530 Mon Sep 17 00:00:00 2001 From: Nick Patilsen <110431552+patilsnr@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:57:47 -0700 Subject: [PATCH 5/9] Update README.md --- scenarios/jwt_authentication/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scenarios/jwt_authentication/README.md b/scenarios/jwt_authentication/README.md index e1495287..3a8f7056 100644 --- a/scenarios/jwt_authentication/README.md +++ b/scenarios/jwt_authentication/README.md @@ -22,6 +22,8 @@ To keep the scenario simple, a single client called "sample_client" publishes an |sample_client|publisher|publish|sample/topic1| |sample_client|subscriber|subscribe|sample/+| +## Prerequisites +This sample involves configuring Event Grid per the specifications in [getting_started](../getting_started). If that sample has not already been set up and run, it should be done before moving onto this one. ## :lock: Configure the Json Web Token and AAD Role Assignments From f5a7c9898afb803a6f8c5ab3377d0cdfc3b9c24c Mon Sep 17 00:00:00 2001 From: Nick Patilsen <110431552+patilsnr@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:01:00 -0700 Subject: [PATCH 6/9] Update README.md --- scenarios/jwt_authentication/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scenarios/jwt_authentication/README.md b/scenarios/jwt_authentication/README.md index 3a8f7056..94fdafab 100644 --- a/scenarios/jwt_authentication/README.md +++ b/scenarios/jwt_authentication/README.md @@ -57,11 +57,11 @@ This sample involves configuring Event Grid per the specifications in [getting_s 6. Select the `Review and Create` tab and then `Create`. 7. **NOTE:** It is possible that your Azure account may not have room for more custom role assignments. In this instance the current workaround is to create a free Azure account and complete this process while logged in from there. -## :triangular_ruler: Configure Event Grid Namespaces +## :triangular_ruler: Configure Event Grid Namespaces (Skip if [getting_started](../getting_started) has already been properly configured) Ensure to create an Event Grid namespace by following the steps in [setup](../setup). Event Grid namespace requires registering the client, and the topic spaces to authorize the publish/subscribe permissions. -### Create the Client +### Create the Client (Skip if [getting_started](../getting_started) has already been properly configured) We will use the SubjectMatchesAuthenticationName validation scheme for `sample_client`. Instructions for how to do this can be found in [getting_started](../getting_started). If this has already been done once, it does not have to be done again (unless using a different Azure account). From b07f7845e690baf57bce81aec1b83db04b8d0ff3 Mon Sep 17 00:00:00 2001 From: Nick Patilsen Date: Mon, 25 Sep 2023 12:03:07 -0700 Subject: [PATCH 7/9] comments --- .../jwt_authentication/dotnet/jwt_authentication/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs index 2b178f7d..87b3817e 100644 --- a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs @@ -11,9 +11,12 @@ // Create JWT var defaultCredential = new DefaultAzureCredential(); + +// Sets the audience field of the JWT to Event Grid var tokenRequestContext = new TokenRequestContext(new string[] { "https://eventgrid.azure.net/" }); AccessToken jwt = defaultCredential.GetToken(tokenRequestContext); +// Required to use port 8883: https://learn.microsoft.com/azure/event-grid/mqtt-support#code-samples MqttClientConnectResult connAck = await mqttClient!.ConnectAsync(new MqttClientOptionsBuilder() .WithClientId("sample_client") .WithTcpServer(hostname, 8883) From a18c16ca8bea4645f23940116528f7841604152f Mon Sep 17 00:00:00 2001 From: Nick Patilsen Date: Mon, 25 Sep 2023 12:36:21 -0700 Subject: [PATCH 8/9] misc --- .../jwt_authentication/dotnet/jwt_authentication/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs index 87b3817e..ab5d830c 100644 --- a/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs +++ b/scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs @@ -16,7 +16,7 @@ var tokenRequestContext = new TokenRequestContext(new string[] { "https://eventgrid.azure.net/" }); AccessToken jwt = defaultCredential.GetToken(tokenRequestContext); -// Required to use port 8883: https://learn.microsoft.com/azure/event-grid/mqtt-support#code-samples +// Required to use port 8883: https://learn.microsoft.com/azure/event-grid/mqtt-support MqttClientConnectResult connAck = await mqttClient!.ConnectAsync(new MqttClientOptionsBuilder() .WithClientId("sample_client") .WithTcpServer(hostname, 8883) From d6bdf0bd95cea3bc488cf95cdd3614bfa3eb5030 Mon Sep 17 00:00:00 2001 From: Nick Patilsen <110431552+patilsnr@users.noreply.github.com> Date: Mon, 2 Oct 2023 09:52:35 -0700 Subject: [PATCH 9/9] Update README.md with websocket info --- scenarios/jwt_authentication/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scenarios/jwt_authentication/README.md b/scenarios/jwt_authentication/README.md index 94fdafab..065f71f5 100644 --- a/scenarios/jwt_authentication/README.md +++ b/scenarios/jwt_authentication/README.md @@ -86,3 +86,18 @@ To run the dotnet sample: ```bash dotnet/jwt_authentication/bin/Debug/net7.0/jwt_authentication ``` + +## Connecting over WebSocket +To connect using WebSockets, modify client's `ConnectAsync()` call as follows: +```csharp +MqttClientConnectResult connAck = await mqttClient!.ConnectAsync(new MqttClientOptionsBuilder() + .WithClientId("sample_client") + //.WithTcpServer(hostname, 8883) + .WithWebSocketServer(b => b.WithUri($"{hostname}:443/mqtt")) + .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V500) + .WithAuthentication("OAUTH2-JWT", Encoding.UTF8.GetBytes(jwt.Token)) + .WithTlsOptions(new MqttClientTlsOptions() { UseTls = true }) + .Build()); +``` + +Note that it is required to use port 443 for websocket connections. To learn more about this flow visit the [documentation](https://learn.microsoft.com/azure/event-grid/mqtt-support#connection-flow).