Build a Distributed IoT Cluster on .NET 6

Secure SignalR endpoints for both web UI clients and IoT devices

Your browser needs to be JavaScript capable to view this video

Try reloading this page, or reviewing your browser settings

This video segment shows how to apply security to SignalR endpoints. It shows how to use login-based authentication on the Blazor web view and how to use client certificate authentication on IoT devices.

Keywords

  • Blazor
  • Iot
  • SignalR
  • .NET 6
  • OpenID Connect
  • OAuth
  • JWT
  • authentication
  • authorization
  • client certificates

Conflict of Interest

The author declares no conflict of interest.

About this video

Author(s)
Fiodar Sazanavets
First online
15 May 2022
DOI
https://doi.org/10.1007/978-1-4842-8285-4_7
Online ISBN
978-1-4842-8285-4
Publisher
Apress
Copyright information
© Fiodar Sazanavets 2022

Video Transcript

In this segment of the video, I will show you how to secure a signal application. We will secure both the access from the web view and from IoT devices as well. To make a start, we will first need to add these two NuGet packages to our DeviceManagementApp.Server application. The first NuGet package, Microsoft.AspNet Core.Authentiation.Certificate allow us to use Certificate authentication, which will apply to our devices.

The other NuGet package, which is Microsoft.AspNet Core.Authentication.JwtBearer, is used for authentication web view. And this is how we secure the endpoints on the hub itself. Secure and SignalR hub endpoints is exactly the same as securing controller endpoints or gRPC service endpoints. You will need to use Authorize attribute. If you apply Authorize attribute on the class itself, then all of these endpoints will be accessible only to logged in users.

If you then need to grant access to one of the endpoints to anybody, you can add Allow Anonymous attribute on that particular endpoint. Or instead of having authorized attributes on the class itself, you can just apply them on the public methods of the class. Normally, if you have authorized attribute without any parameters, what it means is that only logged in users will be able to access that endpoint.

But because in our case, we are using two authentication schemas, we have to explicitly specify each one of them. We have to make sure that both users and devices can access our SignalR hub. For those endpoints that are accessed by devices, we have applied a special policy which is called Device. And that’s how you configure your authentication and authorization. And here we’ve added a couple of global using statements that are relevant to these packages that we’ve just added.

To make sure that we can use certificate authentication, we need to configure some Kestrel options because Kestrel is the server that runs our web application. If it wasn’t Kestrel, if we use some other web servers such as IIS, for example, we would then need to configure these options in the section specific to that particular server type.

In our case, we are enabling certificate authentication, but we are setting it to delay certificate, which means that it will only ask for a certificate if it needs to. It will not force any client to supply certificate authentication and this option will allow us to connect devices and authenticate them via certificate, but also connect the web view and authenticate users just by username and password.

The first step toward our authentication logic is to call AddAuthentication method on our services. We are certain JwtBearer as a default authentication schema. And the default challenge scheme is Open ID connect, in our case. And next we are adding some JwtBearer options. JWT stands for JSON Web Token. JWT is used to authenticate clients in authorization protocol known as OAuth, which works together with OpenID Connect, which is our authentication protocol and the mechanism of obtaining the token from username and password.

The first thing that we specify here is authority. Authority is our address of authentication token provider. In our case, we are hosting our single sign on application at this address, local host with the port number of 5,001. We are setting the validate audience to false. Audience is one of the claims that comes in the token. If this option is set to true then only a particular application will be able to use the token. In our case, we are not that strict. Any application will be able to access this token.

This option allows us to use single sign on applications that does not have HTTPS protocol. This option is only used in development environment. You would never use it in production. SSO provider will always have to be protected by HTTPS. Here we are setting some custom event that is specific to our SignalR hub. There are two ways that authentication token can be delivered in our request. If the inside of authorization handler, where the value is the word “Bearer,” which is the token type followed by the token value.

Or if we are using the web sockets, it will be set in access token query string parameter. But nevertheless, the access token itself will do the same in both of these cases. And here we are just making sure that whichever way of sending token is used, we can still use the token. Now here are some certificate options as well. We are allowing all certificate types. This is said to make sure that we can use self-signed certificates, which we are using in our development environment.

Client certificates are similar to SSL certificates or TLS certificates that you use to enable HTTPS communication, but the purpose is different. First of all, you don’t normally need the certification authority to use them. So this option of using all certificate types, including self-signed ones, is a valid option even in production. But these certificates are used to store the data that validate that the client is indeed what the client claims to be.

Once we receive a properly structured certificate, our certificate validated, the event gets triggered. In our case, we have it in thumbprint of the certificate, which is a unique signature of a certificate based on this data. And we are associating this as a value with the NameIdentifier claim. After this, we are adding this claim to the list of other claims. To make sure that we can use authorization policies, we need to call AddAuthorization method on Services.

In our case, we are adding the following policy. We are checking whether we have NameIdentifier claim and we are checking whether the value of this claim is this, which is a known thumbprint of the certificate that we are using. Of course, in a real environment, this value will be configurable. But for demonstration purposes I have hardcoded it. So this is where the device policy comes from.

If we open our hub again, this is how it’s used. So it’s only the client that passed the requirements of this policy that will be able to use these specific endpoints. The rest of the endpoints are available to any authenticated user because even when we don’t specify any authorized attribute on them explicitly, you still won’t be able to access them anonymously because authorized attribute is set on the class itself.

