Skip to content

Commit fcc8ba6

Browse files
committed
Websocket authentication.
1 parent 8c1b0bc commit fcc8ba6

File tree

16 files changed

+111
-96
lines changed

16 files changed

+111
-96
lines changed

src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ public interface IConversationService
55
IConversationStateService States { get; }
66
string ConversationId { get; }
77
Task<Conversation> NewConversation(Conversation conversation);
8-
Task MarkConnectionReady(string conversationId);
98
void SetConversationId(string conversationId, List<string> states);
109
Task<Conversation> GetConversation(string id);
1110
Task<List<Conversation>> GetConversations();

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,4 @@ public void SetConversationId(string conversationId, List<string> states)
118118
_state.Load(_conversationId);
119119
states.ForEach(x => _state.SetState(x.Split('=')[0], x.Split('=')[1]));
120120
}
121-
122-
public async Task MarkConnectionReady(string conversationId)
123-
{
124-
var hooks = _services.GetServices<IConversationHook>();
125-
var conv = await GetConversation(conversationId);
126-
foreach (var hook in hooks)
127-
{
128-
// Need to check if user connected with agent is the first time.
129-
await hook.OnUserAgentConnectedInitially(conv);
130-
}
131-
}
132121
}

src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,6 @@ public async Task<ConversationViewModel> NewConversation([FromRoute] string agen
4141
return ConversationViewModel.FromSession(conv);
4242
}
4343

44-
[HttpPatch("/conversation/{conversationId}/ready")]
45-
public async Task ReadyToInteractive([FromRoute] string conversationId)
46-
{
47-
var service = _services.GetRequiredService<IConversationService>();
48-
await service.MarkConnectionReady(conversationId);
49-
}
50-
5144
[HttpGet("/conversations/{agentId}")]
5245
public async Task<IEnumerable<ConversationViewModel>> GetConversations()
5346
{

src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using BotSharp.Abstraction.Messaging;
21
using BotSharp.Abstraction.Messaging.Models.RichContent;
32
using Microsoft.AspNetCore.SignalR;
43

@@ -8,12 +7,15 @@ public class ChatHubConversationHook : ConversationHookBase
87
{
98
private readonly IServiceProvider _services;
109
private readonly IHubContext<SignalRHub> _chatHub;
10+
private readonly IUserIdentity _user;
1111

1212
public ChatHubConversationHook(IServiceProvider services,
13-
IHubContext<SignalRHub> chatHub)
13+
IHubContext<SignalRHub> chatHub,
14+
IUserIdentity user)
1415
{
1516
_services = services;
1617
_chatHub = chatHub;
18+
_user = user;
1719
}
1820

1921
public override async Task OnUserAgentConnectedInitially(Conversation conversation)
@@ -33,7 +35,18 @@ public override async Task OnUserAgentConnectedInitially(Conversation conversati
3335
foreach (var message in messages)
3436
{
3537
await Task.Delay(300);
36-
await OnResponseGenerated(new RoleDialogModel(AgentRole.Assistant, message.Text));
38+
39+
await _chatHub.Clients.User(_user.Id).SendAsync("OnMessageReceivedFromAssistant", new ChatResponseModel()
40+
{
41+
ConversationId = conversation.Id,
42+
Text = message.Text,
43+
Sender = new UserViewModel()
44+
{
45+
FirstName = "AI",
46+
LastName = "Assistant",
47+
Role = AgentRole.Assistant
48+
}
49+
});
3750
}
3851
}
3952

@@ -75,7 +88,7 @@ public override async Task OnResponseGenerated(RoleDialogModel message)
7588
{
7689
var conv = _services.GetRequiredService<IConversationService>();
7790

78-
await _chatHub.Clients.All.SendAsync("OnMessageReceivedFromAssistant", new ChatResponseModel()
91+
await _chatHub.Clients.User(_user.Id).SendAsync("OnMessageReceivedFromAssistant", new ChatResponseModel()
7992
{
8093
ConversationId = conv.ConversationId,
8194
MessageId = message.MessageId,
Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,47 @@
1+
using BotSharp.Abstraction.Conversations.Models;
2+
using Microsoft.AspNetCore.Http;
13
using Microsoft.AspNetCore.SignalR;
4+
using Microsoft.EntityFrameworkCore;
25

36
namespace BotSharp.Plugin.ChatHub;
47

8+
[Authorize]
59
public class SignalRHub : Hub
610
{
711
private readonly IServiceProvider _services;
812
private readonly ILogger _logger;
913
private readonly IUserIdentity _user;
14+
private readonly IHttpContextAccessor _context;
1015

1116
public SignalRHub(IServiceProvider services,
1217
ILogger<SignalRHub> logger,
13-
IUserIdentity user)
18+
IUserIdentity user,
19+
IHttpContextAccessor context)
1420
{
1521
_services = services;
1622
_logger = logger;
1723
_user = user;
24+
_context = context;
1825
}
1926

20-
public override Task OnConnectedAsync()
27+
public override async Task OnConnectedAsync()
2128
{
22-
Console.WriteLine($"SignalR Hub: {Context.UserIdentifier} connected in {Context.ConnectionId} [{DateTime.Now}]");
23-
return base.OnConnectedAsync();
29+
_logger.LogInformation($"SignalR Hub: {_user.FirstName} {_user.LastName} ({Context.UserIdentifier}) connected in {Context.ConnectionId} [{DateTime.Now}]");
30+
31+
var hooks = _services.GetServices<IConversationHook>();
32+
var convService = _services.GetRequiredService<IConversationService>();
33+
_context.HttpContext.Request.Query.TryGetValue("conversationId", out var conversationId);
34+
var conv = await convService.GetConversation(conversationId);
35+
36+
foreach (var hook in hooks)
37+
{
38+
// Check if user connected with agent is the first time.
39+
if (!conv.Dialogs.Any())
40+
{
41+
await hook.OnUserAgentConnectedInitially(conv);
42+
}
43+
}
44+
45+
await base.OnConnectedAsync();
2446
}
2547
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Microsoft.AspNetCore.Http;
2+
3+
namespace BotSharp.Plugin.ChatHub;
4+
5+
public class WebSocketsMiddleware
6+
{
7+
private readonly RequestDelegate _next;
8+
9+
public WebSocketsMiddleware(RequestDelegate next)
10+
{
11+
_next = next;
12+
}
13+
14+
public async Task Invoke(HttpContext httpContext)
15+
{
16+
var request = httpContext.Request;
17+
18+
// web sockets cannot pass headers so we must take the access token from query param and
19+
// add it to the header before authentication middleware runs
20+
if (request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) &&
21+
request.Query.TryGetValue("access_token", out var accessToken))
22+
{
23+
request.Headers.Add("Authorization", $"Bearer {accessToken}");
24+
}
25+
26+
await _next(httpContext);
27+
}
28+
}

src/WebStarter/Program.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@
6868
app.UseSwaggerUI();
6969
}
7070

71+
app.MapHub<SignalRHub>("/chatHub");
72+
app.UseMiddleware<WebSocketsMiddleware>();
73+
7174
app.UseAuthentication();
7275
app.UseAuthorization();
7376

@@ -80,6 +83,4 @@
8083
app.UseCors("MyCorsPolicy");
8184
#endif
8285

83-
app.MapHub<SignalRHub>("/chatHub");
84-
8586
app.Run();
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import axios from 'axios';
2+
import { getUserStore } from '$lib/helpers/store.js';
23

34
/**
45
* Set axios http headers globally
5-
* @param {string} token
66
*/
7-
export function setAuthorization(token) {
7+
export function setAuthorization() {
8+
let user = getUserStore();
89
let headers = axios.defaults.headers;
9-
headers.common['Authorization'] = `Bearer ${token}`;
10+
headers.common['Authorization'] = `Bearer ${user.token}`;
1011
}

src/web-live-chat/src/lib/services/api-endpoints.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ export const agentListUrl = `${host}/agents`;
1313
export const agentDetailUrl = `${host}/agent/{id}`;
1414

1515
// conversation
16-
export const conversationInitUrl = `${host}/conversation/{agentId}`
17-
export const conversationReadyUrl = `${host}/conversation/{conversationId}/ready`
18-
export const conversationMessageUrl = `${host}/conversation/{agentId}/{conversationId}`
19-
export const conversationsUrl = `${host}/conversations/{agentId}`
20-
export const dialogsUrl = `${host}/conversation/{conversationId}/dialogs`
16+
export const conversationInitUrl = `${host}/conversation/{agentId}`;
17+
export const conversationMessageUrl = `${host}/conversation/{agentId}/{conversationId}`;
18+
export const conversationsUrl = `${host}/conversations/{agentId}`;
19+
export const dialogsUrl = `${host}/conversation/{conversationId}/dialogs`;
2120

2221
// chathub
2322
export const chatHubUrl = `${host}/chatHub`;

src/web-live-chat/src/lib/services/auth-service.js

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { userStore, getUserStore } from '$lib/helpers/store.js';
22
import { tokenUrl, myInfoUrl, usrCreationUrl } from './api-endpoints.js';
3+
import axios from 'axios';
34

45
/**
56
* This callback type is called `requestCallback` and is displayed as a global symbol.
@@ -37,31 +38,20 @@ export async function getToken(email, password, onSucceed) {
3738
}
3839

3940
/**
41+
* Set token from exteranl
4042
* @param {string} token
41-
* @returns {Promise<import('$typedefs').UserModel>}
4243
*/
43-
export async function myInfo(token) {
44-
const headers = {
45-
Authorization: `Bearer ${token}`,
46-
};
47-
48-
const info = await fetch(myInfoUrl, {
49-
method: 'GET',
50-
headers: headers,
51-
}).then(response => {
52-
if (response.ok) {
53-
return response.json();
54-
} else {
55-
alert(response.statusText);
56-
}
57-
}).then(result => {
58-
let user = getUserStore();
59-
userStore.set({ ...user, init: false, id: result.id });
60-
return result;
61-
})
62-
.catch(error => alert(error.message));
44+
export function setToken(token) {
45+
let user = getUserStore();
46+
userStore.set({ ...user, init: false, loggedIn: true, token: token });
47+
}
6348

64-
return info;
49+
/**
50+
* @returns {Promise<import('$typedefs').UserModel>}
51+
*/
52+
export async function myInfo() {
53+
const response = await axios.get(myInfoUrl);
54+
return response.data;
6555
}
6656

6757
/**

0 commit comments

Comments
 (0)