-
Notifications
You must be signed in to change notification settings - Fork 29
Sample with JWT #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
patilsnr
wants to merge
10
commits into
main
Choose a base branch
from
npatilsen/aadSample
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Sample with JWT #68
Changes from 2 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
f96d5a1
jwt rbac outline
patilsnr 90cd1e0
jwt sample running
patilsnr 96651f7
cleanup
patilsnr 3200f94
Update README.md
patilsnr 48127c9
Update README.md
patilsnr f5a7c98
Update README.md
patilsnr b07f784
comments
patilsnr a18c16c
misc
patilsnr d111358
Merge branch 'main' into npatilsen/aadSample
patilsnr d6bdf0b
Update README.md with websocket info
patilsnr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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/<YOUR SUBSCRIPTION ID HERE>" | ||
| ], | ||
| "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) |
25 changes: 25 additions & 0 deletions
25
scenarios/jwt_authentication/dotnet/jwt_authentication.sln
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| | ||
| 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 |
47 changes: 47 additions & 0 deletions
47
scenarios/jwt_authentication/dotnet/jwt_authentication/Program.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| using MQTTnet; | ||
| 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}"); | ||
|
|
||
| // Create client | ||
| IMqttClient mqttClient = new MqttFactory().CreateMqttClient(MqttNetTraceLogger.CreateTraceLogger()); | ||
| string hostname = "<Event Grid Mqtt Hostname Here>"; | ||
|
|
||
| // 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() | ||
| .WithClientId("sample_client") | ||
| .WithTcpServer(hostname, 8883) | ||
| .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V500) | ||
| .WithAuthentication("OAUTH2-JWT", Encoding.UTF8.GetBytes(jwt.Token)) | ||
| .WithTlsOptions(new MqttClientTlsOptions() { UseTls = true }) | ||
| .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(); | ||
|
|
||
| // 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 | ||
21 changes: 21 additions & 0 deletions
21
scenarios/jwt_authentication/dotnet/jwt_authentication/jwt_authentication.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net7.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Azure.Identity" Version="1.10.1" /> | ||
| <PackageReference Include="MQTTnet" Version="4.3.1.873" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Reference Include="MQTTnet.Client.Extensions"> | ||
| <HintPath>..\..\..\..\mqttclients\dotnet\MQTTnet.Client.Extensions\bin\Debug\net7.0\MQTTnet.Client.Extensions.dll</HintPath> | ||
| </Reference> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.