fix: pass JWT token in WebSocket connection headers
WebSocket connections to /ws/agent were rejected by Kong (401) because the Authorization header was not included. Now reads access_token from secure storage and passes it in the WebSocket upgrade request headers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9cdc4933dc
commit
803cea0fe4
|
|
@ -31,7 +31,10 @@ class WebSocketClient {
|
||||||
|
|
||||||
final uri = Uri.parse('$baseUrl$path');
|
final uri = Uri.parse('$baseUrl$path');
|
||||||
try {
|
try {
|
||||||
_channel = WebSocketChannel.connect(uri);
|
_channel = WebSocketChannel.connect(
|
||||||
|
uri,
|
||||||
|
headers: token != null ? {'Authorization': 'Bearer $token'} : null,
|
||||||
|
);
|
||||||
_isConnected = true;
|
_isConnected = true;
|
||||||
_reconnectAttempts = 0;
|
_reconnectAttempts = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,17 @@ class ChatRepositoryImpl implements ChatRepository {
|
||||||
final ChatRemoteDatasource _remoteDatasource;
|
final ChatRemoteDatasource _remoteDatasource;
|
||||||
final ChatLocalDatasource _localDatasource;
|
final ChatLocalDatasource _localDatasource;
|
||||||
final WebSocketClient _webSocketClient;
|
final WebSocketClient _webSocketClient;
|
||||||
|
final Future<String?> Function() _getAccessToken;
|
||||||
|
|
||||||
ChatRepositoryImpl({
|
ChatRepositoryImpl({
|
||||||
required ChatRemoteDatasource remoteDatasource,
|
required ChatRemoteDatasource remoteDatasource,
|
||||||
required ChatLocalDatasource localDatasource,
|
required ChatLocalDatasource localDatasource,
|
||||||
required WebSocketClient webSocketClient,
|
required WebSocketClient webSocketClient,
|
||||||
|
required Future<String?> Function() getAccessToken,
|
||||||
}) : _remoteDatasource = remoteDatasource,
|
}) : _remoteDatasource = remoteDatasource,
|
||||||
_localDatasource = localDatasource,
|
_localDatasource = localDatasource,
|
||||||
_webSocketClient = webSocketClient;
|
_webSocketClient = webSocketClient,
|
||||||
|
_getAccessToken = getAccessToken;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<StreamEvent> sendMessage({
|
Stream<StreamEvent> sendMessage({
|
||||||
|
|
@ -39,7 +42,8 @@ class ChatRepositoryImpl implements ChatRepository {
|
||||||
final taskId = response['taskId'] as String? ?? response['task_id'] as String?;
|
final taskId = response['taskId'] as String? ?? response['task_id'] as String?;
|
||||||
|
|
||||||
// Connect to the agent WebSocket and subscribe to the session
|
// Connect to the agent WebSocket and subscribe to the session
|
||||||
await _webSocketClient.connect('/ws/agent');
|
final token = await _getAccessToken();
|
||||||
|
await _webSocketClient.connect('/ws/agent', token: token);
|
||||||
_webSocketClient.send({
|
_webSocketClient.send({
|
||||||
'event': 'subscribe_session',
|
'event': 'subscribe_session',
|
||||||
'data': {'sessionId': returnedSessionId, 'taskId': taskId},
|
'data': {'sessionId': returnedSessionId, 'taskId': taskId},
|
||||||
|
|
@ -87,7 +91,8 @@ class ChatRepositoryImpl implements ChatRepository {
|
||||||
sessionId;
|
sessionId;
|
||||||
final taskId = response['taskId'] as String? ?? response['task_id'] as String?;
|
final taskId = response['taskId'] as String? ?? response['task_id'] as String?;
|
||||||
|
|
||||||
await _webSocketClient.connect('/ws/agent');
|
final voiceToken = await _getAccessToken();
|
||||||
|
await _webSocketClient.connect('/ws/agent', token: voiceToken);
|
||||||
_webSocketClient.send({
|
_webSocketClient.send({
|
||||||
'event': 'subscribe_session',
|
'event': 'subscribe_session',
|
||||||
'data': {'sessionId': returnedSessionId, 'taskId': taskId},
|
'data': {'sessionId': returnedSessionId, 'taskId': taskId},
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import '../../../../core/network/dio_client.dart';
|
import '../../../../core/network/dio_client.dart';
|
||||||
import '../../../../core/network/websocket_client.dart';
|
import '../../../../core/network/websocket_client.dart';
|
||||||
|
import '../../../auth/data/providers/auth_provider.dart';
|
||||||
import '../../data/datasources/chat_local_datasource.dart';
|
import '../../data/datasources/chat_local_datasource.dart';
|
||||||
import '../../data/datasources/chat_remote_datasource.dart';
|
import '../../data/datasources/chat_remote_datasource.dart';
|
||||||
import '../../data/models/chat_message_model.dart';
|
import '../../data/models/chat_message_model.dart';
|
||||||
|
|
@ -39,12 +40,14 @@ final chatRepositoryProvider = Provider<ChatRepository>((ref) {
|
||||||
final remote = ref.watch(chatRemoteDatasourceProvider);
|
final remote = ref.watch(chatRemoteDatasourceProvider);
|
||||||
final local = ref.watch(chatLocalDatasourceProvider);
|
final local = ref.watch(chatLocalDatasourceProvider);
|
||||||
final ws = ref.watch(webSocketClientProvider);
|
final ws = ref.watch(webSocketClientProvider);
|
||||||
|
final storage = ref.watch(secureStorageProvider);
|
||||||
|
|
||||||
// Use a no-op local datasource if SharedPreferences is not yet ready
|
// Use a no-op local datasource if SharedPreferences is not yet ready
|
||||||
return ChatRepositoryImpl(
|
return ChatRepositoryImpl(
|
||||||
remoteDatasource: remote,
|
remoteDatasource: remote,
|
||||||
localDatasource: local ?? _NoOpLocalDatasource(),
|
localDatasource: local ?? _NoOpLocalDatasource(),
|
||||||
webSocketClient: ws,
|
webSocketClient: ws,
|
||||||
|
getAccessToken: () => storage.read(key: 'access_token'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue