diff --git a/Tsi1.Api/Tsi1.Api/SignalR/VideoHub.cs b/Tsi1.Api/Tsi1.Api/SignalR/VideoHub.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5e62112c24780b458c531ea9e266d70b4966b1e1
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/SignalR/VideoHub.cs
@@ -0,0 +1,55 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.SignalR;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Helpers;
+
+namespace Tsi1.Api.SignalR
+{
+    [Authorize]
+    public class VideoHub : Hub
+    {
+        public override async Task OnConnectedAsync()
+        {
+            await Groups.AddToGroupAsync(Context.ConnectionId, Context.ConnectionId);
+
+            await Clients.Caller.SendAsync(Context.ConnectionId);
+        }
+
+        [Authorize(Roles = UserTypes.Professor)]
+        public async Task CreateRoom(string roomName)
+        {
+            await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
+
+            await Clients.Caller.SendAsync("CreateRoom", roomName);
+        }
+
+        public async Task Join(string roomName, string offer)
+        {
+
+            Console.WriteLine(offer);
+            await Clients.Group(roomName).SendAsync("Join", Context.ConnectionId, offer);
+
+            await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
+        }
+
+        public async Task Leave(string roomName)
+        {
+            await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
+
+            await Clients.Group(roomName).SendAsync("Leave", Context.ConnectionId);
+        }
+
+        public async Task NotifyUser(string connectionId, string answer)
+        {
+            await Clients.Group(connectionId).SendAsync("NotifyUser", Context.ConnectionId, answer);
+        }
+
+        public async Task ICECandidate(string connectionId, string candidate)
+        {
+            await Clients.Group(connectionId).SendAsync("ICECandidate", Context.ConnectionId, candidate);
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.Api/Startup.cs b/Tsi1.Api/Tsi1.Api/Startup.cs
index 729f1a70cfe93cef91f9ab928d25676c97eaf073..9e0df6280b29f68a2954b9cb32413a6b44cedd46 100644
--- a/Tsi1.Api/Tsi1.Api/Startup.cs
+++ b/Tsi1.Api/Tsi1.Api/Startup.cs
@@ -18,6 +18,7 @@ using Microsoft.Extensions.Options;
 using Microsoft.IdentityModel.Tokens;
 using Microsoft.OpenApi.Models;
 using Tsi1.Api.Infrastructure;
+using Tsi1.Api.SignalR;
 using Tsi1.BusinessLayer.Helpers;
 using Tsi1.BusinessLayer.Interfaces;
 using Tsi1.BusinessLayer.Services;
@@ -40,6 +41,8 @@ namespace Tsi1.Api
         {
             services.AddControllers();
 
+            services.AddSignalR();
+
             var isElasticCloud = bool.Parse(Configuration.GetSection("IsElasticCloud").Value);
 
             var postgreSqlSection = isElasticCloud ? "PostgreSqlCloud" : "PostgreSql";
@@ -90,6 +93,23 @@ namespace Tsi1.Api
                     ValidateLifetime = true,
                     ClockSkew = TimeSpan.FromMinutes(1)
                 };
+
+                x.Events = new JwtBearerEvents
+                {
+                    OnMessageReceived = context =>
+                    {
+                        var accessToken = context.Request.Query["access_token"];
+
+                        var path = context.HttpContext.Request.Path;
+                        if (!string.IsNullOrEmpty(accessToken) &&
+                            path.StartsWithSegments("/hubs"))
+                        {
+                            context.Token = accessToken;
+                        }
+
+                        return Task.CompletedTask;
+                    }
+                };
             });
 
             services.AddSingleton<IJwtAuthManager, JwtAuthManager>();
@@ -155,6 +175,7 @@ namespace Tsi1.Api
             app.UseEndpoints(endpoints =>
             {
                 endpoints.MapControllers();
+                endpoints.MapHub<VideoHub>("hubs/video");
             });
         }
     }