In the last article we learned a lot about WebRTC – what it is, what it does, how it is used and how it should be implemented, but the examples mostly stayed in theory. In this article, we are moving from theory to practice. All the concepts explained in the last article will be shown implemented in some of the most modern technologies currently available for building a powerful and secure WebRTC solution – Microsoft SignalR (with .NET Core 3.1) as our socketing and backend choice, and Angular 11 as our frontend client choice.
Why Microsoft SignalR
Microsoft SignalR is an open-source library that simplifies adding real-time web functionality to a wide variety of apps. It enables server-side code to push content to clients instantly, thus supporting scenarios like real-time notifications and instant messaging, just like WebRTC requires. However, where SignalR exceeds similar technologies is its simplicity, scalability, and security. SignalR is very straight-forward to implement, which will be shown in the example afterward. Moreover, its scalability is mostly built on the fact that it has .NET/.NET Core in its base, which, combined with Microsoft Azure SignalR service, can support even the most demanding environments. Lastly, its security is also based on .NET practices, so everything supported in standard .NET/.NET Core app is also available in out-of-the-box SignalR.
Building the solution
To build the complete WebRTC solution using mentioned technologies, it is needed to implement two major parts – the signaling server app and the web client app. Even though building a STUN/TURN server was mentioned in the last article, for this example, we are going to use free public STUN servers from Google:
stun:stun.1.google.com:19302 stun:stun1.l.google.com:19302
The solution itself will be built as simple as possible, but with all functionalities implemented and working. The idea is to show how to use those technologies to build a secure and functioning WebRTC solution without going too much into advanced WebRTC features. All parts of the code given below, as well as other parts which will be referenced but not shown, can be found at the Github repository of this article.
WebRTC algorithm
A simple WebRTC algorithm used in this article’s code example will allow two web clients to join a session call and start a peer-to-peer media transfer. The algorithm is shown in the image below.
As seen, it consists of a few signaling server methods/endpoints (such as “CreateOrJoinRoom”), which will the clients have to call to communicate with the server, and several signaling messages (such as “created”) which will ensure that each client is always in the right signaling state. Also, the image shows several web client local flags and methods that are needed for the WebRTC process to work correctly (such as “isInitiator” and “setRemoteDescription()”). The steps are going in chronological order, from top to bottom, and after every step is complete, media transfer should begin.
Building the signaling server
Creation of a WebRTC signaling server with Microsoft SignalR consists of 3 main things to do:
1) .NET Core (3.1) Web API project
Create a new (empty) .NET Core Web API project with the following dependencies installed:
Microsoft.AspNetCore.SignalR Microsoft.AspNetCore.SignalR.Common Microsoft.AspNetCore.SignalR.Protocols.Json
2) SignalR Hub
Create a new class, called “SignalingHub” for example, which extends SignalR’s abstract class “Hub”. All the WebRTC business logic for the signaling server will be written in this class. After class is created, register it in Startup’s ConfigureServices method using the following line:
services.AddSignalR();
Also, register the Hub as .NET Core endpoint using the following code in Startup’s Configure method:
app.UseEndpoints(endpoints => { endpoints.MapHub<SignalingHub>("/hubs/signaling"); });
3) WebRTC server-side algorithm implementation
Lastly, it is needed to define the WebRTC algorithm’s methods in the Hub. We need to ensure that this implementation will allow the client to join the session, send messages to the other clients, and leave the session. For this, we are going to define 3 Hub endpoints/methods:
a) CreateOrJoinRoom – creates a new session room on the signaling server (if it’s not already created) and adds the client to the session room
b) LeaveRoom – removes the client from the session room and deletes the room if a connected client was the last one
c) SendMessage – transmits various messages (e.g., SDP offers, SDP answers, ICE candidate, etc.) between the clients
The code for all the described methods can be found here.
Building the client app
For building a working WebRTC Angular client app, it is required to implement three main things:
1) Angular (11) project
Create a new Angular (11) project and install the following dependencies:
npm install webrtc-adapter npm install @microsoft/signalr
2) SignalR service
Create a new service called “SignalrService“, for example, which will be in charge of starting and maintaining the connection to the signaling server and executing and receiving socket requests. To ensure it will be able to do all those tasks, the following methods need to be implemented:
a) connect – creates a new instance of HubConnection class and starts an active connection to the signaling server
b) define – defines incoming socket requests, i.e. actions that will web app perform after the socket request is received
c) invoke – executes the given socket request
d) disconnect – disconnects from the signaling server
The code for those methods, and the entire SignalR service class, can be found here.
3) WebRTC client-side algorithm implementation
The WebRTC algorithm on the web client app can be implemented either in a separate class or the session call page’s app component. For this example, we will choose the latter. For the client-side algorithm to work, three main things need to be configured:
a) Starting a connection to the signaling server – using the SignalR service, connect to the server and execute the “CreateOrJoinRoom” method defined earlier to join the session room
b) Defining the signaling communication – using the SignalR service, define all signaling messages that will be used in WebRTC communication and their respective callbacks (e.g. “created”, “jointed”, etc.)
c) Getting the user media – using the Javascript WebRTC API get local audio/video media stream, like so:
navigator.mediaDevices.getUserMedia({ audio: true, video: true }
After those three steps have been executed, the only thing left is to send the starting “got user media” message, and the WebRTC algorithm can start. The complete implementation of client-side code can be found here.
Adding security
As security should be an essential part of every web application, WebRTC apps are no exception. With SignalR, adding a security layer between our signaling server and client app is reasonably simple to implement. Since SignalR is built in .NET Core environment, it supports all .NET Core authorization and authentication procedures. For example, using JWT authorization is as simple as adding the “[Authorize]” attribute to SignalR Hub where authorization is important and adding “accessTokenFactory” to the HubConnectionBuilder in the web client app with JWT token before making a connection with the signaling server. With that added, all socket requests should be secured with a JWT token. An elementary JWT authorization example of SignalR can also be found at the Github repository of this article.
WebRTC and modern technologies
Building a WebRTC solution used to be a long and demanding process that required several different technologies and custom implementations, making it very difficult to use. However, in the last few years, WebRTC evolved a lot, thus making things much more straight-forward. With modern technologies building a working and secure WebRTC solution can be done relatively fast.
It doesn’t need to be too complicated.
This should ultimately motivate more people to start using WebRTC for their real-time communication needs.