Let’s now have a look how we authenticate devices. We have added the following logic to our shared class. If we open SignalRClientWrapper class, we have this. So we have modified WithURL call on our HubConnectionBuilder object. We had in this file with a password that we set for it to the list of the certificates that will be sent with the request.

.pfx is a file that contains the keys. It is generated from a certificate file. The process of creating a self signed certificate is beyond the scope of this video. There are plenty of instructions on how to do it online by using either PowerShell if you are running Windows, or OpenSSL if you are running any operating system, including Windows. But just for now, all you need to know is that we created the certificate and we’ve added it to both of our client types.

Then there is also blaze application. And authentication in Blazor application is a bit different. First of all, we needed to add another package to it. The package is called Microsoft.AspNet Core.Components. WebAssembly.Authentication. This package is specific to blaze a WebAssembly. We have also modified our program class. We have added this call here. Add oidcAuthentication, which adds Open ID Connect authentication.

In our case, we are reading the value from configuration. Our configuration is appsettings.json file located in wwwroot folder. And these are the settings. We have authority, which will tell the application to contact the specific URL for SSO provider to obtain the token. We have set client ID, which represents the client ID in the configuration inside of our SSO provider. Response type is called.

So basically the way it works with Open ID Connect, when the user is not authenticated, the user will be redirected to the login page of their SSO provider. If the user then enters username and password correctly, the application will receive the code, which is time sensitive, and then that code can be exchanged for JWT token. And then that token can be used by the application to authenticate itself against some other applications.

So we are authenticating into our web view by using username and password. And then once we obtain the token we are using that token to authenticate ourselves into a Signal R Hub application. These settings here are just the path to the pages that our SSO provider will redirect to once it logs in and when it logs out. We have also added a couple of shared views.

There’s authentication component where you can pass the action. So essentially both of this path represents the path to this authentication component with a specific action. The middleware in the backend will handle this. It will know exactly what to do. Login display component is the component that displays the login link which will allow you to log into the application.

Redirect to login is a component that allows you to redirect to the login page if you are not authenticated. And there are also some changes to our App.razor file. We’re enabling redirect to login if the user is not authorized. All of these razor components and all of this markup will be available if you create application from Razer WebAssembly template and set user authentication to true while creating it.

In our case, we needed to add them all because we didn’t create our application this way to start with. Let’s now have a look at our devices view. We haven’t added that many changes to it. We have injected access token provider, which will allow us to extract access token once we authenticate. Now here is the logic that we’ve added to our SignalR Hub. If there’s access token, then we pass this access token.

Once again we are inserting some options into WithURL call. We are associating an action with access token provider option. And all we do is just pass a string access token that we have extracted. There is also an important thing to know. So if I’ll open my SSO provider webview, here is the client application. So remember, we use this as a client application ID. The ID will have to match with one of the clients presented in our SSO provider.

Also, another thing to note, we have switched off any secrets in here. Our client applications won’t have to use secrets and this is because Blazer WebAssembly stores all the settings in the browser, so those secrets would be visible to the users, which you don’t want to do. But if we have just a normal web application where all the settings are stored on the server, the best practice would be to enable this option and to set some secrets which only your client application on SSO provider will know.

We have set up some users, so any of these users presented in this view will be able to access our application. Let’s now launch our application and see what happens. I will start my serve application first. Once this application is up and running, we just copy the URL and we’ll launch our weather device. So we’ll type dotnet run followed by any random serial number followed by URL followed by devices path.

Let’s first see what happens if we launch this application but we haven’t authenticated as a user. We can wait for this application to build. We can give it some time. Then have a look at this view. And as we can see this part here is not throwing any errors while this part here is not displaying anything. Let’s now stop our client application, which we can do by either pressing Control+C or closing this window.

And now let’s log in. We already have a user in our system and this is the user we will use to log in. As you can see, it took us to a different URL, and then once we enter the correct username and password, it redirected us back to our original location. Let’s launch our application again. So it’s dotnet run any random serial number, and the hub URL followed by devices. And as we can see, our device appeared here this time. And because we authenticated, this logout button appears here.

Let’s now put breakpoint in the code that generates this view and extract our token. We can put breakpoint here because by this time, we already have the token. When we use Open ID Connect, our token is stored in the cookies, so we can just refresh this page. We will still be logged in.

And as you can see, our breakpoint has been hit. We can now copy access token value. The best way to examine access token is to go to JWT.io. I just copy the token content into this encoded section. Even though at first glance, it looks like just a random string of characters, it’s not really. It consists of three parts that are Base64 encoded strings. But those really are JSON objects and this is why it’s called JSON token.

The first section is known as header. It only contains a couple of basic fields. The second section is the payload. It has data such as expiration of the token, issuer, which is our authority, client ID, which is the same as we’ve said in our application, which is web app client, subject, authentication time, email of the user, name of the user, the roles that the user is assigned to, and some other claims. Basically, every one of these fields is known as claim.

The third and final part is the signature, which is used for token validation. This is how you can make sure that it’s not just any random JSON that gets passed as your token. And there is one final thing that we can try to see what happens if we remove the certificate of authentication from our devices.

So if we now remove this bit from here and then try to run our application again, it will rebuild it from the code that we just changed. And as we can see, it counted for a one unauthorized exception. And this is because it requires a certificate to authenticate.

This concludes the demonstration of how to use different types of authentication to make sure that signal can only be accessed by those clients that are authorized to use it. And this is the end of our video. Let’s now summarize what we’ve covered.